Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

fix($compile): Cleanup attribute-binding observers #15298

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/ng/compile.js
Original file line number Diff line number Diff line change
Expand Up @@ -3434,7 +3434,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
if (!optional && !hasOwnProperty.call(attrs, attrName)) {
destination[scopeName] = attrs[attrName] = undefined;
}
attrs.$observe(attrName, function(value) {
removeWatch = attrs.$observe(attrName, function(value) {
if (isString(value) || isBoolean(value)) {
var oldValue = destination[scopeName];
recordChanges(scopeName, value, oldValue);
Expand All @@ -3453,6 +3453,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
destination[scopeName] = lastValue;
}
initialChanges[scopeName] = new SimpleChange(_UNINITIALIZED_VALUE, destination[scopeName]);
removeWatchCollection.push(removeWatch);
break;

case '=':
Expand Down
37 changes: 37 additions & 0 deletions test/ng/compileSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4378,6 +4378,43 @@ describe('$compile', function() {
});
});

it('should reinitialize watchers on attribute change', function() {
var constructorSpy = jasmine.createSpy('constructor');

function TestDirective() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Rename to TestController.

this.constructorProp = true;
this.constructorSpy = constructorSpy;
}

TestDirective.prototype.$onChanges = function(changes) {
this.constructorSpy(this.constructorProp);
};

module(function($compileProvider) {
$compileProvider.component('test', {
bindings: {prop: '<', attr: '@'},
controller: TestDirective
});
});

inject(function($compile, $rootScope) {
var template = '<test prop="a" attr="{{b}}"></test>';
$rootScope.a = 'foo';
$rootScope.b = NaN;

element = $compile(template)($rootScope);
$rootScope.$digest();
expect(constructorSpy).toHaveBeenCalledWith(true);

constructorSpy.calls.reset();
$rootScope.$apply('a = "bar"');
expect(constructorSpy).toHaveBeenCalledWith(true);

constructorSpy.calls.reset();
$rootScope.$apply('b = 42');
expect(constructorSpy).toHaveBeenCalledWith(true);
});
});

it('should not call `$onChanges` twice even when the initial value is `NaN`', function() {
var onChangesSpy = jasmine.createSpy('$onChanges');
Expand Down