Angularjs: transclude directive template

前端 未结 3 810
忘掉有多难
忘掉有多难 2020-12-09 06:07

How to use transclusion in the below case. The intention is to use markup in the html (partials) file, than defining it in template (within the directive).

I found a

相关标签:
3条回答
  • 2020-12-09 06:36

    You want to compile the transcluded DOM against the parent scope; you can do this automatically with the injectable $transclude function in a directive's controller definition:

    module.directive("tree", function($compile) {
      return {
        restrict: "E",
        transclude: true,
        scope: { family: '=' },
        template: '<ul>' + 
                    '<li ng-repeat="child in family.children">' +
                      '<tree family="child">' +
                        '<p>{{ child.name }}</p>' +
                      '</tree>' +
                    '</li>' +
                  '</ul>',
        controller: function($element, $transclude) {
          $transclude(function(e) {
            $element.append(e);
          });
        },
        compile: function(tElement, tAttr, transclude) {
          var contents = tElement.contents().remove();
          var compiledContents;
          return function(scope, iElement, iAttr) {
            if(!compiledContents) {
              compiledContents = $compile(contents);
            }
            compiledContents(scope, function(clone) {
              iElement.append(clone);
            });
          };
        }
      };
    });
    

    This allows you to use the parent scope property treeFamily in your root template (also notice the use of child in the directive's template, above):

    <div ng-app="myapp">
      <div ng-controller="TreeCtrl">
        <tree family="treeFamily">
          <p>{{ treeFamily.name }}</p>
        </tree>
      </div>
    </div>
    

    You can see an example here: http://jsfiddle.net/BinaryMuse/UzHeW/

    0 讨论(0)
  • 2020-12-09 06:43

    Very very late to the party. I needed this for a project so after dwelling into it and finding other great approaches and directions, finally came up with this:

    Same code as the ng-transclude directive, but with small addition of context binding that the directive watch and sets on the transcluded generated scope each time it changes. Same as ng-repeat does, but this allows:

    1. using the customize ng-transclude with ng-repeat without the hassle of rewriting ng-repeat and like angular/2 template outlet.
    2. Having the transcluded content keep access the grandparent scope while receiving direct context data from the parent. Same as template outlet again.

    The augmented ng-transclude function:

    return function ngTranscludePostLink(
       ...
      ) {
      let context = null;
      let childScope = null;
      ...
      $scope.$watch($attrs.context, (newVal, oldVal) => {
        context = newVal;
        updateScope(childScope, context);
      });
      ...
      $transclude(ngTranscludeCloneAttachFn, null, slotName);
      ...
      function ngTranscludeCloneAttachFn(clone, transcludedScope) {
         ...                                 
         $element.append(clone);
         childScope = transcludedScope;
         updateScope(childScope, context);
         ...
      }
      ...
      function updateScope(scope, varsHash) {
        if (!scope || !varsHash) {
          return;
        }
        angular.extend(scope, varsHash);
      }
    }
    

    And it's usage:

    App

    <my-list items="$ctrl.movies">
       <div>App data: {{ $ctrl.header }}</div>
       <div>Name:{{ name }} Year: {{ year }} Rating: {{ rating 
              }}</div>
    </my-list>
    

    MyList

    <ul>
      <li ng-repeat="item in $ctrl.items track by item.id">
       <div>Ng repeat item scope id: {{ $id }}</div>
       <cr-transclude context="item"></cr-transclude>
      </li>
    </ul>
    

    Full directive code can be found here on GitHub

    0 讨论(0)
  • 2020-12-09 06:45

    You need to output the name of the family in the template as well: http://jsfiddle.net/roadprophet/DsvX6/

    module.directive("tree", function($compile) {
        return {
            restrict: "E",
            transclude: true,
            scope: {family: '='},
            template:       
                '<ul>' + 
                    '<li ng-transclude></li>' +
                    '<li ng-repeat="child in family.children">' +
                        '<tree family="child">{{family.name}}</tree>' +
                    '</li>' +
                '</ul>',
            compile: function(tElement, tAttr, transclude) {
                var contents = tElement.contents().remove();
                var compiledContents;
                return function(scope, iElement, iAttr) {
                    if(!compiledContents) {
                        compiledContents = $compile(contents, transclude);
                    }
                    compiledContents(scope, function(clone, scope) {
                             iElement.append(clone); 
                    });
                };
            }
        };
    });
    

    EDIT

    You could also simplify by doing this: http://jsfiddle.net/roadprophet/DsvX6/2/

    <div ng-app="myapp">
        <div ng-controller="TreeCtrl">
            <tree family="treeFamily">           
            </tree>
        </div>
    </div>
    
    
    module.directive("tree", function($compile) {
        return {
            restrict: "E",
            transclude: true,
            scope: {family: '='},
            template:       
                '<ul>' + 
                    '<li ng-transclude></li>' +
                    '<p>{{ family.name }}</p>' + 
                    '<li ng-repeat="child in family.children">' +
                        '<tree family="child"></tree>' +
                    '</li>' +
                '</ul>',
            compile: function(tElement, tAttr, transclude) {
                var contents = tElement.contents().remove();
                var compiledContents;
                return function(scope, iElement, iAttr) {
                    if(!compiledContents) {
                        compiledContents = $compile(contents, transclude);
                    }
                    compiledContents(scope, function(clone, scope) {
                             iElement.append(clone); 
                    });
                };
            }
        };
    });
    

    EDIT Same source of the problem though. No template being passed to the inner tree directive. http://jsfiddle.net/roadprophet/DsvX6/3/

    <div ng-app="myapp">
        <div ng-controller="TreeCtrl">
            <tree family="treeFamily">           
                    <p>{{ family.name }}</p>
            </tree>
        </div>
    </div>
    
     template:       
                '<ul>' + 
                    '<li ng-transclude></li>' +
                    '<li ng-repeat="child in family.children">' +
                        '<tree family="child"><div ng-transclude></div></tree>' +
                    '</li>' +
                '</ul>'
    
    0 讨论(0)
提交回复
热议问题