एक निर्देश के भीतर टेम्पलेट को अनुकूलित करना


98

मेरे पास एक फॉर्म है जो बूटस्ट्रैप से मार्कअप का उपयोग कर रहा है, निम्न की तरह:

<form class="form-horizontal">
  <fieldset>
    <legend>Legend text</legend>
    <div class="control-group">
      <label class="control-label" for="nameInput">Name</label>
      <div class="controls">
        <input type="text" class="input-xlarge" id="nameInput">
        <p class="help-block">Supporting help text</p>
      </div>
    </div>
  </fieldset>
</form>

वहाँ बहुत सारे बॉयलरप्लेट कोड है, कि मैं एक नए निर्देश को कम करना चाहूंगा - फॉर्म-इनपुट, इस प्रकार है:

<form-input label="Name" form-id="nameInput"></form-input>

उत्पन्न करता है:

   <div class="control-group">
      <label class="control-label" for="nameInput">Name</label>
      <div class="controls">
        <input type="text" class="input-xlarge" id="nameInput">
      </div>
    </div>

मैं एक साधारण टेम्पलेट के माध्यम से यह काम कर रहा हूं।

angular.module('formComponents', [])
    .directive('formInput', function() {
        return {
            restrict: 'E',
            scope: {
                label: 'bind',
                formId: 'bind'
            },
            template:   '<div class="control-group">' +
                            '<label class="control-label" for="{{formId}}">{{label}}</label>' +
                            '<div class="controls">' +
                                '<input type="text" class="input-xlarge" id="{{formId}}" name="{{formId}}">' +
                            '</div>' +
                        '</div>'

        }
    })

हालाँकि यह तब है जब मैं अधिक उन्नत कार्यक्षमता में जोड़ने के लिए आया हूँ जो कि मैं फंस रहा हूँ।

मैं टेम्पलेट में डिफ़ॉल्ट मानों का समर्थन कैसे कर सकता हूं?

मैं अपने निर्देश पर एक वैकल्पिक विशेषता के रूप में "प्रकार" पैरामीटर को उजागर करना चाहता हूं, जैसे:

<form-input label="Password" form-id="password" type="password"/></form-input>
<form-input label="Email address" form-id="emailAddress" type="email" /></form-input>

हालांकि, अगर कुछ भी निर्दिष्ट नहीं है, तो मैं डिफ़ॉल्ट रूप से करना चाहूंगा "text"। मैं इसका समर्थन कैसे कर सकता हूं?

मैं विशेषताओं की उपस्थिति / अनुपस्थिति के आधार पर टेम्पलेट को कैसे अनुकूलित कर सकता हूं?

अगर यह मौजूद है, तो मैं "आवश्यक" विशेषता का समर्थन करने में सक्षम होना चाहूंगा। उदाहरण के लिए:

<form-input label="Email address" form-id="emailAddress" type="email" required/></form-input>

यदि requiredनिर्देश में मौजूद है, तो मैं इसे जनरेट में जोड़ना चाहूंगा<input /> आउटपुट में , और इसे अन्यथा अनदेखा । मुझे यकीन नहीं है कि इसे कैसे प्राप्त किया जा सकता है।

मुझे संदेह है कि ये आवश्यकताएं एक साधारण टेम्पलेट से आगे बढ़ सकती हैं, और पूर्व-संकलन चरणों का उपयोग करना शुरू करना होगा, लेकिन मैं एक नुकसान में हूं जहां शुरू करना है।


क्या मैं कमरे में हाथी को देखने वाला एक ही व्यक्ति हूँ :) -> क्या होगा यदि typeबाध्यकारी उदाहरण के माध्यम से गतिशील रूप से सेट किया गया है। type="{{ $ctrl.myForm.myField.type}}"? मैंने नीचे सभी तरीकों की जाँच की और कोई भी समाधान नहीं खोज पाया जो इस परिदृश्य में काम करेगा। टेम्पलेट फ़ंक्शन की तरह लगने वाले विशेषताओं के शाब्दिक मूल्यों को देखेंगे । tAttr['type'] == '{{ $ctrl.myForm.myField.type }}'के बजाय tAttr['type'] == 'password'। मैं हैरान हूँ।
दिमित्री के

जवाबों:


211
angular.module('formComponents', [])
  .directive('formInput', function() {
    return {
        restrict: 'E',
        compile: function(element, attrs) {
            var type = attrs.type || 'text';
            var required = attrs.hasOwnProperty('required') ? "required='required'" : "";
            var htmlText = '<div class="control-group">' +
                '<label class="control-label" for="' + attrs.formId + '">' + attrs.label + '</label>' +
                    '<div class="controls">' +
                    '<input type="' + type + '" class="input-xlarge" id="' + attrs.formId + '" name="' + attrs.formId + '" ' + required + '>' +
                    '</div>' +
                '</div>';
            element.replaceWith(htmlText);
        }
    };
})

