मैं गतिशील रूप से AngularJS में एक निर्देश कैसे जोड़ सकता हूं?


212

मेरे पास बहुत उबला हुआ संस्करण है जो मैं कर रहा हूं जो समस्या को पार करता है।

मेरे पास एक सरल है directive। जब भी आप किसी तत्व पर क्लिक करते हैं, तो यह एक और जोड़ता है। हालांकि, इसे सही ढंग से प्रस्तुत करने के लिए पहले संकलित करने की आवश्यकता है।

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

फ़िडल्स यहां हैं: http://jsfiddle.net/paulocoelho/fBjbP/1/

और जेएस यहाँ है:

var module = angular.module('testApp', [])
    .directive('test', function () {
    return {
        restrict: 'E',
        template: '<p>{{text}}</p>',
        scope: {
            text: '@text'
        },
        link:function(scope,element){
            $( element ).click(function(){
                // TODO: This does not do what it's supposed to :(
                $(this).parent().append("<test text='n'></test>");
            });
        }
    };
});

जोश डेविड मिलर द्वारा समाधान: http://jsfiddle.net/paulocoelho/fBjbP/2/

जवाबों:


259

आपके पास वहाँ बहुत सारे बेकार jQuery हैं, लेकिन $ संकलन सेवा वास्तव में इस मामले में सुपर सरल है:

.directive( 'test', function ( $compile ) {
  return {
    restrict: 'E',
    scope: { text: '@' },
    template: '<p ng-click="add()">{{text}}</p>',
    controller: function ( $scope, $element ) {
      $scope.add = function () {
        var el = $compile( "<test text='n'></test>" )( $scope );
        $element.parent().append( el );
      };
    }
  };
});

आप ध्यान देंगे कि मैंने कुछ सर्वोत्तम प्रथाओं का पालन करने के लिए आपके निर्देश को भी रद्द कर दिया। मुझे पता है अगर आप उन लोगों के बारे में कोई प्रश्न हैं।


34
बहुत बढ़िया। यह काम करता हैं। देखें, ये सरल और बुनियादी उदाहरण वही हैं जिन्हें कोणीय के डॉक्स में दिखाया जाना चाहिए। वे जटिल उदाहरणों के साथ शुरू करते हैं।
पीसीलोहो

1
धन्यवाद, जोश, यह वास्तव में उपयोगी था। मैंने Plnkr में एक उपकरण बनाया है जिसका उपयोग हम एक नए कोडरडोज़ो में कर रहे हैं ताकि बच्चों को सीखने में मदद मिल सके कि कैसे कोड बनाया जाए, और मैंने इसे केवल इतना बढ़ाया कि मैं अब डेटापिक, अलर्ट, टैब आदि जैसे कोणीय बूटस्ट्रैप निर्देशों का उपयोग कर सकूं। और अभी यह केवल क्रोम में काम कर रहा है, हालांकि: embed.plnkr.co/WI16H7Rsa5adejXSmyNj/preview
जोशफ जो

3
जोश - बिना उपयोग के इसे पूरा करने का एक आसान तरीका क्या है $compile? वैसे आपके उत्तर के लिए धन्यवाद!
युगलशिव

3
@doubleswirve इस मामले में, यह सिर्फ ngRepeat का उपयोग करने के लिए बहुत आसान होगा। :-) लेकिन मेरा मानना ​​है कि आप नए निर्देशों को पृष्ठ पर गतिशील रूप से जोड़ रहे हैं, जिस स्थिति में उत्तर नहीं है - कोई सरल तरीका नहीं है क्योंकि $compileसेवा वह है जो तारों को निर्देश देती है और उन्हें घटना चक्र में हुक करती है। $compileइस तरह की स्थिति में आईएनजी के आसपास कोई रास्ता नहीं है , लेकिन ज्यादातर मामलों में ngRepeat की तरह एक और निर्देश एक ही काम पूरा कर सकता है (इसलिए ngRepeat हमारे लिए संकलन कर रहा है)। क्या आपके पास विशिष्ट उपयोग का मामला है?
जोश डेविड मिलर

2
क्या संकलन प्रीलिंक अवस्था में नहीं होना चाहिए? मुझे लगता है कि कंट्रोलर में केवल नॉन-डोम, यूनिट-टेस्टेबल कोड होना चाहिए, लेकिन मैं लिंक / कंट्रोलर कॉन्सेप्ट के लिए नया हूं, इसलिए मैं खुद को अनिश्चित हूं। इसके अलावा, एक मूल विकल्प एनजी शामिल है + आंशिक + एनजी-नियंत्रक क्योंकि यह विरासत में दिए गए दायरे के साथ एक निर्देश के रूप में कार्य करेगा ।
मार्कस रैडेल

