Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular directive/child directive transclude inside ng-repeat

The problem is that child directive binds to parent however syntax {{name}} gets ignored by ng-repeat. What would be the right way achieving this?

HTML (Main/child directive)

<compact-select
    no-item-selected-text="Add a Customer"
    no-item-selected-icon="fa-user"
    search-placeholder="Type a customer name"
    cs-model="customer"
    cs-items="contacts"
>
    <display-item-template>
        <span>{{name}}</span>
        or
        <span>{{item.name}}</span>
    </display-item-template>
</compact-select>

Directive

angular.module('core').directive('compactSelect', [function($timeout) {
    return {
        templateUrl : 'modules/core/views/components/compact-select-tpl.html',
        bindToController: true,
        transclude: true,
        scope: {
            noItemSelectedText: '@',
            noItemSelectedIcon: '@',
            csModel: '=',
            csItems: '=csItems'
        },
        controllerAs : 'ctrl',
        controller : function($scope) {

        }
    };
}]).directive('displayItemTemplate', function($timeout) {
    return {
        require: '^compactSelect',
        restrict: 'E'
    }
});

Directive Template (modules/core/views/components/compact-select-tpl.html)

<div class="compact-select-repeater-box" style="" >
    <div ng-transclude ng-repeat="item in ctrl.csItems | filter:searchParam" class="compact-select-repeater" ng-class="ctrl.getHighlightedClass(item)" ng-click="ctrl.itemSelected(item)">
        <span>{{item.name}}</span>
        <span>{{item.id}}</span>
    </div>
    <div style="position:absolute;bottom:0">
        <a href="#">+ Click here to add customer {{ctrl.message}}</a>
    </div>
</div>

I can see that

<span>{{item.name}}</span>
<span>{{item.id}}</span>

Gets replaced with

<span></span>
or
<span>{{item.name}}</span>

and not with

<span>{{name}}</span>
or
<span>{{item.name}}</span>

Question: How do I get ng-repeat to respect html bindings syntax from child directive? Or is there is another way to achieve this?

like image 567
Tim Avatar asked Jun 17 '16 07:06

Tim


2 Answers

If i am not wrong, then you are trying to create a list view such that the template of the list would be provided by the user, but the methods (click,etc) would already be made available through the directive.

Now, since angular 1.3, the transcluded scope is a child of the directive isolated scope,

so, in your case, if you follow the correct hierarchy, you can access the directive scope from within the template provided by the user.

Here is your scope hierarchy:

Directive isolated scope --> ng-repeat new scope for every row --> transcluded scope.

so, if you want to access directive scope from the transcluded scope, you would need to do $parent (for ng-repeat) and then access item.name, like below:

<display-item-template>            
       <span>Item Name: {{$parent.item.name}}</span>            
</display-item-template>

Also, you don't need the bindings inside your compact-select-tpl, because you want that content to come from transclusion:

<div class="compact-select-repeater-box" style="" >

    <div ng-transclude ng-repeat="item in ctrl.csItems | filter:searchParam" 
    class="compact-select-repeater" 
    ng-class="ctrl.getHighlightedClass(item)" 
    ng-click="ctrl.itemSelected(item)">
       <!--  <span>{{item.name}}</span>
        <span>{{item.id}}</span> -->
    </div>

    <div style="position:absolute;bottom:0">
        <a href="#">+ Click here to add customer {{ctrl.message}}</a>
    </div>
</div>
like image 154
gaurav5430 Avatar answered Sep 28 '22 19:09

gaurav5430


The displayItemTemplate directive that you transclude inside the other directive has already it's data interpolated {{name}} and {{item.name}}.

If you don't have these variables in the $scope, it will transclude empty strings inside your spans.

Then in your compactSelect directive, the div that gets transcluded will have it's content overridden.

If you move the displayItemTemplate directive inside the other directive's template, the repeat will work. (you will need to remove ng(transclude and transclude: true

Fiddle

Additionally, if you use bindToController, don't put the attributes inside scope.

function compactSelect() {
  return {
    template : [
      '<div class="compact-select-repeater-box" style="" >',
      '<div ng-repeat="item in ctrl.csItems | filter:searchParam" class="compact-select-repeater" ng-class="ctrl.getHighlightedClass(item)" ng-click="ctrl.itemSelected(item)">',
        '<display-item-template>',
            '<span>{{item.name}}</span>',
        '</display-item-template>',
      '</div>',
      '<div style="position:absolute;bottom:0">',
          '<a href="#">+ Click here to add customer {{ctrl.message}}</a></div></div>',
    ].join(''),
    bindToController: {
      noItemSelectedText: '@',
      noItemSelectedIcon: '@',
      csItems: '=csItems'
    },
    scope: {},
    controllerAs : 'ctrl',
    controller : function($scope) {

    }
  }
}
function displayItemTemplate() {
    return {
        require: '^compactSelect',
        restrict: 'E'
    }
}
function SuperController() {
	this.name = "a name";
	this.contacts = [{name:"rob"}, {name:"jules"}, {name:"blair"}];
}
angular.module('myApp', []);
angular
    .module('myApp')
    .controller('SuperController', SuperController)
    .directive('compactSelect', compactSelect)
    .directive('displayItemTemplate', displayItemTemplate);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<div ng-app="myApp">
  <div ng-controller="SuperController as s">
    <compact-select
        no-item-selected-text="Add a Customer"
        no-item-selected-icon="fa-user"
        search-placeholder="Type a customer name"
        cs-items="s.contacts">
    </compact-select>
  </div>
</div>
like image 23
gyc Avatar answered Sep 28 '22 17:09

gyc