6
यह एक छोटे से देर हो चुकी है, लेकिन में अगर htmlTextआप एक अतिरिक्त ng-clickकहीं, केवल संशोधन को बदलने के लिए किया जाएगा element.replaceWith(htmlText)के साथ element.replaceWith($compile(htmlText))?
जुलैन्सी

@ मिस्को, आपने गुंजाइश से छुटकारा पाने का उल्लेख किया है। क्यों? मेरे पास एक निर्देश है जो अलग-अलग दायरे के साथ उपयोग किए जाने पर संकलन नहीं करता है।
सियाम

1
यह काम नहीं करता है अगर htmlTextइसमें एनजी-ट्रांसकल्डी निर्देश है
Alp

3
दुर्भाग्य से मैंने पाया है कि फॉर्म सत्यापन इसके साथ काम नहीं करता है, $errorसम्मिलित इनपुट पर झंडे कभी सेट नहीं होते हैं। मुझे यह एक निर्देश की कड़ी संपत्ति के भीतर करना था: $compile(htmlText)(scope,function(_el){ element.replaceWith(_el); });फार्म के नियंत्रक के लिए इसके नवगठित अस्तित्व को पहचानने और इसे सत्यापन में शामिल करने के लिए। मुझे निर्देशन की संकलन संपत्ति में काम करने के लिए नहीं मिला।
मेकोनारियो

5
ठीक है, यह 2015 है बाहर वहाँ और मैं कर रहा हूँ यकीन है कि वहाँ कुछ लिपियों में मार्कअप पैदा करने में बहुत गलत है मैन्युअल रूप से
बोरिसओकुंस्की

38

मिसको द्वारा प्रस्तावित समाधान का उपयोग करने की कोशिश की, लेकिन मेरी स्थिति में, कुछ विशेषताओं, जिन्हें मेरे टेम्पलेट html में विलय करने की आवश्यकता थी, वे स्वयं निर्देश थे।

दुर्भाग्य से, परिणामी टेम्पलेट द्वारा संदर्भित सभी निर्देशों ने सही तरीके से काम नहीं किया। मेरे पास कोणीय कोड में गोता लगाने और मूल कारण का पता लगाने के लिए पर्याप्त समय नहीं था, लेकिन एक समाधान मिला, जो संभवतः सहायक हो सकता है।

समाधान कोड को स्थानांतरित करना था, जो टेम्पलेट टेम्पलेट के संकलन से, HTML बनाता है। ऊपर से कोड के आधार पर उदाहरण:

    angular.module('formComponents', [])
  .directive('formInput', function() {
    return {
        restrict: 'E',
        template: function(element, attrs) {
           var type = attrs.type || 'text';
            var required = attrs.hasOwnProperty('required') ? "required='required'" : "";
            var htmlText = '<div class="control-group">' +
                '<label class="control-label" for="' + attrs.formId + '">' + attrs.label + '</label>' +
                    '<div class="controls">' +
                    '<input type="' + type + '" class="input-xlarge" id="' + attrs.formId + '" name="' + attrs.formId + '" ' + required + '>' +
                    '</div>' +
                '</div>';
             return htmlText;
        }
        compile: function(element, attrs)
        {
           //do whatever else is necessary
        }
    }
})

इसने मेरी समस्या का समाधान टेम्पलेट में एम्बेडेड एन-क्लिक के साथ किया
joshcomley

धन्यवाद, यह मेरे लिए भी काम किया। कुछ डिफ़ॉल्ट विशेषताओं को लागू करने के लिए एक निर्देश लपेटना चाहता था।
Martinoss

2
धन्यवाद, मुझे यह भी पता नहीं था कि टेम्पलेट ने एक फ़ंक्शन स्वीकार किया है!
जॉन स्नो

2
यह वर्कअराउंड नहीं है। यह ओपी का सही जवाब है। तत्व की विशेषताओं के आधार पर टेम्पलेट को सशर्त रूप से तैयार करना एक निर्देश / घटक टेम्पलेट फ़ंक्शन का सटीक उद्देश्य है। आपको उसके लिए संकलन का उपयोग नहीं करना चाहिए। कोणीय टीम इस शैली को कोडिंग (संकलन समारोह का उपयोग नहीं करने) के लिए बहुत प्रोत्साहित कर रही है।
jose.angel.jimenez

यह सही उत्तर होना चाहिए, यहां तक ​​कि मुझे पता नहीं था कि टेम्पलेट एक फ़ंक्शन लेता है :)
NeverGiveUp161

5

उपरोक्त उत्तर दुर्भाग्य से काफी काम नहीं करते हैं। विशेष रूप से, संकलन चरण में गुंजाइश की पहुंच नहीं है, इसलिए आप डायनामिक विशेषताओं के आधार पर फ़ील्ड को कस्टमाइज़ नहीं कर सकते। लिंकिंग चरण का उपयोग करने से लगता है कि सबसे अधिक लचीलेपन की पेशकश (अतुल्यकालिक रूप से डोम बनाने के मामले में) नीचे दिए गए दृष्टिकोण से पता चलता है:

