Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
198 views
in Technique[技术] by (71.8m points)

javascript - Pass data to transcluded element

I want to create a directive that organizes a displays data grouped by date. I also want to be able to specify a directive that will display the individual rows. In a perfect world it would look something like this (but nice and pretty)

Friday, Oct 28
    [some directive html]
    [some directive html]
    [some directive html]
Saturday, Oct 29
    [some directive html]
Sunday, Oct 30
    [some directive html]
    [some directive html]
...

This obviously doesn't work, so if you have a better approach please tell me, but I was hoping to be able to do something along these lines:

app.directive('dateOrganized', [function(){
    return {
        template:
            '<div>' +
                '<div ng-repeat="organizedDate in organizedDate">' +
                    '<div>{{organizedDate.date | date}}</div>' +
                    '<div ng-repeat="item in organizedDate.items">' +
                        '{{rowDirectiveHtml}}' +
                    '</div>' +
                '</div>' +
            '</div>',
        scope: {
            organizedDates: '=',
            rowDirectiveHtml: '='
        }
        ...
    };
}])

app.directive('itemRow', [function(){
    return {
        template: '<div>{{item.data}}</div>',
        scope: {
            item: '='
        }
    };
}]);

then use it like this:

<div data-organized organized-dates="stuff" row-directive-html="<div item-row item="item" />" />

I know this is super ugly (and doesn't work, but I'm sure I could get it working with a few tweaks) so what I am really asking, is there a better way to do this?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

This question is more complicated than might appear, so let's break it down.

What you are building is a directive that accepts a partial template - <div item-row item="item" /> - and that template uses (or linked against a scope with) an inner variable - item - that is not defined in the outer scope by the user; its meaning is defined by your directive and your user "discovers" it by reading the documentation of your directive. I typically name such "magic" variables with a prefixed $, e.g. $item.

Step 1

Instead of passing a template as an HTML-as-string via attribute binding, pass it as contents and transclude that content. Transcluding allows you to bind the transcluded content against an arbitrary scope:

<foo>
  <div>my item is: {{$item}}</div>
</foo>
.directive("foo", function(){
  return {
    scope: {},
    transclude: true,
    template: "<h1>I am foo</h1><placeholder></placeholder>",
    link: function(scope, element, attrs, ctrls, transclude){

      scope.$item = "magic variable";

      transclude(scope, function(clonedContent){
        element.find("placeholder").replaceWith(clonedContent);
      });
    }
  };
});

The above will place the template <div>my item is: {{$item}}</div> (could be any template you specify) where the directive foo decides, and will link against a scope that has $item defined.

Step 2

But the added complexity of your directive is that it uses ng-repeat, which by itself accepts a template, and the template your directive receives needs to be used as a template of ng-repeat.

With just the approach above, this would not work, since by the time link runs, ng-repeat will have already transcluded its own content before you had a chance to apply yours.

One way to address that is to manually $compile the template of foo instead of using the template property. Prior to compiling, we will have a chance to place the intended template where needed:

.directive("foo", function($compile){
  return {
    scope: {},
    transclude: true,
    link: function(scope, element, attrs, ctrls, transclude){

      scope.items = [1, 2, 3, 4];

      var template = '<h1>I am foo</h1>
                      <div ng-repeat="$item in items">
                        <placeholder></placeholder>
                      </div>';
      var templateEl = angular.element(template);

      transclude(scope, function(clonedContent){
        templateEl.find("placeholder").replaceWith(clonedContent);

        $compile(templateEl)(scope, function(clonedTemplate){
          element.append(clonedTemplate);
        });
      });
    }
  };
});

Demo


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...