Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(725)

Side by Side Diff: third_party/pkg/angular/lib/directive/ng_model.dart

Issue 124053002: Adding Angular and dependent packages for testing (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 part of angular.directive;
2
3 /**
4 * Ng-model directive is responsible for reading/writing to the model.
5 * The directive itself is headless. (It does not know how to render or what
6 * events to listen for.) It is meant to be used with other directives which
7 * provide the rendering and listening capabilities. The directive itself
8 * knows how to convert the view-value into model-value and vice versa by
9 * allowing others to register converters (To be implemented). It also
10 * knwos how to (in)validate the model and the form in which it is declared
11 * (to be implemented)
12 */
13 @NgDirective(
14 selector: '[ng-model]',
15 map: const {'ng-model': '&model'})
16 class NgModel {
17 final Scope _scope;
18
19 Getter getter = ([_]) => null;
20 Setter setter = (_, [__]) => null;
21 String _exp;
22
23
24 Function _removeWatch = () => null;
25 bool _watchCollection;
26
27 Function render = (value) => null;
28
29 NgModel(this._scope, NodeAttrs attrs) {
30 _exp = 'ng-model=${attrs["ng-model"]}';
31 watchCollection = false;
32 }
33
34 get watchCollection => _watchCollection;
35 set watchCollection(value) {
36 if (_watchCollection == value) return;
37 _watchCollection = value;
38 _removeWatch();
39 if (_watchCollection) {
40 _removeWatch = _scope.$watchCollection((s) => getter(), (value) => render( value), _exp);
41 } else {
42 _removeWatch = _scope.$watch((s) => getter(), (value) => render(value), _e xp);
43 }
44 }
45
46 set model(BoundExpression boundExpression) {
47 getter = boundExpression;
48 setter = boundExpression.assign;
49 }
50
51 // TODO(misko): right now viewValue and modelValue are the same,
52 // but this needs to be changed to support converters and form validation
53 get viewValue => modelValue;
54 set viewValue(value) => modelValue = value;
55
56 get modelValue => getter();
57 set modelValue(value) => setter(value);
58 }
59
60 /**
61 * Usage:
62 *
63 * <input type="checkbox" ng-model="flag">
64 *
65 * This creates a two way databinding between the boolean expression specified i n
66 * ng-model and the checkbox input element in the DOM.  If the ng-model value is
67 * falsy (i.e. one of `false`, `null`, and `0`), then the checkbox is unchecked.
68 * Otherwise, it is checked.  Likewise, when the checkbox is checked, the model
69 * value is set to true. When unchecked, it is set to false.
70 *
71 * The AngularJS style ng-true-value / ng-false-value is not supported.
72 */
73 @NgDirective(selector: 'input[type=checkbox][ng-model]')
74 class InputCheckboxDirective {
75 dom.InputElement inputElement;
76 NgModel ngModel;
77 Scope scope;
78
79 InputCheckboxDirective(dom.Element this.inputElement, this.ngModel, this.scope ) {
80 ngModel.render = (value) {
81 inputElement.checked = value == null ? false : toBool(value);
82 };
83 inputElement.onChange.listen((value) {
84 scope.$apply(() => ngModel.viewValue = inputElement.checked);
85 });
86 }
87 }
88
89
90 abstract class _InputTextlikeDirective {
91 dom.Element inputElement;
92 NgModel ngModel;
93 Scope scope;
94
95 get typedValue => (inputElement as dynamic).value;
96 set typedValue(String value) => (inputElement as dynamic).value = (value == nu ll) ? '' : value;
97
98 _InputTextlikeDirective(dom.Element this.inputElement, this.ngModel, this.scop e) {
99 ngModel.render = (value) {
100 if (value == null) value = '';
101
102 var currentValue = typedValue;
103 if (value != currentValue && !(value is num && currentValue is num && valu e.isNaN && currentValue.isNaN)) {
104 typedValue = value;
105 }
106 };
107 inputElement.onChange.listen(relaxFnArgs(processValue));
108 inputElement.onKeyDown.listen((e) {
109 new async.Timer(Duration.ZERO, processValue);
110 scope.$skipAutoDigest();
111 });
112 }
113
114 processValue() {
115 var value = typedValue;
116 if (value != ngModel.viewValue) {
117 scope.$apply(() => ngModel.viewValue = value);
118 }
119 }
120 }
121
122 /**
123 * Usage:
124 *
125 * <input type="text" ng-model="name">
126 *
127 * This creates a two way databinding between the expression specified in
128 * ng-model and the text input element in the DOM.  If the ng-model value is
129 * `null`, it is treated as equivalent to the empty string for rendering
130 * purposes.
131 */
132 @NgDirective(selector: 'input[type=text][ng-model]')
133 class InputTextDirective extends _InputTextlikeDirective {
134 InputTextDirective(dom.Element inputElement, NgModel ngModel, Scope scope):
135 super(inputElement, ngModel, scope);
136
137 }
138
139 /**
140 * Usage:
141 *
142 * <input type="password" ng-model="name">
143 *
144 * This creates a two way databinding between the expression specified in
145 * ng-model and the password input element in the DOM.  If the ng-model value is
146 * `null`, it is treated as equivalent to the empty string for rendering
147 * purposes.
148 */
149 @NgDirective(selector: 'input[type=password][ng-model]')
150 class InputPasswordDirective extends _InputTextlikeDirective {
151 InputPasswordDirective(dom.Element inputElement, NgModel ngModel, Scope scope) :
152 super(inputElement, ngModel, scope);
153 }
154
155 /**
156 * Usage:
157 *
158 * <textarea ng-model="text">
159 *
160 * This creates a two way databinding between the expression specified in
161 * ng-model and the textarea element in the DOM.  If the ng-model value is
162 * `null`, it is treated as equivalent to the empty string for rendering
163 * purposes.
164 */
165 @NgDirective(selector: 'textarea[ng-model]')
166 class TextAreaDirective extends _InputTextlikeDirective {
167 TextAreaDirective(dom.Element inputElement, NgModel ngModel, Scope scope):
168 super(inputElement, ngModel, scope);
169 }
170
171 /**
172 * Usage:
173 *
174 * <input type="number" ng-model="name">
175 *
176 * This creates a two way databinding between the expression specified in
177 * ng-model and the number input element in the DOM.  If the ng-model value is
178 * `null` or `NaN`, the DOM element is not updated. If the value in the DOM
179 * element is an invalid number, then the expression specified by the `ng-model`
180 * is set to null.,
181 */
182 @NgDirective(selector: 'input[type=number][ng-model]')
183 class InputNumberDirective extends _InputTextlikeDirective {
184 InputNumberDirective(dom.Element inputElement, NgModel ngModel, Scope scope):
185 super(inputElement, ngModel, scope);
186
187 get typedValue => (inputElement as dom.InputElement).valueAsNumber;
188
189 set typedValue(var value) {
190 if (value != null && value is num) {
191 num number = value as num;
192 if (!value.isNaN) {
193 (inputElement as dom.InputElement).valueAsNumber = value;
194 }
195 }
196 }
197 }
198
199 /**
200 * Usage:
201 *
202 * <input type="email" ng-model="emailAddress">
203 *
204 * This creates a two way databinding between the expression specified in
205 * ng-model and the email input element in the DOM.  If the ng-model value is
206 * `null`, the DOM element is not updated. If the value in the DOM element is
207 * an invalid e-mail address, then the expression specified by the `ng-model` is
208 * set to null.,
209 */
210 @NgDirective(selector: 'input[type=email][ng-model]')
211 class InputEmailDirective extends _InputTextlikeDirective {
212 static final EMAIL_REGEXP = new RegExp(
213 r'^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}$');
214 InputEmailDirective(dom.Element inputElement, NgModel ngModel, Scope scope):
215 super(inputElement, ngModel, scope);
216
217 String get typedValue {
218 String value = (inputElement as dom.InputElement).value;
219 return EMAIL_REGEXP.hasMatch(value) ? value : null;
220 }
221
222 set typedValue(String value) {
223 if (value != null && EMAIL_REGEXP.hasMatch(value)) {
224 (inputElement as dom.InputElement).value = value;
225 }
226 }
227 }
228
229
230 /**
231 * Usage:
232 *
233 * <input type="url" ng-model="website">
234 *
235 * This creates a two way databinding between the expression specified in
236 * ng-model and the `url` input element in the DOM.  If the ng-model value is
237 * `null`, the DOM element is not updated. If the value in the DOM element is
238 * an invalid URL, then the expression specified by the `ng-model` is set to
239 * null.,
240 */
241 @NgDirective(selector: 'input[type=url][ng-model]')
242 class InputUrlDirective extends _InputTextlikeDirective {
243 static final URL_REGEXP = new RegExp(
244 r'^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?' +
245 r'(\/|\/([\w#!:.?+=&%@!\-\/]))?$');
246 InputUrlDirective(dom.Element inputElement, NgModel ngModel, Scope scope):
247 super(inputElement, ngModel, scope);
248
249 String get typedValue {
250 String value = (inputElement as dom.InputElement).value;
251 return URL_REGEXP.hasMatch(value) ? value : null;
252 }
253
254 set typedValue(String value) {
255 if (value != null && URL_REGEXP.hasMatch(value)) {
256 (inputElement as dom.InputElement).value = value;
257 }
258 }
259 }
260
261 class _UidCounter {
262 static final int CHAR_0 = "0".codeUnitAt(0);
263 static final int CHAR_9 = "9".codeUnitAt(0);
264 static final int CHAR_A = "A".codeUnitAt(0);
265 static final int CHAR_Z = "Z".codeUnitAt(0);
266 List charCodes = [CHAR_0, CHAR_0, CHAR_0];
267
268 String next() {
269 for (int i = charCodes.length-1; i >= 0; i--) {
270 int code = charCodes[i];
271 if (code == CHAR_9) {
272 charCodes[i] = CHAR_A;
273 return new String.fromCharCodes(charCodes);
274 } else if (code == CHAR_Z) {
275 charCodes[i] = CHAR_0;
276 } else {
277 charCodes[i] = code + 1;
278 return new String.fromCharCodes(charCodes);
279 }
280 }
281 charCodes.insert(0, CHAR_0);
282 return new String.fromCharCodes(charCodes);
283 }
284 }
285
286 final _uidCounter = new _UidCounter();
287
288
289 /**
290 * Usage:
291 *
292 * <input type="radio" ng-model="category">
293 *
294 * This creates a two way databinding between the expression specified in
295 * ng-model and the range input elements in the DOM.  If the ng-model value is
296 * set to a value not corresponding to one of the radio elements, then none of
297 * the radio elements will be check. Otherwise, only the corresponding input
298 * element in the group is checked. Likewise, when a radio button element is
299 * checked, the model is updated with its value. Radio buttons that have a
300 * `name` attribute are left alone. Those that are missing the attribute will
301 * have a unique `name` assigned to them. This sequence goes `001`, `001`, ...
302 * `009`, `00A`, `00Z`, `010`, … and so on using more than 3 characters for the
303 * name when the counter overflows.
304 */
305 @NgDirective(selector: 'input[type=radio][ng-model]')
306 class InputRadioDirective {
307 dom.RadioButtonInputElement radioButtonElement;
308 NgModel ngModel;
309 Scope scope;
310
311 InputRadioDirective(dom.Element this.radioButtonElement, this.ngModel,
312 this.scope, NodeAttrs attrs) {
313 // If there's no "name" set, we'll set a unique name. This ensures
314 // less surprising behavior about which radio buttons are grouped together.
315 if (attrs['name'] == '' || attrs['name'] == null) {
316 attrs["name"] = _uidCounter.next();
317 }
318 ngModel.render = (String value) {
319 radioButtonElement.checked = (value == radioButtonElement.value);
320 };
321 radioButtonElement.onClick.listen((_) {
322 if (radioButtonElement.checked) {
323 scope.$apply(() => ngModel.viewValue = radioButtonElement.value);
324 }
325 });
326 }
327 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698