Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

dynamically adding directives in ng-repeat

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: http://plnkr.co/edit/6pREpoqvmcnJJWzhZZKq

Controller:

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

Directive:

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>

How do I make angular pick up the directive specified in the class that is output via ng-repeat?

like image 214
drch Avatar asked Apr 28 '13 15:04

drch


People also ask

What can I use instead of NG-repeat?

You can consider using transclusion inside a custom directive, to achieve the behavior you are looking for without using ng-repeat.

What is difference between ng-repeat and Ng options?

ng-options is the directive which is designed specifically to populate the items of a dropdown list. One major advantage using ng-options for the dropdown is, it allows us to pass the selected value to be an object. Whereas, using ng-repeat the selected value can only be string.

Does ng-repeat create a new scope?

The controller creates a child scope and the ng-repeat , which will create an LI element for each item in the list of To Do items. It also creates a new scope for each element.

How do I get the index of an element in NG-repeat?

Note: The $index variable is used to get the Index of the Row created by ng-repeat directive. Each row of the HTML Table consists of a Button which has been assigned ng-click directive. The $index variable is passed as parameter to the GetRowIndex function.


1 Answers

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.

like image 166
Eric F. Avatar answered Oct 19 '22 15:10

Eric F.