Why can't we use an interpolation expression with "ng-model", but can use it with "ng-src"?

I'm trying to use an interpolation expression with a directive ng-model

, but it doesn't work. In contrast, when I use interpolation with ng-src

, it works fine. What is this difference due to?

+3


source to share


3 answers


It all depends on how the directive was configured.

Some directives like ng-model

, ng-show

and ng-click

do not use interpolation characters, while directives ng-src

accept interpolation.

Interpolation is supported in directives that only work with strings. If we look at the implementation of ng-src you will find

 attr.$observe(normalized, function(value) {
          if (!value)
             return;

          attr.$set(attrName, value);

          if (msie) element.prop(attrName, attr[attrName]);
        });

      



attr.$observe

watch to change the attribute, not the model. Changes to the model cause changes to the attributes (due to interpolation), resulting in the trigger being fired.

For all other directives, such as ng-model

, the attribute value is an expression that evaluates to the current scope and is not limited to a string value.

If you are developing your own directives, isolated scope properties =

and @

help you achieve something like this.

+3


source


This is due to when the code in the directive looks at the: attribute before or after interpolation.

I suspect I am ngModel

using this attribute, and I mean passing it to $parse

or $eval

, since this is the interval before .

However, the directive ngSrc

uses this attribute, and I mean setting the attribute src

or not the attribute value, after interpolation.

The reason why it ngModel

uses the value before interpolation but ngSrc

uses it after, I suspect it has to do with how they are encoded. Consider the directive

<my-directive my-attr="{{ 'name' }}"></my-directive>

      

coded as:

app.directive('myDirective', function() {
  return {
    restrict: 'E',
    link: function($scope, $element, $attrs) {
      console.log('Link:', $attrs.myAttr);       // Outputs "name"
    },
    controller: function($scope, $element, $attrs) {
      console.log('Controller:', $attrs.myAttr); // Outputs "{{ 'name' }}"
    }
  };
});

      

The non-interpolated value is displayed in the controller, but the post-interpolated value is displayed in the link function. Thus, the order of the (relevant) events

  • controller
  • then interpolation,
  • then linking the function.


You can see this example in Plunker . I suspect it is ngModel

using the value from the controller and therefore seeing the pre-interpolated value, but ngSrc

using the bind function and therefore seeing the post-interpolated value.

One of the reasons why it is ngModel

using a controller and not a bind function, I suspect it might expose the controller to other directives that might use it via an option require

.


A bit of a confusing complication is that it ngSrc

doesn't expect the attribute to be a valid Angular expression. Instead of passing it through $parse

or $eval

, it just uses the post-interpolated attribute value directly. (i.e. you don't need to enclose the URL in quotes).

If you want, you can write a directive that combines interpolation with Angular expressions. From the bind function, you can pass an attribute $parse

or $eval

. If the post-interpolated value is a valid Angular expression, this will work. You can do the same from a controller, but first you need to pass the value through $interpolate

.

As you found it ngModel

doesn't support this. However, I don't understand why you couldn't write a basic version ngModel

that supports both, and therefore can be used like

my-model="myScope{{ 'VariableName' }}"

      

which refers to the scope variable myScopeVariableName

.

To bring this question back again, why does ngModel

not in itself support this, I suspect because the usage restrictions are limited (the OP did not mention this in the question) and would therefore add complexity to the directive for no good reason.

0


source


This is due to the one-way two-way data binding provided by the sandboxed scope directive. See the "Isolated scopes" section in the following post:

http://www.undefinednull.com/2014/02/11/mastering-the-scope-of-a-directive-in-angularjs/

-1


source







All Articles