how to pass either a model or a string to a directive in angularjs?

爷,独闯天下 提交于 2020-01-11 11:18:15

问题


I'm working on a directive, where one of the parameters to it can either be a model (dynamic value) or sometimes a string.

The only way I could do this was using @. Is there a better way to do this?

<my-directive foo="{{foomodel}}">
<my-directive foo="foostring">

In my directive I'm using an isolate scope:

scope: {
    foo: '@'
}

I tried doing this with just strings and it did not work.


回答1:


Reading these answers it seems there is much confusion about the differences between '=', '&' and '@' when using an isolated scope. For your purposes, I would use & and if you want the value to be a string or an object do as @pankajparkar does and encase strings in single quotes.

Unless you need changes in your directive to be reflected in your parent scope, don't use '='. '=' implicitly sets a watch on the isolated scope's property and modifies the parent scope property if it sees a change. Watches are a relatively expensive operation in Angular, and you want to minimize them.

& does not implicity create a watch and it defers evaluation of the expression for when you call the created scope function. It is ideal for your case because you don't need two-way binding. Think of & as creating a function that returns the value generated by the expression assigned to the attribute.

app.directive("foo", function(){
  return {
    scope: {
      foo: "&"
    },
    template: "<span>{{foo()}}</span> | <span>{{typeof foo()}}</span>",
    link: function(scope){
        //so we don't have to set up a watch, I've just inlined the evaluation of the type
    }
  }
})

<div foo="model"></div>       <!-- test | string -->
<div foo="'text'"></div>      <!-- text | string -->
<div foo="5"></div>           <!-- 5 | number -->
<div foo="{a: 5}"></div>

@ is reserved for cases where you want to restrict evaluation of the expression to interpolation. Interpolation in angular ALWAYS results in a string. If you use '@' and your attribute is not an interpolation expression ({{}}), the value of the scope property will be the literal value of the attribute. If your attribute value is an interpolation expression, the value of the property will be the interpolated value of the expression (which will always be a string).

Clear as mud, right?




回答2:


Whenever you have string value then you should wrap it inside ' single quote, because @ means expression evaluation using $parse API of angular

HTML

<my-directive foo="'foostring'">

Hope this could help you, Thanks.




回答3:


If you are using isolate scope, then you could use the two-way binding: "=". This would take a model or a literal, in the following way (assuming $scope.model = "test":

<div foo="model"></div>       <!-- test | string -->
<div foo="'text'"></div>      <!-- text | string -->
<div foo="5"></div>           <!-- 5 | number -->
<div foo="{a: 5}"></div>      <!-- {"a":4} | object -->

and this is the directive definition:

app.directive("foo", function(){
  return {
    scope: {
      foo: "="
    },
    template: "<span>{{foo}}</span> | <span>{{type}}</span>",
    link: function(scope){
      scope.type = typeof scope.foo;
    }
  }
})

plunker

EDIT:

As per feedback from @JoeEnzminger, the "=" is an expensive way to get the actual object as it sets up 2 watchers (for a two-way binding).

For one-way binding (with a single watcher)

You could the foo: "@" binding. This will result in all of the values being interpreted as a string. Specifically, to pass the value of $scope.model you would need to use an expression: {{model}}.

Alternatively, as suggested by @JoeEnzminger's answer below, you could use the foo: "&" binding. This will allow to get the actual model/object back. The drawback is a more awkward function syntax {{foo()}} for expressions.

For one-time binding:

If you could do away with one-time binding, then you could use the $parse service to get the actual object.

For isolate scope:

scope: {}, 
link: function(scope, element, attrs){
  scope.foo = $parse(attrs.foo)(scope.$parent);
}

For inherited scope:

scope: true,
link: function(scope, element, attrs){
  scope.foo = $parse(attrs.foo)(scope);
}


来源:https://stackoverflow.com/questions/28730684/how-to-pass-either-a-model-or-a-string-to-a-directive-in-angularjs

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!