Passing value of a variable to angularjs directive template function

All we need is an easy explanation of the problem, so here it is.

I am trying to pass a $scope’s variable to a directive, but its not working. I am catching the variable in the template function:

app.directive('customdir', function () {

    return {
        restrict: 'E',

        template: function(element, attrs) {
            console.log(attrs.filterby);
            switch (attrs.filterby) {
                case 'World':
                    return '<input type="checkbox">';
            }
            return '<input type="text" />';
        }
    };
});

What I need is the value of variable filterby not the variable name itself.

Plunkr Demo

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

Or like this

app.directive('customdir', function ($compile) {
  var getTemplate = function(filter) {
    switch (filter) {
      case 'World': return '<input type="checkbox" ng-model="filterby">';
      default:  return '<input type="text" ng-model="filterby" />';
    }
  }

    return {
        restrict: 'E',
        scope: {
          filterby: "="
        },
        link: function(scope, element, attrs) {
            var el = $compile(getTemplate(scope.filterby))(scope);
            element.replaceWith(el);
        }
    };
});

http://plnkr.co/edit/yPopi0mYdViElCKrQAq9?p=preview

Method 2

Template should not contain logic because template is view. Template should only contain binding directives to make the view updated based on changes of the scope (model). Something like this:

app.directive('customdir', function ($compile) {

    return {
        restrict: 'E',

        scope:{
          filterby:"="
        },

        link:function (scope, element) {
          scope.$watch("filterby",function(newValue){ //logic is out of template
              if (newValue == "World"){
                scope.showCheckBox = true;
              }
              else {
                scope.showCheckBox = false;
              }
          });
        },

        template: function(element, attrs) {
         //View should be passive and only listens to changes of model to update it accordingly.
            return '<input type="checkbox" ng-show="showCheckBox" / ><input type="text" ng-show="!showCheckBox"  />'; 
        }
    };
});

With this approach, you could even change the input at runtime and the view is updated to reflect the changes.

DEMO

If you want to make a decision on which template to use based on some configuration, you should configure it via a normal property, should not access though scope’s propery. Simply like this:

app.directive('customdir', function ($compile) {

    return {
        restrict: 'E',
        scope: {
            filterby:"=" //filterby is a separate field used for data binding
        },

        template: function(element, attrs) {
            switch (attrs.type) { //view selection configuration should be obtained from the element
                case 'checkbox':
                    return '<input type="checkbox">';
            }
            return '<input type="text" />';
        }
    };
});

Configure it by passing a normal value:

<customdir type="checkbox" filterby="name"></customdir>

DEMO

Method 3

First of all, what is a template function? It should be a link function. Second, you’re overloading the link function incorrectly, order matters here, its always scope, element, attrs .Third, pass the variable in an isolate scope:

app.directive('customdir', function ($compile) {

    return {
        restrict: 'E',
        scope:{
          filterby:'='
        },

        link: function(scope,element, attrs) {
            console.log(scope.filterby);
            switch (scope.filterby) {
                case 'World':
                    return '<input type="checkbox">';
            }
            return '<input type="text" />';
        }
    };
});

or if you insist on attributes then:

 app.directive('customdir', function ($compile) {

        return {
            restrict: 'E',

            link: function(scope,element, attrs) {
                console.log(attrs.filterby);
                switch (attrs.filterby) {
                    case 'World':
                        return '<input type="checkbox">';
                }
                return '<input type="text" />';
            }
        };
    });

but in your html:

 <customdir filterby="{{name}}"></customdir>

To ensure the variables value gets evaluated first. Finally you should not be manipulating the DOM like that, in fact that link function won’t render html as you’d expect. You have a static template and your link function will act as something to set variable values on the template.

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply