AngularJS: Cannot interpolate attribute from first directive to a second. (w/ plunker example)

谁说胖子不能爱 提交于 2019-12-22 12:28:40

问题


Reference

Reference plunker: http://plnkr.co/edit/otv5mVVQ36iPi3Mp0FYw?p=preview

Explanation of the issue

Suppose that we have two directives, first-directive and second-directive. Now suppose we only have access to first-directive which we hope to wrap second-directive with and pass to it our own manipulated attributes.

app.directive('firstDirective', function() {
  return {
    scope: true,
    priority: 1000,
    transclude: true,

    template: function(element,attributes){
      console.log('template')
      return '<second-directive two="{{one}}"></second-directive>'
    },

    compile: function(element,attributes) {
      console.log('compile')
      return {
        pre: function(scope){
          scope.one = 'foo'
            console.log('cpre')
        },
        post: function(scope){
          scope.one = 'foo'
            console.log('cpost')
        },
      }
    },

    controller: ['$scope','$attrs',function($scope,$attrs){
      console.log('controller');
      $scope.one = 'foo';
    }],
  }
})

app.directive('secondDirective',function(){
  return {
    template: function (element,attributes){
      console.log(attributes.two) //{{one}} not 'foo' or 'test'
      return 'Hello {{two}}'
    }
  }
});    

first-directive is being called as follows:

<first-directive one='test'></first-directive>

console.log output as follows:

template
compile
{{one}}
controller
cpre
cpost

So from this I've learned that template is called before compile. This is a peculiar from my novice eyes because there isn't anyway to manipulate the value passed back by the template function through compile, controller, pre, or post link!

The question is this:

How can I call the second-directive with the dynamic attribute value that I want? Keep in mind that second-directive is completely independent and we can't add code there.

PS - One possible idea I have is to call the second-directive as follows:

template: function(element,attributes){
  console.log('template')
  var explicit = ???? /* how to access scope? */
  return '<second-directive two="'+ explicit +'"></second-directive>'
},

or alternatively

template: function(element,attributes){
  console.log('template')
  return $interpolate('<second-directive two="{{one}}"></second-directive>')(scopeObj) /* how does one access scopeObj with current scope values here? */
},

Yet, again, I'm not sure how to get the value being passed to first-directive before any of the other functions are called. Controller has access to $scope and it is called AFTER template.

Your suggestions greatly appreciated.


回答1:


Well if you just want to pass the data from first directive to second directive template, then you can add the dynamics attribute in first directive controller using
this.fromFirstDir = "you can pass from here"

first directive controller :

controller: ['$scope','$attrs',function($scope,$attrs){
      console.log('controller');
      $scope.one = 'foo';

      this.fromFirstDir = "you can pass from here"
    }],
  }

Then using the require attribute in the secondDirective for first directive controller,you can access this dynamic attribute from link function of the secondDirective directive using controller passed to link function. Finally assign those attributes to local scope passed to link function.

app.directive('secondDirective',function(){
  return { 
    scope: {twoData : '@twoData'},
    require : '^firstDirective',
    template: function (element,attributes){
      console.log(attributes.two) //{{one}} not 'foo' or 'test'
      return 'Hello <b>{{fromFirstDir}}</b>'
    },
    link : function(scope,element,attr,firstDirCtrl){
      console.log("===",firstDirCtrl.fromFirstDir) 
      scope.fromFirstDir = firstDirCtrl.fromFirstDir;
    }
  }
});

In this way, those dynamic atributes are available to your second directive.

Here is the final fiddle.

Hope this will help you.




回答2:


Did you write the second directive?

<second-directive two="'+ explicit +'"></second-directive>

For the above code to work you need to have an isolate scope object setup in the second directive, check out the plunkr below.

http://plnkr.co/edit/YP2h3MOhsrjN5sLUNQs6?p=preview




回答3:


I am using your question to learn, but I was able find this, which might work for you:

app.directive("tryThis", function($compile){
    return{
        scope: {
          one: '@',
        },
        link: function(scope, element){
            var template = '<second-directive two="'+scope.one+'"></second-directive>';
            var linkFn = $compile(template);
            var content = linkFn(scope);
            element.append(content);
        }
    }
});

Plunkr is here, note that test is logged in the console now instead of {{one}}. If secondDirective is given an isolated scope, test will then show on screen.

This link also helped me to conceptually understand the problem you are facing, giving some context to the issue of 'no scope during compile' step - I'm not sure there is a way around this.




回答4:


You don't (can't) have access to scope inside the template (because there is no scope at that moment). The template is used to create one or more elements and then they are linked to a scope (after having their controllers instantiated - if any).

There are a lot of ways to pass values between directives and each one is best suited for particular purposes. The simplest (but not necessarily best, depending on your usecase details) is assigning a value on a scope of the wrapper directive and let the inner directive read it off the scope:

<!-- HTML -->
<one for-two="{{test}}"></one>

// JS
angular.
  module('myApp', []).
  directive('one', oneDirective).
  directive('two', twoDirective);

function oneDirective() {
  return {
    restrict: 'E',
    scope: true,
    link: function onePostLink(scope, elem, attrs) {
      scope.two = attrs.forTwo;
    },
    template: '<two></two>'
  };
}

function twoDirective() {
  return {
    restrict: 'E',
    template: 'Hello, {{two}} !'
  };
}



回答5:


You have transclude: true but are not using it in the template. Can't you just use this markup and have the template for first directive use <ng-transclude>? You have scope: true so you could just manipulate the property from the parent/controller and the changes would propogate to both directives.

markup

<first-directive one="test">
    <second-directive two="test"></second-directive>        
</first-directive>

template for first-directive

template: `<div>
               my first directive content
               <ng-transclude></ng-transclude>
           </div>`;

https://docs.angularjs.org/api/ng/directive/ngTransclude



来源:https://stackoverflow.com/questions/30854373/angularjs-cannot-interpolate-attribute-from-first-directive-to-a-second-w-pl

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!