2013-04-28 7 views
19

Sto cercando di aggiungere dinamicamente direttive diverse in una ng-repeat, tuttavia l'output non viene interpretato come direttive.aggiungendo dinamicamente le direttive in ng-repeat

Ho aggiunto un semplice esempio qui: http://plnkr.co/edit/6pREpoqvmcnJJWzhZZKq

Controller:

$scope.colors = [{name:"red"}, {name: "blue"}, {name:"yellow"}]; 

direttiva:

app.directive("red", function() { 
    return { 
     restrict: 'C', 
     template: "RED directive" 
    } 
}); 

Html:

<ul> 
    <li ng-repeat="color in colors"> 
    <span class="{{color.name}}"></span> 
    </li> 
</ul> 

Come faccio a creare un golare raccoglie la direttiva specificata nello class che viene emessa tramite ng-repeat?

+0

domanda interessante! – TheHippo

+0

Non sono sicuro che sia possibile. È possibile passare color.name come parametro a una singola direttiva, quindi controllare il valore ed eseguire/chiamare il codice appropriato da lì. – mikel

risposta

13

So che questa è una vecchia domanda, ma Google mi ha portato qui e non mi piacevano le risposte qui ... Sembravano davvero complicate per qualcosa che dovrebbe essere semplice. Così ho creato questa direttiva:

***** ***** nuovi contenuti

allora ho fatto di questa direttiva più generico, sostenendo una analizzata (il valore tipico angolare) "attributi" attributo.

/** 
* Author: Eric Ferreira <http://stackoverflow.com/users/2954747/eric-ferreira> ©2016 
* 
* This directive takes an attribute object or string and adds it to the element 
* before compilation is done. It doesn't remove any attributes, so all 
* pre-added attributes will remain. 
* 
* @param {Object<String, String>?} attributes - object of attributes and values 
*/ 
.directive('attributes', function attributesDirective($compile, $parse) { 
    'use strict'; 

    return { 
     priority: 999, 
     terminal: true, 
     restrict: 'A', 
     compile: function attributesCompile() { 
      return function attributesLink($scope, element, attributes) { 
       function parseAttr(key, value) { 
        function convertToDashes(match) { 
         return match[0] + '-' + match[1].toLowerCase(); 
        } 

        attributes.$set(key.replace(/([a-z][A-Z])/g, convertToDashes), value !== undefined && value !== null ? value : ''); 
       } 

       var passedAttributes = $parse(attributes.attributes)($scope); 

       if (passedAttributes !== null && passedAttributes !== undefined) { 
        if (typeof passedAttributes === 'object') { 
         for (var subkey in passedAttributes) { 
          parseAttr(subkey, passedAttributes[subkey]); 
         } 
        } else if (typeof passedAttributes === 'string') { 
         parseAttr(passedAttributes, null); 
        } 
       } 

       $compile(element, null, 999)($scope); 
      }; 
     } 
    }; 
}); 

Per caso d'uso del PO, si potrebbe fare:

<li ng-repeat="color in colors"> 
    <span attributes="{'class': color.name}"></span> 
</li> 

o di usarlo come una direttiva attributo:

<li ng-repeat="color in colors"> 
    <span attributes="color.name"></span> 
</li> 

***** FINE nuovi contenuti ** ****

/** 
* Author: Eric Ferreira <http://stackoverflow.com/users/2954747/eric-ferreira> ©2015 
* 
* This directive will simply take a string directive name and do a simple compilation. 
* For anything more complex, more work is needed. 
*/ 
angular.module('attributes', []) 

.directive('directive', function($compile, $interpolate) { 
    return { 
     template: '', 
     link: function($scope, element, attributes) { 
      element.append($compile('<div ' + attributes.directive + '></div>')($scope)); 
     } 
    }; 
}) 

; 

Per il caso specifico in questo q Uestion, si può semplicemente riscrivere la direttiva un po 'per farlo applicare la direttiva a un arco per classe, come così:

angular.module('attributes', []) 

.directive('directive', function($compile, $interpolate) { 
    return { 
     template: '', 
     link: function($scope, element, attributes) { 
      element.replaceWith($compile('<span class=\"' + attributes.directive + '\"></span>')($scope)); 
     } 
    }; 
}) 

; 

Quindi è possibile utilizzare questo ovunque e selezionare una direttiva in base al nome in modo dinamico. Usalo in questo modo:

<li ng-repeat="color in colors"> 
    <span directive="{{color.name}}"></span> 
</li> 

Ho volutamente mantenuto questa direttiva semplice e diretto. Potresti (e probabilmente lo farai) riformulare per soddisfare le tue esigenze.

+1

Perfetto! Proprio quello che stavo pensando! – ironic

+1

La migliore risposta per un tiro lungo. – Brian

3

Non penso che sarà possibile assegnare semplicemente la direttiva come nome di classe: sarà necessario eseguire nuovamente questa operazione tramite $compile, che si dirigerebbe verso il basso verso gli errori di ricorsione.

Una possibile soluzione è delineata a: AngularJS - how to have a directive with a dynamic sub-directive

Se funziona per il vostro caso d'uso, è possibile utilizzare i modelli invece:

<div ng-repeat='template in inner' ng-include='template'></div> 
9

ho affrontato con lo stesso problema in uno dei miei progetti e si può vedere come posso risolvere questo problema sul jsfiddle

HTML:

<div class="page-wrapper" ng-controller="mainCtrl"> 
    <div class="page"> 
    <h3>Page</h3> 
    <ul> 
     <li ng-repeat="widget in widgets"><div proxy="widget" proxy-value="{{widget}}"></div></li> 
    </ul> 
</div> 

JS:

var app = angular.module('app',[]); 
app.controller('mainCtrl', ['$scope', '$q', 'widgetAPI', function($scope, $q, widgetAPI) { 
$scope.widgets = []; 
widgetAPI.get().then(
    function(data) { 
     $scope.widgets = data; 
    }, 
    function(err) { 
     console.log("error", err); 
    } 
);}]) 

.service('widgetAPI', ['$q', function($q) { 
var api = {}; 
api.get = function() { 
    //here will be $http in real app 
    return $q.when(
     [ 
      { 
       component: 'wgtitle', 
       title: "Hello world", 
       color: '#DB1F1F', 
       backgroundColor: '#c1c1c1', 
       fontSize: '32px' 
      }, 
      { 
       component: 'wgimage', 
       src: "http://cs425622.vk.me/v425622209/79c5/JgEUtAic8QA.jpg", 
       width: '100px' 
      }, 
      { 
       component: 'wgimage', 
       src: "http://cs425622.vk.me/v425622209/79cf/S5F71ZMh8d0.jpg", 
       width: '400px' 
      } 

     ] 
    ); 
}; 
return api;}]) 

.directive('proxy', ['$parse', '$injector', '$compile', function ($parse, $injector, $compile) { 
return { 
    replace: true, 
    link: function (scope, element, attrs) { 
     var nameGetter = $parse(attrs.proxy); 
     var name = nameGetter(scope); 
     var value = undefined; 
     if (attrs.proxyValue) { 
      var valueGetter = $parse(attrs.proxyValue); 
      value = valueGetter(scope); 
     } 

     var directive = $injector.get(name.component + 'Directive')[0]; 
     if (value !== undefined) { 
      attrs[name.component] = value; 
     } 
     var a = $compile(directive.template)(scope); 
     element.replaceWith(a); 
    } 
}}]) 

.directive('wgtitle', function() { 
return { 
    restrict: 'A', 
    scope: true, 
    replace: true, 
    template: '<h1 style="color:{{widget.color}}; font-size:{{widget.fontSize}}; background:{{widget.backgroundColor}}" >{{widget.title}}</h1>', 
    link: function(scope, element, attrs) { 

    } 
}}) 

.directive('wgimage', function() { 
return { 
    restrict: 'A', 
    scope: true, 
    replace: true, 
    template: '<img style="width:{{widget.width}}" src="{{widget.src}}"/>', 
    link: function(scope, element, attrs) { 

    } 
}}); 

spero che sarà utile.

+0

Questo è un ottimo modo per evitare uno switch/se in una ng-repeat scegliere direttive diverse in base ad alcuni criteri. Secondo me, questa è la risposta corretta. –

+0

Per qualche motivo non riesco a capire, la funzione di collegamento delle direttive inserite dinamicamente non viene chiamata. Puoi aiutarmi a capire perché e/o pensare a una soluzione alternativa? – dvdplm

+0

JS fiddle fork con avvisi che non vengono mai chiamati: http://jsfiddle.net/rvss3oe1/ – dvdplm