77

एक नए तत्व-निर्देशन को जोड़ने के आदर्श राइसबॉल एलईई के उदाहरण के अलावा

newElement = $compile("<div my-directive='n'></div>")($scope)
$element.parent().append(newElement)

विद्यमान तत्व में एक नया गुण-निर्देश जोड़ना इस तरह से उपयोग किया जा सकता है:

मान लीजिए कि आपकी ऑन-द-मक्खी जोड़ना चाहते हैं कहते हैं कि my-directiveकरने के लिए spanतत्व।

template: '<div>Hello <span>World</span></div>'

link: ($scope, $element, $attrs) ->

  span = $element.find('span').clone()
  span.attr('my-directive', 'my-directive')
  span = $compile(span)($scope)
  $element.find('span').replaceWith span

उम्मीद है की वो मदद करदे।


3
अधिकतम कॉल स्टैक आकार से अधिक त्रुटि को रोकने के लिए मूल निर्देश को निकालना न भूलें।
SRachamim

नमस्ते, क्या आप कृपया मेरे नए प्रस्तावित एपीआई पर प्रोग्राम को निर्देशात्मक रूप से जोड़ने के लिए एक सरल प्रक्रिया में विचार प्रदान करेंगे? github.com/angular/angular.js/issues/6950 धन्यवाद!
trusktr