<!-- Usage: -->
<form>
  <form-field ng-model="formModel[field.attr]" field="field" ng-repeat="field in fields">
</form>
// directive
angular.module('app')
.directive('formField', function($compile, $parse) {
  return { 
    restrict: 'E', 
    compile: function(element, attrs) {
      var fieldGetter = $parse(attrs.field);

      return function (scope, element, attrs) {
        var template, field, id;
        field = fieldGetter(scope);
        template = '..your dom structure here...'
        element.replaceWith($compile(template)(scope));
      }
    }
  }
})

मैंने अधिक पूर्ण कोड और दृष्टिकोण के राइटअप के साथ एक जिस्ट बनाया है


अच्छा दृष्टिकोण। दुर्भाग्य से जब ngTransclude के साथ उपयोग करने पर मुझे निम्नलिखित त्रुटि मिलती है:Error: [ngTransclude:orphan] Illegal use of ngTransclude directive in the template! No parent directive that requires a transclusion found.
Alp

और 'फ़ील्ड: "="' के साथ एक अलग दायरे का उपयोग क्यों नहीं किया जाता है?
IttayD

बहुत अच्छा धन्यवाद! दुर्भाग्य से आपका लिखित दृष्टिकोण ऑफ़लाइन है :(
मिचेल

जिस्ट और राइटअप दोनों ही डेड लिंक हैं।
बिंकी जू

4

यहाँ मैंने जो प्रयोग किया वह समाप्त हो गया।

मैं AngularJS के लिए बहुत नया हूं, इसलिए बेहतर / वैकल्पिक समाधान देखना पसंद करूंगा।

angular.module('formComponents', [])
    .directive('formInput', function() {
        return {
            restrict: 'E',
            scope: {},
            link: function(scope, element, attrs)
            {
                var type = attrs.type || 'text';
                var required = attrs.hasOwnProperty('required') ? "required='required'" : "";
                var htmlText = '<div class="control-group">' +
                    '<label class="control-label" for="' + attrs.formId + '">' + attrs.label + '</label>' +
                        '<div class="controls">' +
                        '<input type="' + type + '" class="input-xlarge" id="' + attrs.formId + '" name="' + attrs.formId + '" ' + required + '>' +
                        '</div>' +
                    '</div>';
                element.html(htmlText);
            }
        }
    })

उदाहरण का उपयोग:

<form-input label="Application Name" form-id="appName" required/></form-input>
<form-input type="email" label="Email address" form-id="emailAddress" required/></form-input>
<form-input type="password" label="Password" form-id="password" /></form-input>

10
इसका एक बेहतर समाधान यह है: (1) फंक्शन को जोड़ने के बजाय एक कंपाइल फंक्शन का उपयोग करें और वहां रिप्लेसमेंट करें। टेम्पलेट आपके मामले में काम नहीं करेगा क्योंकि आप इसे अनुकूलित करना चाहते हैं। (2) गुंजाइश से छुटकारा:
मिसको हेवरी

@MiskoHevery प्रतिक्रिया के लिए धन्यवाद - क्या आप बताएंगे कि एक संकलन फ़ंक्शन यहां लिंक फ़ंक्शन के लिए पसंदीदा क्यों है?
मार्टी पिट

4
मुझे लगता है कि यह उत्तर है, docs.angularjs.org/guide/directive से : "कोई भी ऑपरेशन जो निर्देशों के उदाहरण के बीच साझा किया जा सकता है [जैसे, टेम्पलेट डोम को बदलना] प्रदर्शन कारणों से संकलन समारोह में स्थानांतरित किया जाना चाहिए।"
मार्क राजकोक

@ मैर्टी क्या आप अभी भी अपने किसी कस्टम इनपुट को किसी मॉडल से बांध सकते हैं? (यानी। <form-input ng-model="appName" label="Application Name" form-id="appName" required/></form-input>)
जोनाथन विल्सन

1
@MartyPitt O'Reilly की "AngularJS" पुस्तक से: "तो हमें compileचरण मिला है , जो टेम्पलेट को बदलने से संबंधित है, और linkचरण, जो दृश्य में डेटा को संशोधित करने से संबंधित है। इन पंक्तियों के साथ, प्राथमिक अंतर। compileऔर linkनिर्देशों में compileकार्य यह है कि फ़ंक्शंस टेम्पलेट को बदलने के साथ कार्य करते हैं, और linkफ़ंक्शन मॉडल और दृश्य के बीच एक गतिशील संबंध बनाने के साथ काम करते हैं। यह इस दूसरे चरण में है कि स्कोप संकलित linkकार्यों से जुड़े हुए हैं , और निर्देश डेटा बाइंडिंग के माध्यम से लाइव हो जाता है "
जूलियन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.