Detailed use of transclude in angular advanced articles

Keywords: Javascript angular Attribute

The transclude attribute of the angular directive is a difficult place for beginners to understand. The transclude can be set to false (default), true, or object values. If it is not set, it defaults to false, which means you do not need to embed the contents of the element in the directive into the template.

When transclude is true, the contents of the element containing the instruction are embedded in the element containing the ng-transclude instruction in the template, for example:

index.html

<!DOCTYPE html>
<html ng-app="myapp">
<head>
    <meta charset="utf-8">
    <title>angular test</title>
</head>
<body ng-controller="myCtrl">
    <div hello="{{name}}">Hello</div>
</body>
<script src="./node_modules/angular/angular.js"></script>
<script src="./index.js"></script>
</html>

index.js

let app = angular.module('myapp',[]);
app.controller('myCtrl', $scope =>{
    $scope.name = "Jhon";
});
app.directive('hello', () =>{
    return {
        restrict: 'A',
        template: '<div><span ng-transclude></span>{{name}}</div>',
        transclude: true,
        scope:{
            name: "@hello"
        }
    }
});

The results after running are as follows:

<div hello="Jhon" class="ng-isolate-scope">
    <div class="ng-binding">
        <span ng-transclude="">Hello</span>
        Jhon
    </div>
</div>

When instruction elements contain content that needs to be embedded in different parts of the instruction template, set transclude as an object at this time, for example, the following example I used in my project:

index.html

<!DOCTYPE html>
<html ng-app="myapp">
<head>
    <meta charset="utf-8">
    <title>angular test</title>
</head>
<body ng-controller="myCtrl">
    <panel>
        <panel-header>{{title}}</panel-header>
        <panel-body>{{content}}</panel-body>
        <panel-footer>{{footer}}</panel-footer>
    </panel>
</body>
<script src="./node_modules/angular/angular.js"></script>
<script src="./index.js"></script>
</html>

index.js

let app = angular.module('myapp',[]);
app.controller('myCtrl', ['$scope', $scope =>{
    $scope.title = "Title";
    $scope.content = "content";
    $scope.footer = "footer";
}]);
app.directive('panel', () =>{
    return {
        restrict: 'E',
        replace: true,
        transclude: {
            'header': '?panelHeader',
            'body': 'panelBody',
            'footer': '?panelFooter'
        },
        template: `
            <div class="panel">
                <div class="panel-header" ng-transclude="header"></div>
                <div class="panel-body" ng-transclude="body"></div>
                <div class="panel-footer" ng-transclude="footer"></div>
            </div>`
    }
});

The results are as follows:

<div class="panel">
    <div class="panel-header" ng-transclude="header">
        <panel-header class="ng-binding ng-scope">
            //Title
        </panel-header>
    </div>
    <div class="panel-body" ng-transclude="body">
        <panel-body class="ng-binding ng-scope">
            //content
        </panel-body>
    </div>
    <div class="panel-footer" ng-transclude="footer">
        <panel-footer class="ng-binding ng-scope">
            //footer
        </panel-footer>
    </div>
</div>

There are three instructions inside the instruction element here. They must be called in the form of E. They are inserted in different positions of the template, tranclude specifies the location to insert, transclude is an object of key-value pair, key specifies the location to insert the template, value is what to insert,?Indicates that an instruction does not necessarily exist at this embedding point, otherwise the instruction must be inserted at this point or an error will occur.

It is worth noting that this example also proves that the scope of the elements contained in an instruction inherits from the parent scope of the instruction rather than from the isolated scope.

 

In addition to using the ng-transclude directive to specify where content is embedded, there are two ways to do this.

The first is to use the $transclude service in the controller, such as the following code:

index.html

<!DOCTYPE html>
<html ng-app="myapp">
<head>
    <meta charset="utf-8">
    <title>angular test</title>
</head>
<body ng-controller="myCtrl">
    <hello name="{{name}}"><span>{{action}}</span></hello>
</body>
<script src="./node_modules/angular/angular.js"></script>
<script src="./index.js"></script>
</html>

index.js

let app = angular.module('myapp',[]);
app.controller('myCtrl', ['$scope', $scope =>{
    $scope.name = "Jhon";
    $scope.action = "Hello";
}]);
app.directive('hello', () =>{
    return {
        restrict: 'E',
        transclude: true,
        controller: ['$scope', '$element', '$transclude', ($scope, $element, $transclude) =>{
            $transclude(clone =>{
                //$element.find Can only be found by tag name
                $element.find('span').append(clone);
            });
        }],
        template: '<div><span></span>{{name}}</div>',
        scope: {
            name: '@'
        }
    }
});

The final results are as follows:

<hello name="Jhon" class="ng-isolate-scope">
    <div class="ng-binding">
        <span>
            <span class="ng-binding ng-scope">
                //Hello
            </span>
        </span>
        Jhon
    </div>
</hello>

Whereas $element in the controller is the template after compilation, the parameter clone in $transclude is what the compiled instruction contains.Templates and content can be processed simultaneously.

 

Another way is to specify the third transcludeFn parameter in the compile, as follows:

 index.js

let app = angular.module('myapp',[]);
app.controller('myCtrl', ['$scope', $scope =>{
    $scope.name = "Jhon";
    $scope.action = "Hello";
}]);
app.directive('hello', () =>{
    return {
        restrict: 'E',
        transclude: true,
        compile: (tElement, tAttrs, transcludeFn) =>{
            return (scope, element, attrs) =>{
                scope.action = "hello";
                transcludeFn(scope, (clone) =>{
                    element.find('span').append(clone);
                });
            };
        },
        template: '<div><span></span>{{name}}</div>',
        scope: {
            name: '@'
        }
    }
});

This file achieves the same functions in the above controllers. transcludeFn takes two parameters, a scope, a function, and like the controller, the clone parameter of this function is what you want to embed after compilation.The only difference is that the scope for compiling this clone is the first parameter passed in, while clone in the controller is the parent scope that inherits the instruction.

Posted by ki on Thu, 23 May 2019 10:49:48 -0700