aboutsummaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorTobias Bosch2013-11-14 13:50:36 -0800
committerVojta Jina2013-11-14 20:59:50 -0800
commit90f87072e83234ae366cfeb3c281503c31dad738 (patch)
treed969b74c0fe993900bc91e3e9f1d8004d238ac2c /test
parentc785918cbd245cc8ecf9a38e373b121c4e68a55b (diff)
downloadangular.js-90f87072e83234ae366cfeb3c281503c31dad738.tar.bz2
fix($compile): accessing controllers of transcluded directives from children
Additional API (backwards compatible) - Injects `$transclude` (see directive controllers) as 5th argument to directive link functions. - `$transclude` takes an optional scope as first parameter that overrides the bound scope. Deprecations: - `transclude` parameter of directive compile functions (use the new parameter for link functions instead). Refactorings: - Don't use comment node to temporarily store controllers - `ngIf`, `ngRepeat`, ... now all use `$transclude` Closes #4935.
Diffstat (limited to 'test')
-rwxr-xr-xtest/ng/compileSpec.js203
-rwxr-xr-xtest/ng/directive/ngIfSpec.js28
-rw-r--r--test/ng/directive/ngIncludeSpec.js30
-rw-r--r--test/ng/directive/ngRepeatSpec.js27
-rw-r--r--test/ngRoute/directive/ngViewSpec.js38
5 files changed, 324 insertions, 2 deletions
diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js
index 3e35fae7..85ee17d2 100755
--- a/test/ng/compileSpec.js
+++ b/test/ng/compileSpec.js
@@ -3438,6 +3438,113 @@ describe('$compile', function() {
expect(log).toEqual('pre(); post(unicorn!)');
});
});
+
+ it('should copy the directive controller to all clones', function() {
+ var transcludeCtrl, cloneCount = 2;
+ module(function() {
+ directive('transclude', valueFn({
+ transclude: 'content',
+ controller: function($transclude) {
+ transcludeCtrl = this;
+ },
+ link: function(scope, el, attr, ctrl, $transclude) {
+ var i;
+ for (i=0; i<cloneCount; i++) {
+ $transclude(cloneAttach);
+ }
+
+ function cloneAttach(clone) {
+ el.append(clone);
+ }
+ }
+ }));
+ });
+ inject(function($compile) {
+ element = $compile('<div transclude><span></span></div>')($rootScope);
+ var children = element.children(), i;
+ expect(transcludeCtrl).toBeDefined();
+
+ expect(element.data('$transcludeController')).toBe(transcludeCtrl);
+ for (i=0; i<cloneCount; i++) {
+ expect(children.eq(i).data('$transcludeController')).toBeUndefined();
+ }
+ });
+ });
+
+ it('should provide the $transclude controller local as 5th argument to the pre and post-link function', function() {
+ var ctrlTransclude, preLinkTransclude, postLinkTransclude;
+ module(function() {
+ directive('transclude', valueFn({
+ transclude: 'content',
+ controller: function($transclude) {
+ ctrlTransclude = $transclude;
+ },
+ compile: function() {
+ return {
+ pre: function(scope, el, attr, ctrl, $transclude) {
+ preLinkTransclude = $transclude;
+ },
+ post: function(scope, el, attr, ctrl, $transclude) {
+ postLinkTransclude = $transclude;
+ }
+ };
+ }
+ }));
+ });
+ inject(function($compile) {
+ element = $compile('<div transclude></div>')($rootScope);
+ expect(ctrlTransclude).toBeDefined();
+ expect(ctrlTransclude).toBe(preLinkTransclude);
+ expect(ctrlTransclude).toBe(postLinkTransclude);
+ });
+ });
+
+ it('should allow an optional scope argument in $transclude', function() {
+ var capturedChildCtrl;
+ module(function() {
+ directive('transclude', valueFn({
+ transclude: 'content',
+ link: function(scope, element, attr, ctrl, $transclude) {
+ $transclude(scope, function(clone) {
+ element.append(clone);
+ });
+ }
+ }));
+ });
+ inject(function($compile) {
+ element = $compile('<div transclude>{{$id}}</div>')($rootScope);
+ $rootScope.$apply();
+ expect(element.text()).toBe($rootScope.$id);
+ });
+
+ });
+
+ it('should expose the directive controller to transcluded children', function() {
+ var capturedChildCtrl;
+ module(function() {
+ directive('transclude', valueFn({
+ transclude: 'content',
+ controller: function() {
+ },
+ link: function(scope, element, attr, ctrl, $transclude) {
+ $transclude(function(clone) {
+ element.append(clone);
+ });
+ }
+ }));
+ directive('child', valueFn({
+ require: '^transclude',
+ link: function(scope, element, attr, ctrl) {
+ capturedChildCtrl = ctrl;
+ }
+ }));
+ });
+ inject(function($compile) {
+ element = $compile('<div transclude><div child></div></div>')($rootScope);
+ expect(capturedChildCtrl).toBeTruthy();
+ });
+
+ });
});
@@ -3471,7 +3578,6 @@ describe('$compile', function() {
});
});
-
it('should only allow one element transclusion per element', function() {
module(function() {
directive('first', valueFn({
@@ -3620,8 +3726,101 @@ describe('$compile', function() {
]);
});
});
- });
+ it('should allow to access $transclude in the same directive', function() {
+ var _$transclude;
+ module(function() {
+ directive('transclude', valueFn({
+ transclude: 'element',
+ controller: function($transclude) {
+ _$transclude = $transclude;
+ }
+ }));
+ });
+ inject(function($compile) {
+ element = $compile('<div transclude></div>')($rootScope);
+ expect(_$transclude).toBeDefined()
+ });
+ });
+
+ it('should copy the directive controller to all clones', function() {
+ var transcludeCtrl, cloneCount = 2;
+ module(function() {
+ directive('transclude', valueFn({
+ transclude: 'element',
+ controller: function() {
+ transcludeCtrl = this;
+ },
+ link: function(scope, el, attr, ctrl, $transclude) {
+ var i;
+ for (i=0; i<cloneCount; i++) {
+ $transclude(cloneAttach);
+ }
+
+ function cloneAttach(clone) {
+ el.after(clone);
+ }
+ }
+ }));
+ });
+ inject(function($compile) {
+ element = $compile('<div><div transclude></div></div>')($rootScope);
+ var children = element.children(), i;
+ for (i=0; i<cloneCount; i++) {
+ expect(children.eq(i).data('$transcludeController')).toBe(transcludeCtrl);
+ }
+ });
+ });
+
+ it('should expose the directive controller to transcluded children', function() {
+ var capturedTranscludeCtrl;
+ module(function() {
+ directive('transclude', valueFn({
+ transclude: 'element',
+ controller: function() {
+ },
+ link: function(scope, element, attr, ctrl, $transclude) {
+ $transclude(scope, function(clone) {
+ element.after(clone);
+ });
+ }
+ }));
+ directive('child', valueFn({
+ require: '^transclude',
+ link: function(scope, element, attr, ctrl) {
+ capturedTranscludeCtrl = ctrl;
+ }
+ }));
+ });
+ inject(function($compile) {
+ element = $compile('<div transclude><div child></div></div>')($rootScope);
+ expect(capturedTranscludeCtrl).toBeTruthy();
+ });
+ });
+
+ it('should allow access to $transclude in a templateUrl directive', function() {
+ var transclude;
+ module(function() {
+ directive('template', valueFn({
+ templateUrl: 'template.html',
+ replace: true
+ }));
+ directive('transclude', valueFn({
+ transclude: 'content',
+ controller: function($transclude) {
+ transclude = $transclude;
+ }
+ }));
+ });
+ inject(function($compile, $httpBackend) {
+ $httpBackend.expectGET('template.html').respond('<div transclude></div>');
+ element = $compile('<div template></div>')($rootScope);
+ $httpBackend.flush();
+ expect(transclude).toBeDefined();
+ });
+ });
+
+ });
it('should safely create transclude comment node and not break with "-->"',
inject(function($rootScope) {
diff --git a/test/ng/directive/ngIfSpec.js b/test/ng/directive/ngIfSpec.js
index 3173f476..427bfd59 100755
--- a/test/ng/directive/ngIfSpec.js
+++ b/test/ng/directive/ngIfSpec.js
@@ -148,6 +148,34 @@ describe('ngIf', function () {
});
+describe('ngIf and transcludes', function() {
+ it('should allow access to directive controller from children when used in a replace template', function() {
+ var controller;
+ module(function($compileProvider) {
+ var directive = $compileProvider.directive;
+ directive('template', valueFn({
+ template: '<div ng-if="true"><span test></span></div>',
+ replace: true,
+ controller: function() {
+ this.flag = true;
+ }
+ }));
+ directive('test', valueFn({
+ require: '^template',
+ link: function(scope, el, attr, ctrl) {
+ controller = ctrl;
+ }
+ }));
+ });
+ inject(function($compile, $rootScope) {
+ var element = $compile('<div><div template></div></div>')($rootScope);
+ $rootScope.$apply();
+ expect(controller.flag).toBe(true);
+ dealoc(element);
+ });
+ });
+});
+
describe('ngIf animations', function () {
var body, element, $rootElement;
diff --git a/test/ng/directive/ngIncludeSpec.js b/test/ng/directive/ngIncludeSpec.js
index beb29da7..aba71e44 100644
--- a/test/ng/directive/ngIncludeSpec.js
+++ b/test/ng/directive/ngIncludeSpec.js
@@ -439,6 +439,36 @@ describe('ngInclude', function() {
});
});
+describe('ngInclude and transcludes', function() {
+ it('should allow access to directive controller from children when used in a replace template', function() {
+ var controller;
+ module(function($compileProvider) {
+ var directive = $compileProvider.directive;
+ directive('template', valueFn({
+ template: '<div ng-include="\'include.html\'"></div>',
+ replace: true,
+ controller: function() {
+ this.flag = true;
+ }
+ }));
+ directive('test', valueFn({
+ require: '^template',
+ link: function(scope, el, attr, ctrl) {
+ controller = ctrl;
+ }
+ }));
+ });
+ inject(function($compile, $rootScope, $httpBackend) {
+ $httpBackend.expectGET('include.html').respond('<div><div test></div></div>');
+ var element = $compile('<div><div template></div></div>')($rootScope);
+ $rootScope.$apply();
+ $httpBackend.flush();
+ expect(controller.flag).toBe(true);
+ dealoc(element);
+ });
+ });
+});
+
describe('ngInclude animations', function() {
var body, element, $rootElement;
diff --git a/test/ng/directive/ngRepeatSpec.js b/test/ng/directive/ngRepeatSpec.js
index 9dde36e7..6584f31a 100644
--- a/test/ng/directive/ngRepeatSpec.js
+++ b/test/ng/directive/ngRepeatSpec.js
@@ -1058,6 +1058,33 @@ describe('ngRepeat', function() {
});
});
+describe('ngRepeat and transcludes', function() {
+ it('should allow access to directive controller from children when used in a replace template', function() {
+ var controller;
+ module(function($compileProvider) {
+ var directive = $compileProvider.directive;
+ directive('template', valueFn({
+ template: '<div ng-repeat="l in [1]"><span test></span></div>',
+ replace: true,
+ controller: function() {
+ this.flag = true;
+ }
+ }));
+ directive('test', valueFn({
+ require: '^template',
+ link: function(scope, el, attr, ctrl) {
+ controller = ctrl;
+ }
+ }));
+ });
+ inject(function($compile, $rootScope) {
+ var element = $compile('<div><div template></div></div>')($rootScope);
+ $rootScope.$apply();
+ expect(controller.flag).toBe(true);
+ dealoc(element);
+ });
+ });
+});
describe('ngRepeat animations', function() {
var body, element, $rootElement;
diff --git a/test/ngRoute/directive/ngViewSpec.js b/test/ngRoute/directive/ngViewSpec.js
index 1df19d6a..e96da022 100644
--- a/test/ngRoute/directive/ngViewSpec.js
+++ b/test/ngRoute/directive/ngViewSpec.js
@@ -514,6 +514,44 @@ describe('ngView', function() {
});
});
+describe('ngView and transcludes', function() {
+ it('should allow access to directive controller from children when used in a replace template', function() {
+ var controller;
+ module('ngRoute');
+ module(function($compileProvider, $routeProvider) {
+ $routeProvider.when('/view', {templateUrl: 'view.html'});
+ var directive = $compileProvider.directive;
+ directive('template', function() {
+ return {
+ template: '<div ng-view></div>',
+ replace: true,
+ controller: function() {
+ this.flag = true;
+ }
+ };
+ });
+
+ directive('test', function() {
+ return {
+ require: '^template',
+ link: function(scope, el, attr, ctrl) {
+ controller = ctrl;
+ }
+ };
+ });
+ });
+ inject(function($compile, $rootScope, $httpBackend, $location) {
+ $httpBackend.expectGET('view.html').respond('<div><div test></div></div>');
+ var element = $compile('<div><div template></div></div>')($rootScope);
+ $location.url('/view');
+ $rootScope.$apply();
+ $httpBackend.flush();
+ expect(controller.flag).toBe(true);
+ dealoc(element);
+ });
+ });
+});
+
describe('ngView animations', function() {
var body, element, $rootElement;