mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-05-20 04:11:51 +00:00
refactor($compile): always call attr.$observe
attr.$observe used to call function only if there was interpolation on that attribute. We now call the observation function all the time but we only save the reference to it if interpolation is present.
This commit is contained in:
parent
2491319575
commit
9be82d942f
4 changed files with 39 additions and 44 deletions
|
|
@ -221,9 +221,9 @@ function $CompileProvider($provide) {
|
||||||
|
|
||||||
this.$get = [
|
this.$get = [
|
||||||
'$injector', '$interpolate', '$exceptionHandler', '$http', '$templateCache', '$parse',
|
'$injector', '$interpolate', '$exceptionHandler', '$http', '$templateCache', '$parse',
|
||||||
'$controller',
|
'$controller', '$rootScope',
|
||||||
function($injector, $interpolate, $exceptionHandler, $http, $templateCache, $parse,
|
function($injector, $interpolate, $exceptionHandler, $http, $templateCache, $parse,
|
||||||
$controller) {
|
$controller, $rootScope) {
|
||||||
|
|
||||||
var LOCAL_MODE = {
|
var LOCAL_MODE = {
|
||||||
attribute: function(localName, mode, parentScope, scope, attr) {
|
attribute: function(localName, mode, parentScope, scope, attr) {
|
||||||
|
|
@ -268,7 +268,6 @@ function $CompileProvider($provide) {
|
||||||
|
|
||||||
var Attributes = function(element, attr) {
|
var Attributes = function(element, attr) {
|
||||||
this.$$element = element;
|
this.$$element = element;
|
||||||
this.$$observers = {};
|
|
||||||
this.$attr = attr || {};
|
this.$attr = attr || {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -286,7 +285,8 @@ function $CompileProvider($provide) {
|
||||||
* @param {string=} attrName Optional none normalized name. Defaults to key.
|
* @param {string=} attrName Optional none normalized name. Defaults to key.
|
||||||
*/
|
*/
|
||||||
$set: function(key, value, writeAttr, attrName) {
|
$set: function(key, value, writeAttr, attrName) {
|
||||||
var booleanKey = getBooleanAttrName(this.$$element[0], key);
|
var booleanKey = getBooleanAttrName(this.$$element[0], key),
|
||||||
|
$$observers = this.$$observers;
|
||||||
|
|
||||||
if (booleanKey) {
|
if (booleanKey) {
|
||||||
this.$$element.prop(key, value);
|
this.$$element.prop(key, value);
|
||||||
|
|
@ -314,7 +314,7 @@ function $CompileProvider($provide) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// fire observers
|
// fire observers
|
||||||
forEach(this.$$observers[key], function(fn) {
|
$$observers && forEach($$observers[key], function(fn) {
|
||||||
try {
|
try {
|
||||||
fn(value);
|
fn(value);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -333,10 +333,17 @@ function $CompileProvider($provide) {
|
||||||
* @returns {function(*)} the `fn` Function passed in.
|
* @returns {function(*)} the `fn` Function passed in.
|
||||||
*/
|
*/
|
||||||
$observe: function(key, fn) {
|
$observe: function(key, fn) {
|
||||||
// keep only observers for interpolated attrs
|
var attrs = this,
|
||||||
if (this.$$observers[key]) {
|
$$observers = (attrs.$$observers || (attrs.$$observers = {})),
|
||||||
this.$$observers[key].push(fn);
|
listeners = ($$observers[key] || ($$observers[key] = []));
|
||||||
}
|
|
||||||
|
listeners.push(fn);
|
||||||
|
$rootScope.$evalAsync(function() {
|
||||||
|
if (!listeners.$$inter) {
|
||||||
|
// no one registered attribute interpolation function, so lets call it manually
|
||||||
|
fn(attrs[key]);
|
||||||
|
}
|
||||||
|
});
|
||||||
return fn;
|
return fn;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -990,16 +997,16 @@ function $CompileProvider($provide) {
|
||||||
directives.push({
|
directives.push({
|
||||||
priority: 100,
|
priority: 100,
|
||||||
compile: valueFn(function(scope, element, attr) {
|
compile: valueFn(function(scope, element, attr) {
|
||||||
|
var $$observers = (attr.$$observers || (attr.$$observers = {}));
|
||||||
|
|
||||||
if (name === 'class') {
|
if (name === 'class') {
|
||||||
// we need to interpolate classes again, in the case the element was replaced
|
// we need to interpolate classes again, in the case the element was replaced
|
||||||
// and therefore the two class attrs got merged - we want to interpolate the result
|
// and therefore the two class attrs got merged - we want to interpolate the result
|
||||||
interpolateFn = $interpolate(attr[name], true);
|
interpolateFn = $interpolate(attr[name], true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// we define observers array only for interpolated attrs
|
|
||||||
// and ignore observers for non interpolated attrs to save some memory
|
|
||||||
attr.$$observers[name] = [];
|
|
||||||
attr[name] = undefined;
|
attr[name] = undefined;
|
||||||
|
($$observers[name] || ($$observers[name] = [])).$$inter = true;
|
||||||
scope.$watch(interpolateFn, function(value) {
|
scope.$watch(interpolateFn, function(value) {
|
||||||
attr.$set(name, value);
|
attr.$set(name, value);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -284,7 +284,6 @@ forEach(BOOLEAN_ATTR, function(propName, attrName) {
|
||||||
priority: 100,
|
priority: 100,
|
||||||
compile: function() {
|
compile: function() {
|
||||||
return function(scope, element, attr) {
|
return function(scope, element, attr) {
|
||||||
attr.$$observers[attrName] = [];
|
|
||||||
scope.$watch(attr[normalized], function(value) {
|
scope.$watch(attr[normalized], function(value) {
|
||||||
attr.$set(attrName, !!value);
|
attr.$set(attrName, !!value);
|
||||||
});
|
});
|
||||||
|
|
@ -301,27 +300,15 @@ forEach(['src', 'href'], function(attrName) {
|
||||||
ngAttributeAliasDirectives[normalized] = function() {
|
ngAttributeAliasDirectives[normalized] = function() {
|
||||||
return {
|
return {
|
||||||
priority: 99, // it needs to run after the attributes are interpolated
|
priority: 99, // it needs to run after the attributes are interpolated
|
||||||
compile: function() {
|
link: function(scope, element, attr) {
|
||||||
return function(scope, element, attr) {
|
attr.$observe(normalized, function(value) {
|
||||||
var value = attr[normalized];
|
attr.$set(attrName, value);
|
||||||
if (value == undefined) {
|
|
||||||
// undefined value means that the directive is being interpolated
|
|
||||||
// so just register observer
|
|
||||||
attr.$$observers[attrName] = [];
|
|
||||||
attr.$observe(normalized, function(value) {
|
|
||||||
attr.$set(attrName, value);
|
|
||||||
|
|
||||||
// on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
|
// on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
|
||||||
// then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
|
// then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
|
||||||
// to set the property as well to achieve the desired effect
|
// to set the property as well to achieve the desired effect
|
||||||
if (msie) element.prop(attrName, value);
|
if (msie) element.prop(attrName, value);
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
// value present means that no interpolation, so copy to native attribute.
|
|
||||||
attr.$set(attrName, value);
|
|
||||||
element.prop(attrName, value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1239,7 +1239,6 @@ var ngValueDirective = function() {
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return function(scope, elm, attr) {
|
return function(scope, elm, attr) {
|
||||||
attr.$$observers.value = [];
|
|
||||||
scope.$watch(attr.ngValue, function(value) {
|
scope.$watch(attr.ngValue, function(value) {
|
||||||
attr.$set('value', value, false);
|
attr.$set('value', value, false);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1249,15 +1249,15 @@ describe('$compile', function() {
|
||||||
|
|
||||||
|
|
||||||
describe('interpolation', function() {
|
describe('interpolation', function() {
|
||||||
var observeSpy, attrValueDuringLinking;
|
var observeSpy, directiveAttrs;
|
||||||
|
|
||||||
beforeEach(module(function() {
|
beforeEach(module(function() {
|
||||||
directive('observer', function() {
|
directive('observer', function() {
|
||||||
return function(scope, elm, attr) {
|
return function(scope, elm, attr) {
|
||||||
|
directiveAttrs = attr;
|
||||||
observeSpy = jasmine.createSpy('$observe attr');
|
observeSpy = jasmine.createSpy('$observe attr');
|
||||||
|
|
||||||
expect(attr.$observe('someAttr', observeSpy)).toBe(observeSpy);
|
expect(attr.$observe('someAttr', observeSpy)).toBe(observeSpy);
|
||||||
attrValueDuringLinking = attr.someAttr;
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
@ -1295,19 +1295,21 @@ describe('$compile', function() {
|
||||||
|
|
||||||
|
|
||||||
it('should set interpolated attrs to undefined', inject(function($rootScope, $compile) {
|
it('should set interpolated attrs to undefined', inject(function($rootScope, $compile) {
|
||||||
attrValueDuringLinking = null;
|
|
||||||
$compile('<div some-attr="{{whatever}}" observer></div>')($rootScope);
|
$compile('<div some-attr="{{whatever}}" observer></div>')($rootScope);
|
||||||
expect(attrValueDuringLinking).toBeUndefined();
|
expect(directiveAttrs.someAttr).toBeUndefined();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
it('should not call observer of non-interpolated attr', inject(function($rootScope, $compile) {
|
it('should call observer of non-interpolated attr through $evalAsync',
|
||||||
$compile('<div some-attr="nonBound" observer></div>')($rootScope);
|
inject(function($rootScope, $compile) {
|
||||||
expect(attrValueDuringLinking).toBe('nonBound');
|
$compile('<div some-attr="nonBound" observer></div>')($rootScope);
|
||||||
|
expect(directiveAttrs.someAttr).toBe('nonBound');
|
||||||
|
|
||||||
$rootScope.$digest();
|
expect(observeSpy).not.toHaveBeenCalled();
|
||||||
expect(observeSpy).not.toHaveBeenCalled();
|
$rootScope.$digest();
|
||||||
}));
|
expect(observeSpy).toHaveBeenCalled();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
it('should delegate exceptions to $exceptionHandler', function() {
|
it('should delegate exceptions to $exceptionHandler', function() {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue