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

Commit 27e9340

Browse files
committed
feat(jqLite): expose isolateScope() getter similar to scope()
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.
1 parent b5af198 commit 27e9340

File tree

5 files changed

+178
-6
lines changed

5 files changed

+178
-6
lines changed

src/Angular.js

+1
Original file line numberDiff line numberDiff line change
@@ -1244,6 +1244,7 @@ function bindJQuery() {
12441244
jqLite = jQuery;
12451245
extend(jQuery.fn, {
12461246
scope: JQLitePrototype.scope,
1247+
isolateScope: JQLitePrototype.isolateScope,
12471248
controller: JQLitePrototype.controller,
12481249
injector: JQLitePrototype.injector,
12491250
inheritedData: JQLitePrototype.inheritedData

src/jqLite.js

+15-2
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@
8585
* - `injector()` - retrieves the injector of the current element or its parent.
8686
* - `scope()` - retrieves the {@link api/ng.$rootScope.Scope scope} of the current
8787
* element or its parent.
88+
* - `isolateScope()` - retrieves an isolate {@link api/ng.$rootScope.Scope scope} if one is attached directly to the
89+
* current element. This getter should be used only on elements that contain a directive which starts a new isolate
90+
* scope. Calling `scope()` on this element always returns the original non-isolate scope.
8891
* - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
8992
* parent element is reached.
9093
*
@@ -344,9 +347,13 @@ function jqLiteInheritedData(element, name, value) {
344347
if(element[0].nodeType == 9) {
345348
element = element.find('html');
346349
}
350+
var names = isArray(name) ? name : [name];
347351

348352
while (element.length) {
349-
if ((value = element.data(name)) !== undefined) return value;
353+
354+
for (var i = 0, ii = names.length; i < ii; i++) {
355+
if ((value = element.data(names[i])) !== undefined) return value;
356+
}
350357
element = element.parent();
351358
}
352359
}
@@ -418,7 +425,13 @@ forEach({
418425
inheritedData: jqLiteInheritedData,
419426

420427
scope: function(element) {
421-
return jqLiteInheritedData(element, '$scope');
428+
// Can't use jqLiteData here directly so we stay compatible with jQuery!
429+
return jqLite(element).data('$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
430+
},
431+
432+
isolateScope: function(element) {
433+
// Can't use jqLiteData here directly so we stay compatible with jQuery!
434+
return jqLite(element).data('$isolateScope') || jqLite(element).data('$isolateScopeNoTemplate');
422435
},
423436

424437
controller: jqLiteController ,

src/ng/compile.js

+10-2
Original file line numberDiff line numberDiff line change
@@ -1368,7 +1368,15 @@ function $CompileProvider($provide) {
13681368
var $linkNode = jqLite(linkNode);
13691369

13701370
isolateScope = scope.$new(true);
1371-
$linkNode.data('$isolateScope', isolateScope);
1371+
1372+
if (templateDirective && (templateDirective === newIsolateScopeDirective.$$originalDirective)) {
1373+
$linkNode.data('$isolateScope', isolateScope) ;
1374+
} else {
1375+
$linkNode.data('$isolateScopeNoTemplate', isolateScope);
1376+
}
1377+
1378+
1379+
13721380
safeAddClass($linkNode, 'ng-isolate-scope');
13731381

13741382
forEach(newIsolateScopeDirective.scope, function(definition, scopeName) {
@@ -1600,7 +1608,7 @@ function $CompileProvider($provide) {
16001608
origAsyncDirective = directives.shift(),
16011609
// The fact that we have to copy and patch the directive seems wrong!
16021610
derivedSyncDirective = extend({}, origAsyncDirective, {
1603-
templateUrl: null, transclude: null, replace: null
1611+
templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
16041612
}),
16051613
templateUrl = (isFunction(origAsyncDirective.templateUrl))
16061614
? origAsyncDirective.templateUrl($compileNode, tAttrs)

test/jqLiteSpec.js

+34
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,13 @@ describe('jqLite', function() {
151151
dealoc(element);
152152
});
153153

154+
it('should retrieve isolate scope attached to the current element', function() {
155+
var element = jqLite('<i>foo</i>');
156+
element.data('$isolateScope', scope);
157+
expect(element.isolateScope()).toBe(scope);
158+
dealoc(element);
159+
});
160+
154161
it('should retrieve scope attached to the html element if its requested on the document',
155162
function() {
156163
var doc = jqLite(document),
@@ -182,6 +189,33 @@ describe('jqLite', function() {
182189
});
183190

184191

192+
describe('isolateScope', function() {
193+
194+
it('should retrieve isolate scope attached to the current element', function() {
195+
var element = jqLite('<i>foo</i>');
196+
element.data('$isolateScope', scope);
197+
expect(element.isolateScope()).toBe(scope);
198+
dealoc(element);
199+
});
200+
201+
202+
it('should not walk up the dom to find scope', function() {
203+
var element = jqLite('<ul><li><p><b>deep deep</b><p></li></ul>');
204+
var deepChild = jqLite(element[0].getElementsByTagName('b')[0]);
205+
element.data('$isolateScope', scope);
206+
expect(deepChild.isolateScope()).toBeUndefined();
207+
dealoc(element);
208+
});
209+
210+
211+
it('should return undefined when no scope was found', function() {
212+
var element = jqLite('<div></div>');
213+
expect(element.isolateScope()).toBeFalsy();
214+
dealoc(element);
215+
});
216+
});
217+
218+
185219
describe('injector', function() {
186220
it('should retrieve injector attached to the current element or its parent', function() {
187221
var template = jqLite('<div><span></span></div>'),

test/ng/compileSpec.js

+118-2
Original file line numberDiff line numberDiff line change
@@ -1375,7 +1375,7 @@ describe('$compile', function() {
13751375
return function (scope, element) {
13761376
iscope = scope;
13771377
log(scope.$id);
1378-
expect(element.data('$isolateScope')).toBe(scope);
1378+
expect(element.data('$isolateScopeNoTemplate')).toBe(scope);
13791379
};
13801380
}
13811381
};
@@ -1522,7 +1522,7 @@ describe('$compile', function() {
15221522
);
15231523

15241524

1525-
it('should allow more one new scope directives per element, but directives should share' +
1525+
it('should allow more than one new scope directives per element, but directives should share' +
15261526
'the scope', inject(
15271527
function($rootScope, $compile, log) {
15281528
element = $compile('<div class="scope-a; scope-b"></div>')($rootScope);
@@ -1554,6 +1554,120 @@ describe('$compile', function() {
15541554
expect(log).toEqual('002');
15551555
})
15561556
);
1557+
1558+
1559+
describe('scope()/isolate() scope getters', function() {
1560+
1561+
describe('with no directives', function() {
1562+
1563+
it('should return the scope of the parent node', inject(
1564+
function($rootScope, $compile) {
1565+
element = $compile('<div></div>')($rootScope);
1566+
expect(element.scope()).toBe($rootScope);
1567+
})
1568+
);
1569+
});
1570+
1571+
1572+
describe('with new scope directives', function() {
1573+
1574+
it('should return the new scope at the directive element', inject(
1575+
function($rootScope, $compile) {
1576+
element = $compile('<div scope></div>')($rootScope);
1577+
expect(element.scope().$parent).toBe($rootScope);
1578+
})
1579+
);
1580+
1581+
1582+
it('should return the new scope for children in the original template', inject(
1583+
function($rootScope, $compile) {
1584+
element = $compile('<div scope><a></a></div>')($rootScope);
1585+
expect(element.find('a').scope().$parent).toBe($rootScope);
1586+
})
1587+
);
1588+
1589+
1590+
it('should return the new scope for children in the directive template', inject(
1591+
function($rootScope, $compile, $httpBackend) {
1592+
$httpBackend.expect('GET', 'tscope.html').respond('<a></a>');
1593+
element = $compile('<div tscope></div>')($rootScope);
1594+
$httpBackend.flush();
1595+
expect(element.find('a').scope().$parent).toBe($rootScope);
1596+
})
1597+
);
1598+
});
1599+
1600+
1601+
describe('with isolate scope directives', function() {
1602+
1603+
it('should return the root scope for directives at the root element', inject(
1604+
function($rootScope, $compile) {
1605+
element = $compile('<div iscope></div>')($rootScope);
1606+
expect(element.scope()).toBe($rootScope);
1607+
})
1608+
);
1609+
1610+
1611+
it('should return the non-isolate scope at the directive element', inject(
1612+
function($rootScope, $compile) {
1613+
var directiveElement;
1614+
element = $compile('<div><div iscope></div></div>')($rootScope);
1615+
directiveElement = element.children();
1616+
expect(directiveElement.scope()).toBe($rootScope);
1617+
expect(directiveElement.isolateScope().$parent).toBe($rootScope);
1618+
})
1619+
);
1620+
1621+
1622+
it('should return the isolate scope for children in the original template', inject(
1623+
function($rootScope, $compile) {
1624+
element = $compile('<div iscope><a></a></div>')($rootScope);
1625+
expect(element.find('a').scope()).toBe($rootScope); //xx
1626+
})
1627+
);
1628+
1629+
1630+
it('should return the isolate scope for children in directive template', inject(
1631+
function($rootScope, $compile, $httpBackend) {
1632+
$httpBackend.expect('GET', 'tiscope.html').respond('<a></a>');
1633+
element = $compile('<div tiscope></div>')($rootScope);
1634+
expect(element.isolateScope()).toBeUndefined(); // this is the current behavior, not desired feature
1635+
$httpBackend.flush();
1636+
expect(element.find('a').scope()).toBe(element.isolateScope());
1637+
expect(element.isolateScope()).not.toBe($rootScope);
1638+
})
1639+
);
1640+
});
1641+
1642+
1643+
describe('with isolate scope directives and directives that manually create a new scope', function() {
1644+
1645+
it('should return the new scope at the directive element', inject(
1646+
function($rootScope, $compile) {
1647+
var directiveElement;
1648+
element = $compile('<div><a ng-if="true" iscope></a></div>')($rootScope);
1649+
$rootScope.$apply();
1650+
directiveElement = element.find('a');
1651+
expect(directiveElement.scope().$parent).toBe($rootScope);
1652+
expect(directiveElement.scope()).not.toBe(directiveElement.isolateScope());
1653+
})
1654+
);
1655+
1656+
1657+
it('should return the isolate scope for child elements', inject(
1658+
function($rootScope, $compile, $httpBackend) {
1659+
var directiveElement, child;
1660+
$httpBackend.expect('GET', 'tiscope.html').respond('<span></span>');
1661+
element = $compile('<div><a ng-if="true" tiscope></a></div>')($rootScope);
1662+
$rootScope.$apply();
1663+
$httpBackend.flush();
1664+
directiveElement = element.find('a');
1665+
child = directiveElement.find('span');
1666+
expect(child.scope()).toBe(directiveElement.isolateScope());
1667+
})
1668+
);
1669+
});
1670+
});
15571671
});
15581672
});
15591673
});
@@ -2121,6 +2235,7 @@ describe('$compile', function() {
21212235
});
21222236
}));
21232237

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

2249+
21342250
it('should not give the isolate scope to other directive template', function() {
21352251
module(function() {
21362252
directive('otherTplDir', function() {

0 commit comments

Comments
 (0)