Skip to content

Commit 438348a

Browse files
committed
feat($compile): add allOrNothing attr interpolation
1 parent 6bc2fac commit 438348a

File tree

2 files changed

+44
-2
lines changed

2 files changed

+44
-2
lines changed

src/ng/compile.js

+21-2
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
641641
var Attributes = function(element, attr) {
642642
this.$$element = element;
643643
this.$attr = attr || {};
644+
this.$$allOrNothingAttr = {};
644645
};
645646

646647
Attributes.prototype = {
@@ -768,6 +769,22 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
768769
});
769770
},
770771

772+
/**
773+
* @ngdoc method
774+
* @name $compile.directive.Attributes#$setAllOrNothing
775+
* @function
776+
*
777+
* @description
778+
* Changes the behavior of an interpolated attribute to use "all or nothing" mode.
779+
*
780+
* This means that the attribute's interpolated value is `undefined` unless all
781+
* interpolated expressions are defined.
782+
*
783+
* @param {string} key Normalized key. (ie ngAttribute) .
784+
*/
785+
$setAllOrNothing: function(key) {
786+
this.$$allOrNothingAttr[key] = true;
787+
},
771788

772789
/**
773790
* @ngdoc method
@@ -1865,11 +1882,13 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
18651882
// initialize attr object so that it's ready in case we need the value for isolate
18661883
// scope initialization, otherwise the value would not be available from isolate
18671884
// directive's linking fn during linking phase
1868-
attr[name] = interpolateFn(scope);
1885+
attr[name] = interpolateFn(scope, attr.$$allOrNothingAttr[name]);
18691886

18701887
($$observers[name] || ($$observers[name] = [])).$$inter = true;
18711888
(attr.$$observers && attr.$$observers[name].$$scope || scope).
1872-
$watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
1889+
$watch(function (scope) {
1890+
return interpolateFn(scope, attr.$$allOrNothingAttr[name]);
1891+
}, function interpolateFnWatchAction(newValue, oldValue) {
18731892
//special case for class attribute addition + removal
18741893
//so that class changes can tap into the animation
18751894
//hooks provided by the $animate service. Be sure to

test/ng/compileSpec.js

+23
Original file line numberDiff line numberDiff line change
@@ -2074,6 +2074,29 @@ describe('$compile', function() {
20742074
expect(observeSpy).toHaveBeenCalledOnceWith('bound-value');
20752075
}));
20762076

2077+
it('should observe interpolated attrs in allOrNothing mode', inject(function($rootScope, $compile) {
2078+
directive('someAttr', function() {
2079+
return function(scope, elm, attr) {
2080+
attr.$setAllOrNothing('someAttr');
2081+
}
2082+
});
2083+
2084+
$compile('<div some-attr="{{first}} {{second}}" observer></div>')($rootScope);
2085+
2086+
// should be async
2087+
expect(observeSpy).not.toHaveBeenCalled();
2088+
2089+
$rootScope.$apply(function() {
2090+
$rootScope.first = 'A';
2091+
});
2092+
expect(observeSpy).toHaveBeenCalledOnceWith(undefined);
2093+
2094+
$rootScope.$apply(function() {
2095+
$rootScope.second = 'B';
2096+
});
2097+
expect(observeSpy).toHaveBeenCalledWith('A B');
2098+
}));
2099+
20772100

20782101
it('should return a deregistration function while observing an attribute', inject(function($rootScope, $compile) {
20792102
$compile('<div some-attr="{{value}}" observer></div>')($rootScope);

0 commit comments

Comments
 (0)