Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conditionally add angular directive attribute to an element

Is there a straight-forward, simple way to do the following -

<div class="my-class" my-custom-directive="{{evaluate expression}}"></div>

So that angular won't add the directive unless the expression is evaluated to true?

Edit:

The directive has to be an attribute so please, no solutions like ng-if with restrict: 'E',
ng-class with restrict: 'C' or ng-attr - which doesn't work with custom directives.

like image 212
Daniel Avatar asked May 07 '15 16:05

Daniel


People also ask

How do you do a conditional directive?

Currently, there is NO way to conditionally apply a directive to a component. This is not supported. The components which you have created can be added or removed conditionally. There is already an issue created for the same with angular2 , so it should be the case with angular4 aswell.

Can we import directives in Angular?

You can put commonly used directives, pipes, and components into one module and then import just that module wherever you need it in other parts of your application. Notice the following: It imports the CommonModule because the module's component needs common directives.

What is custom attribute directive in Angular?

Using custom attribute directive we can change appearances such as text color, background color and font size of a body of an HTML element that can be called the host element. To change appearance Angular provides ElementRef class that can directly access DOM.

Can directive be used as component?

A component is a single unit that encapsulates both view and logic whereas directives are used to enhance the behavior of components or dom elements and it doesn't have any templates. Component extends directive so every component is a directive.


2 Answers

It's possible to do this by creating a directive with a high priority and terminal: true. Then you can fiddle with the element attributes (add or remove them) and then recompile the element to let the directives run.

Here is the example as a plunk: http://plnkr.co/edit/DemVGr?p=info

Change the expression in the "directive-if" attribute to keep/remove the "logger" directive.

If the expression for an attribute evaluates to false then it will be removed.

<div directive-if="{'logger': 'myValue == 1'}"
     logger="testValue">
    <p>"logger" directive exists? <strong>{{logger}}</strong></p>
</div>

Here is the directive implementation.

With some minor tweaking you could swap this around to add directives instead of removing them if that's what you would prefer.

/**
 * The "directiveIf" directive allows other directives
 * to be dynamically removed from this element.
 *
 * Any number of directives can be controlled with the object
 * passed in the "directive-if" attribute on this element:
 *
 *    {'attributeName': 'expression'[, 'attribute': 'expression']}
 * 
 * If `expression` evaluates to `false` then `attributeName`
 * will be removed from this element.
 *
 * Usage:
 *
 *         <any directive-if="{'myDirective': 'expression'}"
 *                    my-directive>
 *         </any>
 *
 */
directive('directiveIf', ['$compile', function($compile) {

    // Error handling.
    var compileGuard = 0;
    // End of error handling.

    return {

        // Set a high priority so we run before other directives.
        priority: 100,
        // Set terminal to true to stop other directives from running.
        terminal: true,

        compile: function() {
            return {
                pre: function(scope, element, attr) {

                    // Error handling.
                    // 
                    // Make sure we don't go into an infinite 
                    // compile loop if something goes wrong.
                    compileGuard++;
                    if (compileGuard >= 10) {
                        console.log('directiveIf: infinite compile loop!');
                        return;
                    }
                    // End of error handling.


                    // Get the set of directives to apply.
                    var directives = scope.$eval(attr.directiveIf);
                    angular.forEach(directives, function(expr, directive) {
                        // Evaluate each directive expression and remove
                        // the directive attribute if the expression evaluates
                        // to `false`.
                        var result = scope.$eval(expr);
                        if (result === false) {
                            // Set the attribute to `null` to remove the attribute.
                            // 
                            // See: https://docs.angularjs.org/api/ng/type/$compile.directive.Attributes#$set
                            attr.$set('logger', null)
                        }
                    });


                    // Remove our own directive before compiling
                    // to avoid infinite compile loops.
                    attr.$set('directiveIf', null);

                    // Recompile the element so the remaining directives
                    // can be invoked.
                    var result = $compile(element)(scope);


                    // Error handling.
                    // 
                    // Reset the compileGuard after compilation
                    // (otherwise we can't use this directive multiple times).
                    // 
                    // It should be safe to reset here because we will
                    // only reach this code *after* the `$compile()`
                    // call above has returned.
                    compileGuard = 0;

                }
            };

        }
    };
}]);
like image 170
Sly_cardinal Avatar answered Oct 15 '22 22:10

Sly_cardinal


@Sly_cardinal is right, used his code, but had to make a few adjustments:

(function () {

angular.module('MyModule').directive('directiveIf', function ($compile) {

    // Error handling.
    var compileGuard = 0;
    // End of error handling.

    return {

        // Set a high priority so we run before other directives.
        priority: 100,
        // Set terminal to true to stop other directives from running.
        terminal: true,

        compile: function() {
            return {
                pre: function(scope, element, attr) {

                    // Error handling.
                    // Make sure we don't go into an infinite
                    // compile loop if something goes wrong.
                    compileGuard++;
                    if (compileGuard >= 10) {
                        console.log('directiveIf: infinite compile loop!');
                        return;
                    }


                    // Get the set of directives to apply.
                    var directives = scope.$eval(attr.directiveIf);

                    for (var key in directives) {
                        if (directives.hasOwnProperty(key)) {

                            // if the direcitve expression is truthy
                            if (directives[key]) {
                                attr.$set(key, true);
                            } else {
                                attr.$set(key, null);
                            }
                        }
                    }

                    // Remove our own directive before compiling
                    // to avoid infinite compile loops.
                    attr.$set('directiveIf', null);

                    // Recompile the element so the remaining directives
                    // can be invoked.
                    var result = $compile(element)(scope);


                    // Error handling.
                    //
                    // Reset the compileGuard after compilation
                    // (otherwise we can't use this directive multiple times).
                    //
                    // It should be safe to reset here because we will
                    // only reach this code *after* the `$compile()`
                    // call above has returned.
                    compileGuard = 0;

                }
            };

        }
    };
});

})();
like image 39
John David Five Avatar answered Oct 15 '22 22:10

John David Five