aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuis Ramón López2013-01-26 20:15:06 +0100
committerIgor Minar2013-02-25 14:30:54 -0800
commitac899d0da59157fa1c6429510791b6c3103d9401 (patch)
tree1d6dd8e5e0b394000c14d691d21480025d138318
parent30162b769cccb1965a4ad85a4eedd805d3764853 (diff)
downloadangular.js-ac899d0da59157fa1c6429510791b6c3103d9401.tar.bz2
feat($compile): '=?' makes '=' binding optional
If you bind using '=' to a non-existant parent property, the compiler will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception, which is right because the model doesn't exist. This enhancement allow to specify that a binding is optional so it won't complain if the parent property is not defined. In order to mantain backward compability, the new behaviour must be specified using '=?' instead of '='. The local property will be undefined is these cases. Closes #909 Closes #1435
-rw-r--r--docs/content/guide/directive.ngdoc4
-rw-r--r--src/ng/compile.js8
-rw-r--r--test/ng/compileSpec.js30
3 files changed, 39 insertions, 3 deletions
diff --git a/docs/content/guide/directive.ngdoc b/docs/content/guide/directive.ngdoc
index 2f93ef33..925268bb 100644
--- a/docs/content/guide/directive.ngdoc
+++ b/docs/content/guide/directive.ngdoc
@@ -336,7 +336,9 @@ compiler}. The attributes are:
Given `<widget my-attr="parentModel">` and widget definition of
`scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the
value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
- in `localModel` and any changes in `localModel` will reflect in `parentModel`.
+ in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent
+ scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You
+ can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional.
* `&` or `&attr` - provides a way to execute an expression in the context of the parent scope.
If no `attr` name is specified then the attribute name is assumed to be the same as the
diff --git a/src/ng/compile.js b/src/ng/compile.js
index 5508605e..f39b0935 100644
--- a/src/ng/compile.js
+++ b/src/ng/compile.js
@@ -768,13 +768,14 @@ function $CompileProvider($provide) {
$element = attrs.$$element;
if (newIsolateScopeDirective) {
- var LOCAL_REGEXP = /^\s*([@=&])\s*(\w*)\s*$/;
+ var LOCAL_REGEXP = /^\s*([@=&])(\??)\s*(\w*)\s*$/;
var parentScope = scope.$parent || scope;
forEach(newIsolateScopeDirective.scope, function(definiton, scopeName) {
var match = definiton.match(LOCAL_REGEXP) || [],
- attrName = match[2]|| scopeName,
+ attrName = match[3] || scopeName,
+ optional = (match[2] == '?'),
mode = match[1], // @, =, or &
lastValue,
parentGet, parentSet;
@@ -796,6 +797,9 @@ function $CompileProvider($provide) {
}
case '=': {
+ if (optional && !attrs[attrName]) {
+ return;
+ }
parentGet = $parse(attrs[attrName]);
parentSet = parentGet.assign || function() {
// reset the change, or we will throw this exception on every $digest
diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js
index 9d0acc22..ae1eb000 100644
--- a/test/ng/compileSpec.js
+++ b/test/ng/compileSpec.js
@@ -1857,6 +1857,9 @@ describe('$compile', function() {
ref: '=',
refAlias: '= ref',
reference: '=',
+ optref: '=?',
+ optrefAlias: '=? optref',
+ optreference: '=?',
expr: '&',
exprAlias: '&expr'
},
@@ -1993,6 +1996,33 @@ describe('$compile', function() {
});
+ describe('optional object reference', function() {
+ it('should update local when origin changes', inject(function() {
+ compile('<div><span my-component optref="name">');
+ expect(componentScope.optRef).toBe(undefined);
+ expect(componentScope.optRefAlias).toBe(componentScope.optRef);
+
+ $rootScope.name = 'misko';
+ $rootScope.$apply();
+ expect(componentScope.optref).toBe($rootScope.name);
+ expect(componentScope.optrefAlias).toBe($rootScope.name);
+
+ $rootScope.name = {};
+ $rootScope.$apply();
+ expect(componentScope.optref).toBe($rootScope.name);
+ expect(componentScope.optrefAlias).toBe($rootScope.name);
+ }));
+
+ it('should not throw exception when reference does not exist', inject(function() {
+ compile('<div><span my-component>');
+
+ expect(componentScope.optref).toBe(undefined);
+ expect(componentScope.optrefAlias).toBe(undefined);
+ expect(componentScope.optreference).toBe(undefined);
+ }));
+ });
+
+
describe('executable expression', function() {
it('should allow expression execution with locals', inject(function() {
compile('<div><span my-component expr="count = count + offset">');