Créer une directive conteneur dans angularjs

J'essaie donc de créer une directive qui mettra en page une collection d'éléments dans des colonnes. Dans le plunker, j'ai une version extrêmement simplifiée qui n'utilise qu'un seul ul, mais ce n'est pas important. Je veux que la directive soit appelée comme.

<my-column-layout collection="names"> <tab name="{{ item }}"></tab> </my-column-layout> 

Je veux utiliser le html interne (l'onglet ici) comme modèle pour chaque élément de la collection. J'ai essayé d'avoir juste une répétition ng dans le modèle de ma colonne-disposition comme

 template : '<ul><li ng-repeat="item in collection" ng-transclude></li></ul> 

Qui a fonctionné, mais il n'a pas eu accès à la portée des contrôleurs contenant, de sorte que je n'ai pas eu d'événements de clic sur l'onglet et que l'on appelle une fonction dans le contrôleur. Je pense donc que je suis dans la bonne direction avec l'utilisation de transclus, mais pas sûr. De même, lorsque j'essaie d'ajouter un autre nom à la collection de noms, cela ne s'affiche pas dans la collection de ma directive. Ma portée. $ Watch ('collection' …) n'est jamais appelé.

Http://plnkr.co/edit/4vyZDAhBcbULEd3uIznh?p=preview

Espérons que quelqu'un peut aider

Je fais quelque chose que je pense être similaire. Permettez-moi de savoir si j'ai manqué quelque chose. J'ai une directive qui fait une répétition ng translucide basée sur des données distantes. Voici comment cela fonctionne.

Mettre à jour

C'est le modèle dans le balisage de la page qui est le problème. Cependant, si vous souhaitez que le modèle ng-repeat existe sur le même balisage de page, vous pouvez le faire:

 <script type="text/ng-template" id="navbar.html"> <li ng-repeat="item in items" ng-class="{active: item.selected}"> <a href="/{{item.link}}">{{item.title}}</a> </li> </script> 

Pas exactement la même chose, mais il vous appartient du même effet – un modèle sur la même page que la directive – tout simplement pas imbriqué avec elle.

Fin de la mise à jour

J'ai le même tableau dans les domaines parent et enfant: c.- $scope.items . $scope.items . Parce que c'est un type de référence, à travers un héritage prototypique, les deux champs font référence au même objet. Dans l'emplacement qui ne met pas à jour la propriété, je l'initialise comme ceci $scope.items = $scope.items || []; $scope.items = $scope.items || []; – c'est-à-dire si la propriété n'a pas été initialisée, initialisez-la, sinon gardez-la.

 directive('navbar', ['$location', '$http', function ($location, $http) { return { restrict: 'E', transclude: true, scope: { heading: '@'}, controller: 'NavbarCtrl', templateUrl: 'navbar.html', replace: true, link: function ($scope, $element, $attrs, navbarCtrl) { $scope.items = []; $scope.heading = $scope.heading || $attrs.heading; $http.get(itemsUrl).success(function(data) { $scope.items = ... async get of data ... ; navbarCtrl.selectByUrl($location.absUrl()); }); $scope.$watch('$location.absUrl()', function (locationPath) { navbarCtrl.selectByUrl(locationPath) }); } } }]) 

La montre $ de la directive appelle une fonction de contrôleur, et cette fonction a accès à la portée du contrôleur par sa fermeture.

 function NavbarCtrl($scope, $timeout, $http, $location, $attrs) { $scope.items = $scope.items || []; this.select = $scope.select = function (item) { angular.forEach($scope.items, function (item) { item.selected = false; }); item.selected = true; }; this.selectByUrl = function (url) { angular.forEach($scope.items, function (item) { if ('http://' + item.link === url) { $scope.select(item); } }); }; } 

Ensuite, dans mon modèle, que je transmet, j'ai:

 <li ng-repeat="item in items" ng-class="{active: item.selected}"> <a href="/{{item.link}}">{{item.title}}</a> </li> 

Dans le balisage de la page, je l'utilise comme ceci:

 <div ng-controller="NavbarCtrl"> <navbar heading="Navbar Heading"/> </div> 

Répéteur personnalisé …

Construire un répéteur personnalisé est une tâche compliquée. Principalement en raison des problèmes de performance, mais aussi parce qu'il devrait bien fonctionner avec d'autres directives.

Si vous avez vraiment besoin de créer un, vous devez d'abord plonger dans ngRepeat code source pour comprendre certaines considérations et ensuite le muter en fonction de vos besoins.

NgRepeat utilise maintenant $ watchCollection ( depuis 1.2 ) qui a remplacé le pendentif $ .


Solution

Donc, ma recommandation est de ne pas créer un répéteur personnalisé , utilisez ngRepeat !

Je ne sais toujours pas ce que vous voulez atteindre, mais c'est la construction.

Voici un plunker: http://plnkr.co/edit/pziqRzz0i1mU6eG5lAmd?p=preview

Créez une autre directive personnalisée au lieu de ngTransclude

 app.directive('myTransclude',function(){ return { require: "^myColumnLayout", link: function(scope,elm,attr,ctrl,$transclude){ $transclude(function(clone){ elm.empty(); elm.append(clone); ctrl.do("Hi") }) } } }); 

Créez votre directive avec ng-repeating my-transclude et un contrôleur:

 app.directive('myColumnLayout', function() { return { restrict: 'EA', transclude: true, controller: function(){ this.do = function(x) { console.log(x) } }, template: '<ul><li ng-repeat="item in collection track by $index" my-transclude></li></ul>', scope: { collection: '=' } } }); 

Pourquoi?

  • Maintenant, vous avez le contrôle total de la phase de transclusion dans my-transclude
  • Vous pouvez définir sur le contrôleur tout ce que vous devez partager dans le contenu transcrit.
  • Vous ne vous taisez pas avec des choses répétées.

Ce n'est pas clair pour moi ce que vous faites exactement, mais je corrige une erreur dans votre directive comme suit.

  1. Effacez les enfants avant de les recréer.
  2. Notez le troisième paramètre de la fonction $ watch. Il est nécessaire de regarder une collection.

     app.directive('myColumnLayout', function () { return { restrict: 'EA', transclude: true, scope: { collection: '=' }, link: function (scope, elem, attrs, container, transclude) { scope.$watch('collection', function (newVal, oldVal) { elem.empty(); for (var i = 0; i < newVal.length; i++) { var li = angular.element('<li></li>'); var scp = scope.$parent.$new(); scp.item = newVal[i]; transclude(scp, function (clone) { elem.append(clone); }); } }, true); } } });