Tim
Tim

Reputation: 2715

How to tell angular where to render child directives inside parent template (multiple transclude)?

I have problem that is inability to render child directive (selected-item-template) in the parent template.

Code bellow:

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>{{$parent.item.id}}</span>
        <span>{{$parent.item.name}}</span>
    </display-item-template>
    <selected-item-template>
       Your have selected customer: {{$parent.item.name}}
    </selected-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() {
    return {
        require: '^compactSelect',
        restrict: 'E'
    }
}).directive('selectedItemTemplate', function() {
    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>
    **HERE I WANT SELECTED ITEM TEMPLATE**
</div>

Question: How I can tell where child directive needs to be rendered?

Directive on ng-repeat works, but when I add two directives everything gets combined together and that's not what I want. Is there is a way to specify with ng-transclude where to render which directive? Like ng-transclude="displayItemTemplate" and ng-transclude="selectedItemTemplate" respectively?

Upvotes: 0

Views: 1299

Answers (2)

Tim
Tim

Reputation: 2715

I have figured out how to achieve following. It can be done with multiple transcludsions, since angular 1.5. I just need to define transcludeSlot.

Code bellow:

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>{{$parent.item.id}}</span>
        <span>{{$parent.item.name}}</span>
    </display-item-template>
    <item-selected-template>
       Your have selected customer: {{$parent.csModel.name}}
    </item-selected-template>
</compact-select>

Directive

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

        }
    };
}]);

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

<div class="compact-select-repeater-box" style="" >
    <div ng-transclude="repeaterItemSlot" 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 ng-transclude="itemSelectedTemplateSlot"></div>
</div>

So this works nicely similar to XAML.

Upvotes: 0

Maher
Maher

Reputation: 2547

This Example Based on Directives, and i want to show you how multi directives work together on one array.

I hope this helps you.

    var app = angular.module("app", []);

        app.controller("ctrl", function ($scope) {

            $scope.selectedList = [];

            $scope.data = [
                { name: "a", checked: true },
                { name: "b", checked: false }
            ];

            $scope.getResult = function () {
                console.log($scope.selectedList);
            }

        });

        app.directive("directiveA", [function () {
            return {
                restrict: "E",
                template: "<ul ng-repeat=\"item in items\">" +
                    "<li><directive-c data=\"item\"></directive-c> {{item.name}} <directive-b data=\"item\"></directive-b></li>" +
                    "</ul>",
                scope: {
                    items: "="
                }
            };
        }]);

        app.directive("directiveB", function () {
            return {
                restrict: "E",
                template: "<button ng-click=\"renderData(data)\">{{data.checked ? 'unchecked':'checked'}}</button>",
                scope: {
                    data: "="
                },
                link: function (scope) {

                    scope.renderData = function (data) {
                        data.checked = !data.checked;
                    }

                }
            }
        });

        app.directive("directiveC", function () {
            return {
                restrict: "E",
                template: "<input type=\"checkbox\" ng-model=\"data.checked\">",
                scope: {
                    data: "="
                }
            }
        });

        app.directive("directiveD", function () {
            return {
                restrict: "E",
                template: "<ul ng-repeat=\"item in items\">" +
                    "<li ng-if=\"item.checked\">{{item.name}}</li>" +
                    "</ul>",
                scope: {
                    items: "=",
                    result: "="
                },
                link: function (scope) {

                    scope.$watch("items", function (newValue) {
                        scope.result = [];
                        if (newValue) {
                            angular.forEach(scope.items, function (item, index) {
                                if (item.checked) scope.result.push(item);
                            });
                        }
                    }, true);


                }
            }
        });
<!DOCTYPE html>
<html ng-app="app" ng-controller="ctrl">
<head>
    <title></title>
</head>
<body>

    <h3>items</h3>
  
    <directive-a items="data"></directive-a>

    <h3>selected items</h3>

    <directive-d items="data" result="selectedList"></directive-d>

    <hr />

    <button ng-click="getResult()">get selected list</button>
    <small>after click, check your console</small>

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

</body>
</html>

Upvotes: 0

Related Questions