Angular JS Magazine

joi, 9 octombrie 2014

Angular Custom Directive with Compile Function and Transclusion

This tip shows you how to use a compile function in a custom directive. The below code simulates ng-repeat directive for a list of tennis players - the goal is to repeat the <li> element for each player:

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
    <script src="../angular/angular.min.js"></script>
    <link href="./css/thumbnail.css" rel="stylesheet"/>
    <script>
        angular.module("appModule", [])
                .controller("appCtrl", function ($scope) {
                    $scope.injury = "No";
                    $scope.court = {name: "Philippe Chatrier"};
                    $scope.players = [
                        { name: 'Nadal, Rafael (ESP)', rank: 1, age: '28', prizemoney: 66149345,
                            src: 'rafa.png' },
                        { name: 'Djokovic, Novak (SRB)', rank: 2, age: '27', prizemoney: 70704129,
                           src: 'novak.png'},
                        { name: 'Federer, Roger (SUI)', rank: 3, age: '33', prizemoney: 84827704,
                           src: 'roger.png' },
                        { name: 'Wawrinka, Stan (SUI)', rank: 4, age: '29', prizemoney: 13155060,
                           src: 'stan.png' }
                    ];
                    $scope.more_players = [
                        { name: 'Ferrer, David (ESP)', rank: 5, age: '32', prizemoney: 24034072,
                           src: 'david.png' },
                        { name: 'Tsonga, Jo-Wilfried (FRA)', rank: 11, age: '29', prizemoney: 1708240,
                           src: 'tonga.png' },
                        { name: 'Simon, Gilles (FRA)', rank: 26, age: '29', prizemoney: 760469,
                           src: 'simon.png' },
                        { name: 'Lopez, Feliciano (ESP)', rank: 20, age: '33', prizemoney: 1100579,
                           src: 'lopez.png' },
                        { name: 'Benneteau, Julien (FRA)', rank: 28, age: '32', prizemoney: 617688,
                           src: 'julien.png' },
                        { name: 'Verdasco, Fernando (ESP)', rank: 33, age: '30', prizemoney: 689219,
                           src: 'verdasco.png' },
                        { name: 'Mayer, Leonardo (ARG)', rank: 25, age: '27', prizemoney: 946294,
                           src: 'mayer.png' }
                    ];
                    $scope.addMorePlayers = function () {
                        $scope.players.push($scope.more_players[Math.floor((Math.random() *
                          (0 - ($scope.more_players.length-1)) + ($scope.more_players.length-1)))]);
                    }
                })
                .directive("atpThumbnail", function () {
                    return {
                        scope: {
                            data: "=singles",
                            prop: "@player"
                        },
                        transclude: 'element',
                        compile: function (element, attrs, transcludeFunction) {
                            return function ($scope, $element, $attr) {
                                $scope.$watch("data.length", function () {
                                    var parent = $element.parent();
                                    parent.children().remove();
                                    for (var i = 0; i < $scope.data.length; i++) {
                                        var childScope = $scope.$new();
                                        childScope[$scope.prop] = $scope.data[i];
                                        transcludeFunction(childScope, function (playerClone) {
                                            parent.append(playerClone);
                                        });
                                    }
                                });
                            }
                        }
                    }
                });
    </script>
</head>
<body>
<div ng-app="appModule" ng-controller="appCtrl">
    <div>
        <ul>
            <li atp-thumbnail singles="players" player="p">
                <img src="./images/{{p.src}}" width="70" height="100"/>
                <h3>{{p.name}} ({{p.age}} years old)</h3>
                <p>Rank: {{p.rank}}, Prize Money: {{p.prizemoney | currency}}</p>
            </li>
        </ul>
    </div>
    <div><button ng-click="addMorePlayers()">Add More Players</button></div>
</div>
</body>
</html>