Подтвердить что ты не робот

Динамическое добавление директив в 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>

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

4b9b3361

Ответ 1

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

***** NEW CONTENT *****

С тех пор я сделал эту директиву более общей, поддерживая анализируемый (типичный атрибут angular) атрибутов.

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

В случае использования OP вы можете сделать:

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

;

В конкретном случае в этом вопросе можно просто переписать директиву бит, чтобы он применил директиву к span по классу, так:

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>

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

Ответ 2

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

    }
}});

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

Ответ 3

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

Одно возможное решение изложено в: AngularJS - как иметь директиву с динамической под-директивой

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

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