मेरी इच्छा है कि 2015 में हमारे पास कॉल स्टैक आकार में सीमा नहीं होगी। :(
साइको Brm

3
Maximum call stack size exceededत्रुटि हमेशा अनंत प्रत्यावर्तन की वजह से होता है। मैंने कभी ऐसा उदाहरण नहीं देखा है जहाँ स्टैक का आकार बढ़ने से यह हल हो जाए।
गनक्रोंस

इसी तरह की समस्या मैं झेल रहा हूँ, क्या आप मुझे यहां मदद कर सकते हैं stackoverflow.com/questions/38821980/…
pandu das

45

गतिशील रूप से कोणीयज पर निर्देश जोड़ने की दो शैलियाँ हैं:

एक कोणीय निर्देश दूसरे निर्देश में जोड़ें

  • एक नया तत्व (निर्देश) डालना
  • तत्व को एक नया गुण (निर्देश) सम्मिलित करना

एक नया तत्व (निर्देश) डालना

यह आसान है। और आप "लिंक" या "संकलन" में उपयोग कर सकते हैं।

var newElement = $compile( "<div my-diretive='n'></div>" )( $scope );
$element.parent().append( newElement );

तत्व को एक नई विशेषता सम्मिलित करना

यह कठिन है, और मुझे दो दिनों के भीतर सिरदर्द बना देता है।

"$ संकलन" का उपयोग करना महत्वपूर्ण पुनरावर्ती त्रुटि को बढ़ाएगा !! हो सकता है कि यह तत्व को फिर से संकलित करते समय वर्तमान निर्देश को अनदेखा कर दे।

$element.$set("myDirective", "expression");
var newElement = $compile( $element )( $scope ); // critical recursive error.
var newElement = angular.copy(element);          // the same error too.
$element.replaceWith( newElement );

तो, मुझे निर्देशन "लिंक" फ़ंक्शन को कॉल करने का एक तरीका खोजना होगा। उन उपयोगी तरीकों को प्राप्त करना बहुत कठिन है जो गहराई से बंद के अंदर छिपे हुए हैं।

compile: (tElement, tAttrs, transclude) ->
   links = []
   myDirectiveLink = $injector.get('myDirective'+'Directive')[0] #this is the way
   links.push myDirectiveLink
   myAnotherDirectiveLink = ($scope, $element, attrs) ->
       #....
   links.push myAnotherDirectiveLink
   return (scope, elm, attrs, ctrl) ->
       for link in links
           link(scope, elm, attrs, ctrl)       

अब, यह अच्छी तरह से काम है।


1
यदि संभव हो तो वेनिला जेएस में तत्व को एक नया गुण सम्मिलित करने का एक डेमो देखना पसंद करेंगे - मुझे कुछ याद आ रहा है ...
पैट्रिक

तत्व में एक नया गुण सम्मिलित करने का वास्तविक उदाहरण यहाँ है (मेरे
गीथब

1
ईमानदारी से मदद नहीं करता है। यह है कि मैं अपनी समस्या को हल करने के लिए समाप्त हो गया, हालांकि: stackoverflow.com/a/20137542/1455709
पैट्रिक

हां, यह मामला किसी अन्य निर्देश में विशेषता निर्देश सम्मिलित करने का है, न कि टेम्पलेट में डालने वाला तत्व।
राइसबॉल LEE

टेम्पलेट के बाहर करने के पीछे क्या तर्क है?
पैट्रिक

9
function addAttr(scope, el, attrName, attrValue) {
  el.replaceWith($compile(el.clone().attr(attrName, attrValue))(scope));
}

5

जोश डेविड मिलर द्वारा स्वीकृत उत्तर महान काम करता है यदि आप गतिशील रूप से एक निर्देश जोड़ने की कोशिश कर रहे हैं जो एक इनलाइन का उपयोग करता है template। हालाँकि यदि आपका निर्देश templateUrlउसके उत्तर का लाभ लेता है तो काम नहीं करेगा। यहाँ मेरे लिए क्या काम किया गया है:

.directive('helperModal', [, "$compile", "$timeout", function ($compile, $timeout) {
    return {
        restrict: 'E',
        replace: true,
        scope: {}, 
        templateUrl: "app/views/modal.html",
        link: function (scope, element, attrs) {
            scope.modalTitle = attrs.modaltitle;
            scope.modalContentDirective = attrs.modalcontentdirective;
        },
        controller: function ($scope, $element, $attrs) {
            if ($attrs.modalcontentdirective != undefined && $attrs.modalcontentdirective != '') {
                var el = $compile($attrs.modalcontentdirective)($scope);
                $timeout(function () {
                    $scope.$digest();
                    $element.find('.modal-body').append(el);
                }, 0);
            }
        }
    }
}]);

5

जोश डेविड मिलर सही है।

PCoelho, यदि आप सोच रहे हैं कि $compileपर्दे के पीछे क्या है और HTML आउटपुट निर्देश से कैसे उत्पन्न होता है, तो कृपया नीचे क्लिक करें

$compileसेवा एचटीएमएल (का टुकड़ा संकलित "< test text='n' >< / test >") कि निर्देश (एक तत्व के रूप "परीक्षण") भी शामिल है और एक समारोह पैदा करता है। इस फ़ंक्शन को "HTML आउटपुट को एक निर्देश से प्राप्त करने के लिए" गुंजाइश के साथ निष्पादित किया जा सकता है।

var compileFunction = $compile("< test text='n' > < / test >");
var HtmlOutputFromDirective = compileFunction($scope);

पूर्ण कोड नमूनों के साथ अधिक विवरण यहां: http://www.learn-angularjs-apps-projects.com/AngularJs/dynamically-add-directives-in-angularjs


4

पिछले उत्तरों में से कई से प्रेरित होकर मैं निम्नलिखित "स्ट्रोमैन" निर्देश के साथ आया हूं जो खुद को किसी अन्य निर्देश के साथ बदल देगा।

app.directive('stroman', function($compile) {
  return {
    link: function(scope, el, attrName) {
      var newElem = angular.element('<div></div>');
      // Copying all of the attributes
      for (let prop in attrName.$attr) {
        newElem.attr(prop, attrName[prop]);
      }
      el.replaceWith($compile(newElem)(scope)); // Replacing
    }
  };
});

महत्वपूर्ण: उन निर्देशों को पंजीकृत करें जिन्हें आप उपयोग करना चाहते हैं restrict: 'C'। ऐशे ही:

app.directive('my-directive', function() {
  return {
    restrict: 'C',
    template: 'Hi there',
  };
});

आप इस तरह का उपयोग कर सकते हैं:

<stroman class="my-directive other-class" randomProperty="8"></stroman>

इसे पाने के लिए:

<div class="my-directive other-class" randomProperty="8">Hi there</div>

प्रो टिप। यदि आप कक्षाओं के आधार पर निर्देशों का उपयोग नहीं करना चाहते हैं तो आप अपनी '<div></div>'पसंद के अनुसार कुछ बदल सकते हैं । उदाहरण के लिए एक निश्चित विशेषता है जिसमें वांछित निर्देश का नाम है class


इसी तरह की समस्या मैं झेल रहा हूं, क्या आप मुझे यहां मदद कर सकते हैं stackoverflow.com/questions/38821980/…
pandu das

हे भगवान। इस $ संकलन को खोजने में 2 दिन लगे ... धन्यवाद दोस्तों .. यह सबसे अच्छा काम करता है ... AJS you rock ....
श्रीनिवासन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.