dynamically adding directives in ng-repeat

前端 未结 3 1694
北恋
北恋 2020-12-08 05:24

I am trying to dynamically add different directives in an ng-repeat however the output is not being interpreted as directives.

I\'ve added a simple example here: htt

相关标签:
3条回答
  • 2020-12-08 05:41

    i faced with the same problem in one of my projects and you can see how i solve this problem on 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) {
    
        }
    }});
    

    I hope it will usefull.

    0 讨论(0)
  • 2020-12-08 05:45

    I know this is an old question, but google brought me here, and I didn't like the answers here... They seemed really complicated for something that should be simple. So I created this directive:

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

    I've since made this directive more generic, supporting a parsed (the typical angular value) "attributes" attribute.

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

    For the OP's use case, you could do:

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

    Or to use it as an attribute directive:

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

    ***** END NEW CONTENT ******

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

    For the specific case in this question, one can just rewrite the directive a bit to make it apply the directive to a span by class, as so:

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

    Then you can use this anywhere and select a directive by name dynamically. Use it like so:

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

    I purposely kept this directive simple and straightforward. You may (and probably will) have to reword it to fit your needs.

    0 讨论(0)
  • 2020-12-08 05:58

    I don't think you'll be able to just assign the directive as a class name - you would need to run this through $compile again, which would be heading down the path towards recursion errors.

    One possible solution is outlined at: AngularJS - how to have a directive with a dynamic sub-directive

    If it works for your use case, you can use templates instead:

    <div ng-repeat='template in inner' ng-include='template'></div>
    
    0 讨论(0)
提交回复
热议问题