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

Commit

Permalink
feat(jqLite): expose isolateScope() getter similar to scope()
Browse files Browse the repository at this point in the history
See doc update in the diff for more info.

BREAKING CHANGE: jqLite#scope() does not return the isolate scope on the element
that triggered directive with isolate scope. Use jqLite#isolateScope() instead.
  • Loading branch information
IgorMinar committed Nov 8, 2013
1 parent b5af198 commit 27e9340
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 6 deletions.
1 change: 1 addition & 0 deletions src/Angular.js
Original file line number Diff line number Diff line change
Expand Up @@ -1244,6 +1244,7 @@ function bindJQuery() {
jqLite = jQuery;
extend(jQuery.fn, {
scope: JQLitePrototype.scope,
isolateScope: JQLitePrototype.isolateScope,
controller: JQLitePrototype.controller,
injector: JQLitePrototype.injector,
inheritedData: JQLitePrototype.inheritedData
Expand Down
17 changes: 15 additions & 2 deletions src/jqLite.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@
* - `injector()` - retrieves the injector of the current element or its parent.
* - `scope()` - retrieves the {@link api/ng.$rootScope.Scope scope} of the current
* element or its parent.
* - `isolateScope()` - retrieves an isolate {@link api/ng.$rootScope.Scope scope} if one is attached directly to the
* current element. This getter should be used only on elements that contain a directive which starts a new isolate
* scope. Calling `scope()` on this element always returns the original non-isolate scope.
* - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
* parent element is reached.
*
Expand Down Expand Up @@ -344,9 +347,13 @@ function jqLiteInheritedData(element, name, value) {
if(element[0].nodeType == 9) {
element = element.find('html');
}
var names = isArray(name) ? name : [name];

while (element.length) {
if ((value = element.data(name)) !== undefined) return value;

for (var i = 0, ii = names.length; i < ii; i++) {
if ((value = element.data(names[i])) !== undefined) return value;
}
element = element.parent();
}
}
Expand Down Expand Up @@ -418,7 +425,13 @@ forEach({
inheritedData: jqLiteInheritedData,

scope: function(element) {
return jqLiteInheritedData(element, '$scope');
// Can't use jqLiteData here directly so we stay compatible with jQuery!
return jqLite(element).data('$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
},

isolateScope: function(element) {
// Can't use jqLiteData here directly so we stay compatible with jQuery!
return jqLite(element).data('$isolateScope') || jqLite(element).data('$isolateScopeNoTemplate');
},

controller: jqLiteController ,
Expand Down
12 changes: 10 additions & 2 deletions src/ng/compile.js
Original file line number Diff line number Diff line change
Expand Up @@ -1368,7 +1368,15 @@ function $CompileProvider($provide) {
var $linkNode = jqLite(linkNode);

isolateScope = scope.$new(true);
$linkNode.data('$isolateScope', isolateScope);

if (templateDirective && (templateDirective === newIsolateScopeDirective.$$originalDirective)) {
$linkNode.data('$isolateScope', isolateScope) ;
} else {
$linkNode.data('$isolateScopeNoTemplate', isolateScope);
}



safeAddClass($linkNode, 'ng-isolate-scope');

forEach(newIsolateScopeDirective.scope, function(definition, scopeName) {
Expand Down Expand Up @@ -1600,7 +1608,7 @@ function $CompileProvider($provide) {
origAsyncDirective = directives.shift(),
// The fact that we have to copy and patch the directive seems wrong!
derivedSyncDirective = extend({}, origAsyncDirective, {
templateUrl: null, transclude: null, replace: null
templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
}),
templateUrl = (isFunction(origAsyncDirective.templateUrl))
? origAsyncDirective.templateUrl($compileNode, tAttrs)
Expand Down
34 changes: 34 additions & 0 deletions test/jqLiteSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,13 @@ describe('jqLite', function() {
dealoc(element);
});

it('should retrieve isolate scope attached to the current element', function() {
var element = jqLite('<i>foo</i>');
element.data('$isolateScope', scope);
expect(element.isolateScope()).toBe(scope);
dealoc(element);
});

it('should retrieve scope attached to the html element if its requested on the document',
function() {
var doc = jqLite(document),
Expand Down Expand Up @@ -182,6 +189,33 @@ describe('jqLite', function() {
});


describe('isolateScope', function() {

it('should retrieve isolate scope attached to the current element', function() {
var element = jqLite('<i>foo</i>');
element.data('$isolateScope', scope);
expect(element.isolateScope()).toBe(scope);
dealoc(element);
});


it('should not walk up the dom to find scope', function() {
var element = jqLite('<ul><li><p><b>deep deep</b><p></li></ul>');
var deepChild = jqLite(element[0].getElementsByTagName('b')[0]);
element.data('$isolateScope', scope);
expect(deepChild.isolateScope()).toBeUndefined();
dealoc(element);
});


it('should return undefined when no scope was found', function() {
var element = jqLite('<div></div>');
expect(element.isolateScope()).toBeFalsy();
dealoc(element);
});
});


describe('injector', function() {
it('should retrieve injector attached to the current element or its parent', function() {
var template = jqLite('<div><span></span></div>'),
Expand Down
120 changes: 118 additions & 2 deletions test/ng/compileSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1375,7 +1375,7 @@ describe('$compile', function() {
return function (scope, element) {
iscope = scope;
log(scope.$id);
expect(element.data('$isolateScope')).toBe(scope);
expect(element.data('$isolateScopeNoTemplate')).toBe(scope);
};
}
};
Expand Down Expand Up @@ -1522,7 +1522,7 @@ describe('$compile', function() {
);


it('should allow more one new scope directives per element, but directives should share' +
it('should allow more than one new scope directives per element, but directives should share' +
'the scope', inject(
function($rootScope, $compile, log) {
element = $compile('<div class="scope-a; scope-b"></div>')($rootScope);
Expand Down Expand Up @@ -1554,6 +1554,120 @@ describe('$compile', function() {
expect(log).toEqual('002');
})
);


describe('scope()/isolate() scope getters', function() {

describe('with no directives', function() {

it('should return the scope of the parent node', inject(
function($rootScope, $compile) {
element = $compile('<div></div>')($rootScope);
expect(element.scope()).toBe($rootScope);
})
);
});


describe('with new scope directives', function() {

it('should return the new scope at the directive element', inject(
function($rootScope, $compile) {
element = $compile('<div scope></div>')($rootScope);
expect(element.scope().$parent).toBe($rootScope);
})
);


it('should return the new scope for children in the original template', inject(
function($rootScope, $compile) {
element = $compile('<div scope><a></a></div>')($rootScope);
expect(element.find('a').scope().$parent).toBe($rootScope);
})
);


it('should return the new scope for children in the directive template', inject(
function($rootScope, $compile, $httpBackend) {
$httpBackend.expect('GET', 'tscope.html').respond('<a></a>');
element = $compile('<div tscope></div>')($rootScope);
$httpBackend.flush();
expect(element.find('a').scope().$parent).toBe($rootScope);
})
);
});


describe('with isolate scope directives', function() {

it('should return the root scope for directives at the root element', inject(
function($rootScope, $compile) {
element = $compile('<div iscope></div>')($rootScope);
expect(element.scope()).toBe($rootScope);
})
);


it('should return the non-isolate scope at the directive element', inject(
function($rootScope, $compile) {
var directiveElement;
element = $compile('<div><div iscope></div></div>')($rootScope);
directiveElement = element.children();
expect(directiveElement.scope()).toBe($rootScope);
expect(directiveElement.isolateScope().$parent).toBe($rootScope);
})
);


it('should return the isolate scope for children in the original template', inject(
function($rootScope, $compile) {
element = $compile('<div iscope><a></a></div>')($rootScope);
expect(element.find('a').scope()).toBe($rootScope); //xx
})
);


it('should return the isolate scope for children in directive template', inject(
function($rootScope, $compile, $httpBackend) {
$httpBackend.expect('GET', 'tiscope.html').respond('<a></a>');
element = $compile('<div tiscope></div>')($rootScope);
expect(element.isolateScope()).toBeUndefined(); // this is the current behavior, not desired feature
$httpBackend.flush();
expect(element.find('a').scope()).toBe(element.isolateScope());
expect(element.isolateScope()).not.toBe($rootScope);
})
);
});


describe('with isolate scope directives and directives that manually create a new scope', function() {

it('should return the new scope at the directive element', inject(
function($rootScope, $compile) {
var directiveElement;
element = $compile('<div><a ng-if="true" iscope></a></div>')($rootScope);
$rootScope.$apply();
directiveElement = element.find('a');
expect(directiveElement.scope().$parent).toBe($rootScope);
expect(directiveElement.scope()).not.toBe(directiveElement.isolateScope());
})
);


it('should return the isolate scope for child elements', inject(
function($rootScope, $compile, $httpBackend) {
var directiveElement, child;
$httpBackend.expect('GET', 'tiscope.html').respond('<span></span>');
element = $compile('<div><a ng-if="true" tiscope></a></div>')($rootScope);
$rootScope.$apply();
$httpBackend.flush();
directiveElement = element.find('a');
child = directiveElement.find('span');
expect(child.scope()).toBe(directiveElement.isolateScope());
})
);
});
});
});
});
});
Expand Down Expand Up @@ -2121,6 +2235,7 @@ describe('$compile', function() {
});
}));


it('should give other directives the parent scope', inject(function($rootScope) {
compile('<div><input type="text" my-component store-scope ng-model="value"></div>');
$rootScope.$apply(function() {
Expand All @@ -2131,6 +2246,7 @@ describe('$compile', function() {
expect(componentScope.$parent).toBe(regularScope)
}));


it('should not give the isolate scope to other directive template', function() {
module(function() {
directive('otherTplDir', function() {
Expand Down

1 comment on commit 27e9340

@mgol
Copy link
Member

@mgol mgol commented on 27e9340 Nov 13, 2013

Choose a reason for hiding this comment

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

While I understand the reasons for this change, I think it's unfortunate breaking changes are introduced between RC and the final version. It makes it that much harder for developers to test & report potential bugs before the release.

Please sign in to comment.