Asked  6 Months ago    Answers:  5   Viewed   49 times

There are a couple of popular recursive angular directive Q&A's out there, which all come down to one of the following solutions:

  • manually incrementally 'compile' HTML based on runtime scope state
    • example 1 [stackoverflow]
    • example 2 [angular jsfiddles page]
  • don't use a directive at all, but a <script> template which refers to itself
    • example 1 [google groups]

The first one has the problem that you can't remove previously compiled code unless you comprehensibly manage the manual compile process. The second approach has the problem of... not being a directive and missing out on its powerful capabilities, but more urgently, it can't be parameterised the same way a directive can be; it's simply bound to a new controller instance.

I've been playing with manually doing an angular.bootstrap or @compile() in the link function, but that leaves me with the problem of manually keeping track of elements to remove and add.

Is there a good way to have a parameterized recursive pattern that manages adding/removing elements to reflect runtime state? That is to say, a tree with a add/delete node button and some input field whose value is passed down a node's child nodes. Perhaps a combination of the second approach with chained scopes (but I have no idea how to do this)?

 Answers

43

Inspired by the solutions described in the thread mentioned by @dnc253, I abstracted the recursion functionality into a service.

module.factory('RecursionHelper', ['$compile', function($compile){
    return {
        /**
         * Manually compiles the element, fixing the recursion loop.
         * @param element
         * @param [link] A post-link function, or an object with function(s) registered via pre and post properties.
         * @returns An object containing the linking functions.
         */
        compile: function(element, link){
            // Normalize the link parameter
            if(angular.isFunction(link)){
                link = { post: link };
            }

            // Break the recursion loop by removing the contents
            var contents = element.contents().remove();
            var compiledContents;
            return {
                pre: (link && link.pre) ? link.pre : null,
                /**
                 * Compiles and re-adds the contents
                 */
                post: function(scope, element){
                    // Compile the contents
                    if(!compiledContents){
                        compiledContents = $compile(contents);
                    }
                    // Re-add the compiled contents to the element
                    compiledContents(scope, function(clone){
                        element.append(clone);
                    });

                    // Call the post-linking function, if any
                    if(link && link.post){
                        link.post.apply(null, arguments);
                    }
                }
            };
        }
    };
}]);

Which is used as follows:

module.directive("tree", ["RecursionHelper", function(RecursionHelper) {
    return {
        restrict: "E",
        scope: {family: '='},
        template: 
            '<p>{{ family.name }}</p>'+
            '<ul>' + 
                '<li ng-repeat="child in family.children">' + 
                    '<tree family="child"></tree>' +
                '</li>' +
            '</ul>',
        compile: function(element) {
            // Use the compile function from the RecursionHelper,
            // And return the linking function(s) which it returns
            return RecursionHelper.compile(element);
        }
    };
}]);

See this Plunker for a demo. I like this solution best because:

  1. You don't need an special directive which makes your html less clean.
  2. The recursion logic is abstracted away into the RecursionHelper service, so you keep your directives clean.

Update: As of Angular 1.5.x, no more tricks are required, but works only with template, not with templateUrl

Tuesday, June 1, 2021
 
e_i_pi
answered 6 Months ago
78

This isn't possible with [innerHTML]="..." at all.
You can compile components at runtime to get components and directives for dynamic HTML.

See How can I use/create dynamic template to compile dynamic Component with Angular 2.0? for more details.

Tuesday, July 6, 2021
 
Xatoo
answered 5 Months ago
52

The problem arises because within your loop, this gets redefined to the inner function scope.

countChildren(n, levelWidth, level) {
    var self = this; // Get a reference to your object.

    if (n.children && n.children.length > 0) {
        if (levelWidth.length <= level + 1) {
            levelWidth.push(0);
        }
        levelWidth[level + 1] += n.children.length;

        n.children.forEach(function (n) {
            // Use "self" instead of "this" to avoid the change in scope.
            self.countChildren(n, levelWidth, level+1);
        });    
    }
    // Return largest openend width
    return levelWidth;
}
Wednesday, August 11, 2021
 
LOKESH
answered 4 Months ago
48

Expressions in Angular2 are very similar to expressions in Angular in terms of the scope of what they allow.

JavaScript expressions that promote side effects are prohibited including

  • Assignment (= +=, -=)
  • Using the new keyword
  • Chaining expressions using a semicolon or comma
  • Increment (++) and decrement operators

Furthermore, there is no support for bitwise operators like | or &

Generally, it's a good idea to put complex JavaScript logic inside a controller or component, instead of inside a view. This is because of the Separation of Concerns design principle and making code more modular and readable.

https://angular.io/docs/ts/latest/guide/template-syntax.html#!#template-expressions

Tuesday, August 24, 2021
 
Stubbi
answered 3 Months ago
26

i+1 means "return value that is one larger than i, don't change i"

i++ means "increment i by one, but return the original value"

++i means "increment i by one and return the incremented value"

So in this case if you use i+1 you're not changing the value of i, but you're sending a value one larger than i as an argument. You could also use the ++i, if you would need the value in i to change also.

Examples

i = 10
a = i+1
// a = 11, i = 10

i = 10
a = i++
// a = 10, i = 11

i = 10
a = ++i
// a = 11, i = 11

The same applies also for i-1, i-- and --i

Friday, November 5, 2021
 
Mike Schall
answered 3 Weeks ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share