forked from angular/angular.js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathngRef.js
209 lines (205 loc) · 7.9 KB
/
ngRef.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
'use strict';
/**
* @ngdoc directive
* @name ngRef
* @restrict A
*
* @description
* The `ngRef` attribute tells AngularJS to assign the element component controller
* to the given property in the current scope.
*
* If the component is destroyed `null` is assigned to the property.
*
*
* @element ANY
* @param {string} ngRef property name - this must be a valid AngularJS expression identifier
*
*
* @example
* ### Simple toggle
* This example shows how the controller of the component toggle
* is reused in the template through the scope to use its logic.
* <example name="ng-ref-component" module="myApp">
* <file name="index.html">
* <my-toggle ng-ref="myToggle"></my-toggle>
* <button ng-click="myToggle.toggle()">Toggle</button>
* <div ng-show="myToggle.isOpen()">
* You are using a component in the same template to show it.
* </div>
* </file>
* <file name="index.js">
* angular.module('myApp', []);
* </file>
* <file name="toggle.js">
* function ToggleController() {
* var opened = false;
* this.isOpen = function() { return opened; };
* this.toggle = function() { opened = !opened; };
* }
*
* angular.module('myApp').component('myToggle', {
* controller: ToggleController
* });
* </file>
* <file name="protractor.js" type="protractor">
* it('should publish the toggle into the scope', function() {
* var toggle = element(by.buttonText('Toggle'));
* expect(toggle.evaluate('myToggle.isOpen()')).toEqual(false);
* toggle.click();
* expect(toggle.evaluate('myToggle.isOpen()')).toEqual(true);
* });
* </file>
* </example>
*
* @example
* ### ngRef inside scopes
* This example shows how new scopes limits
* <example name="ng-ref-scopes" module="myApp">
* <file name="index.html">
* <h3>Outer Toggle</h3>
* <my-toggle ng-ref="outerToggle">Outer Toggle</my-toggle>
* <div>outerToggle.isOpen(): {{outerToggle.isOpen() | json}}</div>
*
* <h3>ngRepeat toggle</h3>
* <ul>
* <li ng-repeat="i in [1,2,3]">
* <my-toggle ng-ref="ngRepeatToggle">ngRepeat Toggle {{i}}</my-toggle>
* <div>ngRepeatToggle.isOpen(): {{ngRepeatToggle.isOpen() | json}}</div>
* <div>outerToggle.isOpen(): {{outerToggle.isOpen() | json}}</div>
* </li>
* </ul>
* <div>ngRepeat.isOpen(): {{ngRepeatToggle.isOpen() | json}}</div>
*
* <h3>ngIf toggle</h3>
* <div ng-if="true">
* <my-toggle ng-ref="ngIfToggle">ngIf Toggle</my-toggle>
* <div>ngIfToggle.isOpen(): {{ngIfToggle.isOpen() | json}}</div>
* <div>outerToggle.isOpen(): {{outerToggle.isOpen() | json}}</div>
* </div>
* <div>ngIf.isOpen(): {{ngIf.isOpen() | json}}</div>
* </file>
* <file name="index.js">
* angular.module('myApp', []);
* </file>
* <file name="toggle.js">
* function ToggleController() {
* var opened = false;
* this.isOpen = function() { return opened; };
* this.toggle = function() { opened = !opened; };
* }
*
* angular.module('myApp').component('myToggle', {
* template: '<button ng-click="$ctrl.toggle()" ng-transclude></button>',
* transclude: true,
* controller: ToggleController
* });
* </file>
* <file name="protractor.js" type="protractor">
* var OuterToggle = function() {
* this.toggle = function() {
* element(by.buttonText('Outer Toggle')).click();
* };
* this.isOpen = function() {
* return element.all(by.binding('outerToggle.isOpen()')).first().getText();
* };
* };
* var NgRepeatToggle = function(i) {
* var parent = element.all(by.repeater('i in [1,2,3]')).get(i - 1);
* this.toggle = function() {
* element(by.buttonText('ngRepeat Toggle ' + i)).click();
* };
* this.isOpen = function() {
* return parent.element(by.binding('ngRepeatToggle.isOpen() | json')).getText();
* };
* this.isOuterOpen = function() {
* return parent.element(by.binding('outerToggle.isOpen() | json')).getText();
* };
* };
* var NgRepeatToggles = function() {
* var toggles = [1,2,3].map(function(i) { return new NgRepeatToggle(i); });
* this.forEach = function(fn) {
* toggles.forEach(fn);
* };
* this.isOuterOpen = function(i) {
* return toggles[i - 1].isOuterOpen();
* };
* };
* var NgIfToggle = function() {
* var parent = element(by.css('[ng-if]'));
* this.toggle = function() {
* element(by.buttonText('ngIf Toggle')).click();
* };
* this.isOpen = function() {
* return by.binding('ngIfToggle.isOpen() | json').getText();
* };
* this.isOuterOpen = function() {
* return parent.element(by.binding('outerToggle.isOpen() | json')).getText();
* };
* };
*
* it('should toggle the outer toggle', function() {
* var outerToggle = new OuterToggle();
* expect(outerToggle.isOpen()).toEqual('outerToggle.isOpen(): false');
* outerToggle.toggle();
* expect(outerToggle.isOpen()).toEqual('outerToggle.isOpen(): true');
* });
*
* it('should toggle all outer toggles', function() {
* var outerToggle = new OuterToggle();
* var repeatToggles = new NgRepeatToggles();
* var ifToggle = new NgIfToggle();
* expect(outerToggle.isOpen()).toEqual('outerToggle.isOpen(): false');
* expect(repeatToggles.isOuterOpen(1)).toEqual('outerToggle.isOpen(): false');
* expect(repeatToggles.isOuterOpen(2)).toEqual('outerToggle.isOpen(): false');
* expect(repeatToggles.isOuterOpen(3)).toEqual('outerToggle.isOpen(): false');
* expect(ifToggle.isOuterOpen()).toEqual('outerToggle.isOpen(): false');
* outerToggle.toggle();
* expect(outerToggle.isOpen()).toEqual('outerToggle.isOpen(): true');
* expect(repeatToggles.isOuterOpen(1)).toEqual('outerToggle.isOpen(): true');
* expect(repeatToggles.isOuterOpen(2)).toEqual('outerToggle.isOpen(): true');
* expect(repeatToggles.isOuterOpen(3)).toEqual('outerToggle.isOpen(): true');
* expect(ifToggle.isOuterOpen()).toEqual('outerToggle.isOpen(): true');
* });
*
* it('should toggle each repeat iteration separately', function() {
* var repeatToggles = new NgRepeatToggles();
*
* repeatToggles.forEach(function(repeatToggle) {
* expect(repeatToggle.isOpen()).toEqual('ngRepeatToggle.isOpen(): false');
* expect(repeatToggle.isOuterOpen()).toEqual('outerToggle.isOpen(): false');
* repeatToggle.toggle();
* expect(repeatToggle.isOpen()).toEqual('ngRepeatToggle.isOpen(): true');
* expect(repeatToggle.isOuterOpen()).toEqual('outerToggle.isOpen(): false');
* });
* });
* </file>
* </example>
*
*/
var ngRefDirective = ['$parse',function($parse) {
return {
priority: -1,
restrict: 'A',
compile: function(tElement, tAttrs) {
// gets the expected controller name, converts <data-some-thing> into "someThing"
var controllerName = directiveNormalize(nodeName_(tElement));
// get the expression for value binding
var getter = $parse(tAttrs.ngRef);
var setter = getter.assign;
return function(scope, element) {
// gets the controller of the current component or the current DOM element
var controller = element.data('$' + controllerName + 'Controller');
var value = controller || element[0];
setter(scope, value);
// when the element is removed, remove it (nullify it)
element.on('$destroy', function() {
// only remove it if value has not changed,
// carefully because animations (and other procedures) may duplicate elements
if (getter(scope) === value) {
setter(scope, null);
}
});
};
}
};
}];