12

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

0

3 Answers 3

14

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

Sign up to request clarification or add additional context in comments.

9 Comments

Amazing thanks! Just what I needed. Some crazy ass logic there though! :)
@Vikas Singhal: I don't think that it's a good idea to choose a template based on scope's attribute. It should be a normal attribute for the directive. scope's attributes (model) should only be used by template (view) though binding
I understand that point, but I have a situation where I need to show a checkbox, inputbox, textarea or select based on a filter value. Feel free to suggest an alternative here.
@KhanhTO suppose form is dynamically created using JSON... that JSON would become scope model and would pass data to directive via attributes. Therefore I don't understand your concerns
@charlietfl: for example: the directive is a view to display a list of items with 2 modes (list view and tree view). We should configure the view by a property in the html directly by specifying this directive should display a list or a tree, the model should not contain a property to indicate the view for it. That's what I mean in my answer
|
9

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

Comments

1

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.

1 Comment

Again, does not work : plnkr.co/edit/pqmb5XWyCeXF44C85KZX?p=preview. Can you help me with the code itself and tell what exactly is right way?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.