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

Commit 7c81953

Browse files
committed
add tests and docs for ngRepeat + ngRef
1 parent 0d7e268 commit 7c81953

File tree

2 files changed

+121
-29
lines changed

2 files changed

+121
-29
lines changed

src/ng/directive/ngRef.js

+86-23
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,101 @@
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+
* template: '<strong>myWrapper</strong>' +
93+
* '<div>ngRepeatToggle.isOpen(): {{$ctrl.ngRepeatToggle.isOpen() | json}}</div>' +
94+
* '<my-toggle ng-ref="$ctrl.ngRepeatToggle">ngRepeat Toggle {{i}}</my-toggle>'
9495
* });
9596
* </file>
97+
* <file name="root.html">
98+
* <strong>myRoot</strong>
99+
* <my-toggle ng-ref="$ctrl.outerToggle">Outer Toggle</my-toggle>
100+
* <div>outerToggle.isOpen(): {{$ctrl.outerToggle.isOpen() | json}}</div>
101+
* <div><em>wrappers assigned to root</em><br>
102+
* <div ng-repeat="wrapper in $ctrl.wrappers">
103+
* wrapper.ngRepeatToggle.isOpen(): {{wrapper.ngRepeatToggle.isOpen() | json}}
104+
* </div>
105+
*
106+
* <ul>
107+
* <li ng-repeat="(index, value) in [1,2,3]">
108+
* <strong>ngRepeat</strong>
109+
* <div>outerToggle.isOpen(): {{$ctrl.outerToggle.isOpen() | json}}</div>
110+
* <my-wrapper ng-ref="$ctrl.wrappers[index]"></my-wrapper>
111+
* </li>
112+
* </ul>
113+
*
114+
* <div>ngIfToggle.isOpen(): {{ngIfToggle.isOpen()}} // This is always undefined because it's
115+
* assigned to the child scope created by ngIf.
116+
* </div>
117+
* <div ng-if="true">
118+
<strong>ngIf</strong>
119+
* <my-toggle ng-ref="ngIfToggle">ngIf Toggle</my-toggle>
120+
* <div>ngIfToggle.isOpen(): {{ngIfToggle.isOpen() | json}}</div>
121+
* <div>outerToggle.isOpen(): {{$ctrl.outerToggle.isOpen() | json}}</div>
122+
* </div>
123+
* </file>
124+
* <file name="styles.css">
125+
* ul {
126+
* list-style: none;
127+
* padding-left: 0;
128+
* }
129+
*
130+
* li[ng-repeat] {
131+
* background: lightgreen;
132+
* padding: 8px;
133+
* margin: 8px;
134+
* }
135+
*
136+
* [ng-if] {
137+
* background: lightgrey;
138+
* padding: 8px;
139+
* }
140+
*
141+
* my-root {
142+
* background: lightgoldenrodyellow;
143+
* padding: 8px;
144+
* display: block;
145+
* }
146+
*
147+
* my-wrapper {
148+
* background: lightsalmon;
149+
* padding: 8px;
150+
* display: block;
151+
* }
152+
*
153+
* my-toggle {
154+
* background: lightblue;
155+
* padding: 8px;
156+
* display: block;
157+
* }
158+
* </file>
96159
* <file name="protractor.js" type="protractor">
97160
* var OuterToggle = function() {
98161
* this.toggle = function() {

test/ng/directive/ngRefSpec.js

+35-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict';
22

3-
describe('ngRef', function() {
3+
fdescribe('ngRef', function() {
44

55
describe('on a component', function() {
66

@@ -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)