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

Commit 935bb71

Browse files
committed
add tests and docs for ngRepeat + ngRef
1 parent 416e251 commit 935bb71

File tree

2 files changed

+122
-29
lines changed

2 files changed

+122
-29
lines changed

src/ng/directive/ngRef.js

+88-24
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212
*
1313
* If the element with `ngRef` is destroyed `null` is assigned to the property.
1414
*
15+
* Note that if you want to assign from a child into the parent scope, you must initialize the
16+
* target property on the parent scope, otherwise `ngRef` will assign on the child scope.
17+
* This commonly happens when assigning elements or components wrapped in {@link ngIf} or
18+
* {@link ngRepeat}. See the second example below.
19+
*
1520
*
1621
* @element ANY
1722
* @param {string} ngRef property name - A valid AngularJS expression identifier to which the
@@ -56,43 +61,102 @@
5661
*
5762
* @example
5863
* ### ngRef inside scopes
59-
* This example shows how scopes contain `ngRef` assignments.
64+
* This example shows how `ngRef` works with child scopes. The `ngRepeat`-ed `myWrapper` components
65+
* are assigned to the scope of `myRoot`, because the `toggles` property has been initialized.
66+
* The repeated `myToggle` components are published to the child scopes created by `ngRepeat`.
67+
* `ngIf` behaves similarly - the assignment of `myToggle` happens in the `ngIf` child scope,
68+
* because the target property has not been initialized on the `myRoot` component controller.
69+
*
6070
* <example name="ng-ref-scopes" module="myApp">
6171
* <file name="index.html">
62-
* <h3>Outer Toggle</h3>
63-
* <my-toggle ng-ref="outerToggle">Outer Toggle</my-toggle>
64-
* <div>outerToggle.isOpen(): {{outerToggle.isOpen() | json}}</div>
65-
*
66-
* <h3>ngRepeat toggle</h3>
67-
* <ul>
68-
* <li ng-repeat="i in [1,2,3]">
69-
* <my-toggle ng-ref="ngRepeatToggle">ngRepeat Toggle {{i}}</my-toggle>
70-
* <div>ngRepeatToggle.isOpen(): {{ngRepeatToggle.isOpen() | json}}</div>
71-
* <div>outerToggle.isOpen(): {{outerToggle.isOpen() | json}}</div>
72-
* </li>
73-
* </ul>
74-
* <div>ngRepeatToggle.isOpen(): {{ngRepeatToggle.isOpen() | json}}</div>
75-
*
76-
* <h3>ngIf toggle</h3>
77-
* <div ng-if="true">
78-
* <my-toggle ng-ref="ngIfToggle">ngIf Toggle</my-toggle>
79-
* <div>ngIfToggle.isOpen(): {{ngIfToggle.isOpen() | json}}</div>
80-
* <div>outerToggle.isOpen(): {{outerToggle.isOpen() | json}}</div>
81-
* </div>
82-
* <div>ngIfToggle.isOpen(): {{ngIfToggle.isOpen() | json}}</div>
72+
* <my-root></my-root>
8373
* </file>
8474
* <file name="index.js">
8575
* angular.module('myApp', [])
76+
* .component('myRoot', {
77+
* templateUrl: 'root.html',
78+
* controller: function() {
79+
* this.wrappers = []; // initialize the array so that the wrappers are assigned into the parent scope
80+
* }
81+
* })
8682
* .component('myToggle', {
87-
* template: '<button ng-click="$ctrl.toggle()" ng-transclude></button>',
83+
* template: '<strong>myToggle</strong><button ng-click="$ctrl.toggle()" ng-transclude></button>',
8884
* transclude: true,
8985
* controller: function ToggleController() {
9086
* var opened = false;
9187
* this.isOpen = function() { return opened; };
9288
* this.toggle = function() { opened = !opened; };
9389
* }
90+
* })
91+
* .component('myWrapper', {
92+
* transclude: true,
93+
* template: '<strong>myWrapper</strong>' +
94+
* '<div>ngRepeatToggle.isOpen(): {{$ctrl.ngRepeatToggle.isOpen() | json}}</div>' +
95+
* '<my-toggle ng-ref="$ctrl.ngRepeatToggle"><ng-transclude></ng-transclude></my-toggle>'
9496
* });
9597
* </file>
98+
* <file name="root.html">
99+
* <strong>myRoot</strong>
100+
* <my-toggle ng-ref="$ctrl.outerToggle">Outer Toggle</my-toggle>
101+
* <div>outerToggle.isOpen(): {{$ctrl.outerToggle.isOpen() | json}}</div>
102+
* <div><em>wrappers assigned to root</em><br>
103+
* <div ng-repeat="wrapper in $ctrl.wrappers">
104+
* wrapper.ngRepeatToggle.isOpen(): {{wrapper.ngRepeatToggle.isOpen() | json}}
105+
* </div>
106+
*
107+
* <ul>
108+
* <li ng-repeat="(index, value) in [1,2,3]">
109+
* <strong>ngRepeat</strong>
110+
* <div>outerToggle.isOpen(): {{$ctrl.outerToggle.isOpen() | json}}</div>
111+
* <my-wrapper ng-ref="$ctrl.wrappers[index]">ngRepeat Toggle {{$index + 1}}</my-wrapper>
112+
* </li>
113+
* </ul>
114+
*
115+
* <div>ngIfToggle.isOpen(): {{ngIfToggle.isOpen()}} // This is always undefined because it's
116+
* assigned to the child scope created by ngIf.
117+
* </div>
118+
* <div ng-if="true">
119+
<strong>ngIf</strong>
120+
* <my-toggle ng-ref="ngIfToggle">ngIf Toggle</my-toggle>
121+
* <div>ngIfToggle.isOpen(): {{ngIfToggle.isOpen() | json}}</div>
122+
* <div>outerToggle.isOpen(): {{$ctrl.outerToggle.isOpen() | json}}</div>
123+
* </div>
124+
* </file>
125+
* <file name="styles.css">
126+
* ul {
127+
* list-style: none;
128+
* padding-left: 0;
129+
* }
130+
*
131+
* li[ng-repeat] {
132+
* background: lightgreen;
133+
* padding: 8px;
134+
* margin: 8px;
135+
* }
136+
*
137+
* [ng-if] {
138+
* background: lightgrey;
139+
* padding: 8px;
140+
* }
141+
*
142+
* my-root {
143+
* background: lightgoldenrodyellow;
144+
* padding: 8px;
145+
* display: block;
146+
* }
147+
*
148+
* my-wrapper {
149+
* background: lightsalmon;
150+
* padding: 8px;
151+
* display: block;
152+
* }
153+
*
154+
* my-toggle {
155+
* background: lightblue;
156+
* padding: 8px;
157+
* display: block;
158+
* }
159+
* </file>
96160
* <file name="protractor.js" type="protractor">
97161
* var OuterToggle = function() {
98162
* this.toggle = function() {
@@ -103,7 +167,7 @@
103167
* };
104168
* };
105169
* var NgRepeatToggle = function(i) {
106-
* var parent = element.all(by.repeater('i in [1,2,3]')).get(i - 1);
170+
* var parent = element.all(by.repeater('(index, value) in [1,2,3]')).get(i - 1);
107171
* this.toggle = function() {
108172
* element(by.buttonText('ngRepeat Toggle ' + i)).click();
109173
* };

test/ng/directive/ngRefSpec.js

+34-5
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ describe('ngRef', function() {
1010
$compileProvider.component('myComponent', {
1111
template: 'foo',
1212
controller: function() {
13-
console.log('ctrl');
1413
myComponentController = this;
1514
}
1615
});
@@ -101,11 +100,11 @@ describe('ngRef', function() {
101100
expect($rootScope.myComponent).toBe(enteringController);
102101
}));
103102

104-
it('should allow bind to a parent controller', function() {
105-
$rootScope.$ctrl = {};
103+
it('should allow binding to a nested property', function() {
104+
$rootScope.obj = {};
106105

107-
$compile('<my-component ng-ref="$ctrl.myComponent"></my-component>')($rootScope);
108-
expect($rootScope.$ctrl.myComponent).toBe(myComponentController);
106+
$compile('<my-component ng-ref="obj.myComponent"></my-component>')($rootScope);
107+
expect($rootScope.obj.myComponent).toBe(myComponentController);
109108
});
110109

111110
});
@@ -485,4 +484,34 @@ describe('ngRef', function() {
485484
});
486485
});
487486

487+
488+
it('should work with ngRepeat-ed components', function() {
489+
var controllers = [];
490+
491+
module(function($compileProvider) {
492+
$compileProvider.component('myComponent', {
493+
template: 'foo',
494+
controller: function() {
495+
controllers.push(this);
496+
}
497+
});
498+
});
499+
500+
501+
inject(function($compile, $rootScope) {
502+
$rootScope.elements = [0,1,2,3,4];
503+
$rootScope.controllers = []; // Initialize the array because ngRepeat creates a child scope
504+
505+
var template = '<div><my-component ng-repeat="(key, el) in elements" ng-ref="controllers[key]"></my-component></div>';
506+
var element = $compile(template)($rootScope);
507+
$rootScope.$apply();
508+
509+
expect($rootScope.controllers).toEqual(controllers);
510+
511+
$rootScope.$apply('elements = []');
512+
513+
expect($rootScope.controllers).toEqual([null, null, null, null, null]);
514+
});
515+
});
516+
488517
});

0 commit comments

Comments
 (0)