Why can't we use an interpolation expression with "ng-model", but can use it with "ng-src"?
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.
source to share
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.
source to share
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/
source to share