fix($compile): prevent double attr interpolation w/ templateUrl

This fixes the issue that caused two attr interpolation observers
to be registered for the same attribute as a result of isolate
scope definition with attr (@) property for this attribute.

Duplicate observers would then fight with each other updating the
model.

The issue occured only when this directive was used in a repeater
because that's when we clone the template node which caused the
two observers to point to two different sets of $attr instances.

Closes #1166, #836
This commit is contained in:
Igor Minar 2012-10-25 00:33:36 -07:00
parent 45a8db9c08
commit 4dbd8452eb
2 changed files with 34 additions and 1 deletions

View file

@ -899,7 +899,7 @@ function $CompileProvider($provide) {
origAsyncDirective = directives.shift(),
// The fact that we have to copy and patch the directive seems wrong!
derivedSyncDirective = extend({}, origAsyncDirective, {
controller: null, templateUrl: null, transclude: null
controller: null, templateUrl: null, transclude: null, scope: null
});
$compileNode.html('');

View file

@ -1048,6 +1048,39 @@ describe('$compile', function() {
expect($exceptionHandler.errors).toEqual([]);
});
});
it('should resume delayed compilation without duplicates when in a repeater', function() {
// this is a test for a regression
// scope creation, isolate watcher setup, controller instantiation, etc should happen
// only once even if we are dealing with delayed compilation of a node due to templateUrl
// and the template node is in a repeater
var controllerSpy = jasmine.createSpy('controller');
module(function($compileProvider) {
$compileProvider.directive('delayed', valueFn({
controller: controllerSpy,
templateUrl: 'delayed.html',
scope: {
title: '@'
}
}));
});
inject(function($templateCache, $compile, $rootScope) {
$rootScope.coolTitle = 'boom!';
$templateCache.put('delayed.html', '<div>{{title}}</div>');
element = $compile(
'<div><div ng-repeat="i in [1,2]"><div delayed title="{{coolTitle + i}}"></div>|</div></div>'
)($rootScope);
$rootScope.$apply();
expect(controllerSpy.callCount).toBe(2);
expect(element.text()).toBe('boom!1|boom!2|');
});
});
});