2013-04-28 2 views
19

Я пытаюсь динамически добавлять разные директивы в ng-repeat, однако вывод не интерпретируется как директивы.директивы динамического добавления в ng-repeat

Я добавил простой пример здесь: http://plnkr.co/edit/6pREpoqvmcnJJWzhZZKq

Контроллер:

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

Директива:

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> 

Как сделать gular выбрать директиву, указанную в class, которая выводится через ng-repeat?

+0

интересный вопрос! – TheHippo

+0

Я не уверен, что это возможно. Однако вы можете передать color.name как параметр в одну директиву, а затем проверить значение и запустить/вызвать соответствующий код. – mikel

ответ

13

Я знаю, что это старый вопрос, но google привел меня сюда, и мне здесь не понравились ответы ... Они казались очень сложными для чего-то, что должно быть простым. Так что я создал эту директиву:

***** ***** НОВЫЙ КОНТЕНТ

Я так сделал эту директиву более общий характер, поддерживая обработанную (типичное значение угла) «атрибуты» атрибут.

/** 
* 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); 
      }; 
     } 
    }; 
}); 

Для случая использования в ФП, вы можете сделать:

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

Или использовать его в качестве директивы атрибута:

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

***** КОНЕЦ НОВОГО СОДЕРЖАНИЯ ** ****

/** 
* 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)); 
     } 
    }; 
}) 

; 

Для конкретного случая в этом q uestion, можно просто переписать директиву немного, чтобы сделать его применить директиву пролета по классам, а так:

angular.module('attributes', []) 

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

; 

Затем вы можете использовать это в любом месте и выберите директиву по имени динамически. Используйте его так:

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

Я преднамеренно сохранил эту директиву простой и понятной. Вы можете (и, вероятно, будете) переписывать его в соответствии с вашими потребностями.

+1

Отлично! О чем я думал! – ironic

+1

Лучший ответ на длинный выстрел. – Brian

3

Я не думаю, что вы сможете просто назначить директиву как имя класса - вам нужно будет снова запустить это через $compile, который будет двигаться по пути к ошибкам рекурсии.

Одно из возможных решений описано в: AngularJS - how to have a directive with a dynamic sub-directive

Если он работает для случая использования, вы можете использовать шаблоны вместо:

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

я столкнулся с той же проблемой в одном из моих проектов и вас можно увидеть, как я могу решить эту проблему на 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) { 

    } 
}}); 

Я надеюсь, что это будет полезно,.

+0

Это невероятно хороший способ избежать переключения/если в ng-repeat выбрать разные директивы, основанные на некоторых критериях. На мой взгляд, это правильный ответ. –

+0

По какой-то причине я не могу понять, что функция связывания динамически вставленных директив не вызывается. Можете ли вы помочь мне понять, почему и/или думать об обходном пути? – dvdplm

+0

Вилка для скрипки JS с предупреждениями, которые никогда не называются: http://jsfiddle.net/rvss3oe1/ – dvdplm