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

Side by Side Diff: third_party/pkg/angular/lib/core/directive.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.core;
2
3 abstract class NgAnnotation {
4 /**
5 * CSS selector which will trigger this component/directive.
6 * CSS Selectors are limited to a single element and can contain:
7 *
8 * * `element-name` limit to a given element name.
9 * * `.class` limit to an element with a given class.
10 * * `[attribute]` limit to an element with a given attribute name.
11 * * `[attribute=value]` limit to an element with a given attribute and value.
12 *
13 *
14 * Example: `input[type=checkbox][ng-model]`
15 */
16 final String selector;
17
18 /**
19 * Specifies the compiler action to be taken on the child nodes of the
20 * element which this currently being compiled. The values are:
21 *
22 * * [COMPILE_CHILDREN] (*default*)
23 * * [TRANSCLUDE_CHILDREN]
24 * * [IGNORE_CHILDREN]
25 */
26 final String children;
27
28 /**
29 * Compile the child nodes of the element. This is the default.
30 */
31 static const String COMPILE_CHILDREN = 'compile';
32 /**
33 * Compile the child nodes for transclusion and makes available
34 * [BoundBlockFactory], [BlockFactory] and [BlockHole] for injection.
35 */
36 static const String TRANSCLUDE_CHILDREN = 'transclude';
37 /**
38 * Do not compile/visit the child nodes. Angular markup on descendant nodes
39 * will not be processed.
40 */
41 static const String IGNORE_CHILDREN = 'ignore';
42
43 /**
44 * A directive/component controller class can be injected into other
45 * directives/components. This attribute controls whether the
46 * controller is available to others.
47 *
48 * * `local` [NgDirective.LOCAL_VISIBILITY] - the controller can be injected
49 * into other directives / components on the same DOM element.
50 * * `children` [NgDirective.CHILDREN_VISIBILITY] - the controller can be
51 * injected into other directives / components on the same or child DOM
52 * elements.
53 * * `direct_children` [NgDirective.DIRECT_CHILDREN_VISIBILITY] - the
54 * controller can be injected into other directives / components on the
55 * direct children of the current DOM element.
56 */
57 final String visibility;
58 final List<Type> publishTypes;
59
60 /**
61 * Use map to define the mapping of DOM attributes to fields.
62 * The map's key is the DOM attribute name (DOM attribute is in dash-case).
63 * The Map's value consists of a mode prefix followed by an expression.
64 * The destination expression will be evaluated against the instance of the
65 * directive / component class.
66 *
67 * * `@` - Map the DOM attribute string. The attribute string will be taken
68 * literally or interpolated if it contains binding {{}} systax and assigned
69 * to the expression. (cost: 0 watches)
70 *
71 * * `=>` - Treat the DOM attribute value as an expression. Set up a watch,
72 * which will read the expression in the attribute and assign the value
73 * to destination expression. (cost: 1 watch)
74 *
75 * * `<=>` - Treat the DOM attribute value as an expression. Set up a watch
76 * on both outside as well as component scope to keep the src and
77 * destination in sync. (cost: 2 watches)
78 *
79 * * `=>!` - Treat the DOM attribute value as an expression. Set up a one time
80 * watch on expression. Once the expression turns truthy it will no longer
81 * update. (cost: 1 watches until not null, then 0 watches)
82 *
83 * * `&` - Treat the DOM attribute value as an expression. Assign a closure
84 * function into the field. This allows the component to control
85 * the invocation of the closure. This is useful for passing
86 * expressions into controllers which act like callbacks. (cost: 0 watches)
87 *
88 * Example:
89 *
90 * <my-component title="Hello {{username}}"
91 * selection="selectedItem"
92 * on-selection-change="doSomething()">
93 *
94 * @NgComponent(
95 * selector: 'my-component'
96 * map: const {
97 * 'title': '@title',
98 * 'selection': '<=>currentItem',
99 * 'on-selection-change': '&onChange'
100 * }
101 * )
102 * class MyComponent {
103 * String title;
104 * var currentItem;
105 * ParsedFn onChange;
106 * }
107 *
108 * The above example shows how all three mapping modes are used.
109 *
110 * * `@title` maps the title DOM attribute to the controller `title`
111 * field. Notice that this maps the content of the attribute, which
112 * means that it can be used with `{{}}` interpolation.
113 *
114 * * `<=>currentItem` maps the expression (in this case the `selectedItem`
115 * in the current scope into the `currentItem` in the controller. Notice
116 * that mapping is bi-directional. A change either in field or on
117 * parent scope will result in change to the other.
118 *
119 * * `&onChange` maps the expression into tho controllers `onChange`
120 * field. The result of mapping is a callable function which can be
121 * invoked at any time by the controller. The invocation of the
122 * callable function will result in the expression `doSomething()` to
123 * be executed in the parent context.
124 */
125 final Map<String, String> map;
126
127 /**
128 * Use the list to specify expression containing attributes which are not
129 * included under [map] with '=' or '@' specification.
130 */
131 final List<String> exportExpressionAttrs;
132
133 /**
134 * Use the list to specify a expressions which are evaluated dynamically
135 * (ex. via [Scope.$eval]) and are otherwise not statically discoverable.
136 */
137 final List<String> exportExpressions;
138
139 /**
140 * An expression under which the controller instance will be published into.
141 * This allows the expressions in the template to be referring to controller
142 * instance and its properties.
143 */
144 final String publishAs;
145
146 const NgAnnotation({
147 this.selector,
148 this.children: NgAnnotation.COMPILE_CHILDREN,
149 this.visibility: NgDirective.LOCAL_VISIBILITY,
150 this.publishAs,
151 this.publishTypes: const [],
152 this.map: const {},
153 this.exportExpressions: const [],
154 this.exportExpressionAttrs: const []
155 });
156
157 toString() => selector;
158 get hashCode => selector.hashCode;
159 operator==(other) =>
160 other is NgAnnotation && this.selector == other.selector;
161
162 NgAnnotation cloneWithNewMap(newMap);
163 }
164
165
166 /**
167 * Meta-data marker placed on a class which should act as a controller for the
168 * component. Angular components are a light-weight version of web-components.
169 * Angular components use shadow-DOM for rendering their templates.
170 *
171 * Angular components are instantiated using dependency injection, and can
172 * ask for any injectable object in their constructor. Components
173 * can also ask for other components or directives declared on the DOM element.
174 *
175 * Components can implement [NgAttachAware], [NgDetachAware], [NgShadowRootAware ] and
176 * declare these optional methods:
177 *
178 * * `attach()` - Called on first [Scope.$digest()].
179 * * `detach()` - Called on when owning scope is destroyed.
180 * * `onShadowRoot(ShadowRoot shadowRoot)` - Called when [ShadowRoot] is loaded.
181 */
182 class NgComponent extends NgAnnotation {
183 /**
184 * Inlined HTML template for the component.
185 */
186 final String template;
187
188 /**
189 * A URL to HTML template. This will be loaded asynchronously and
190 * cached for future component instances.
191 */
192 final String templateUrl;
193
194 /**
195 * A CSS URL to load into the shadow DOM.
196 */
197 final String cssUrl;
198
199 /**
200 * A list of CSS URLs to load into the shadow DOM.
201 */
202 final List<String> cssUrls;
203
204 List<String> get allCssUrls {
205 if (cssUrls == null && cssUrl == null) return null;
206 if (cssUrls == null && cssUrl != null) return [cssUrl];
207 if (cssUrls != null && cssUrl == null) return cssUrls;
208 if (cssUrls != null && cssUrl != null) return [cssUrl]..addAll(cssUrls);
209 }
210
211 /**
212 * Set the shadow root applyAuthorStyles property. See shadow-DOM
213 * documentation for further details.
214 */
215 final bool applyAuthorStyles;
216
217 /**
218 * Set the shadow root resetStyleInheritance property. See shadow-DOM
219 * documentation for further details.
220 */
221 final bool resetStyleInheritance;
222
223 const NgComponent({
224 this.template,
225 this.templateUrl,
226 this.cssUrl,
227 this.cssUrls,
228 this.applyAuthorStyles,
229 this.resetStyleInheritance,
230 publishAs,
231 map,
232 selector,
233 visibility,
234 publishTypes : const <Type>[],
235 exportExpressions,
236 exportExpressionAttrs
237 }) : super(selector: selector,
238 children: NgAnnotation.COMPILE_CHILDREN,
239 visibility: visibility,
240 publishTypes: publishTypes,
241 publishAs: publishAs,
242 map: map,
243 exportExpressions: exportExpressions,
244 exportExpressionAttrs: exportExpressionAttrs);
245
246 NgAnnotation cloneWithNewMap(newMap) =>
247 new NgComponent(
248 template: this.template,
249 templateUrl: this.templateUrl,
250 cssUrls: this.cssUrls,
251 applyAuthorStyles: this.applyAuthorStyles,
252 resetStyleInheritance: this.resetStyleInheritance,
253 publishAs: this.publishAs,
254 map: newMap,
255 selector: this.selector,
256 visibility: this.visibility,
257 publishTypes: this.publishTypes,
258 exportExpressions: this.exportExpressions,
259 exportExpressionAttrs: this.exportExpressionAttrs);
260 }
261
262 RegExp _ATTR_NAME = new RegExp(r'\[([^\]]+)\]$');
263
264 /**
265 * Meta-data marker placed on a class which should act as a directive.
266 *
267 * Angular directives are instantiated using dependency injection, and can
268 * ask for any injectable object in their constructor. Directives
269 * can also ask for other components or directives declared on the DOM element.
270 *
271 * Directives can implement [NgAttachAware], [NgDetachAware] and
272 * declare these optional methods:
273 *
274 * * `attach()` - Called on first [Scope.$digest()].
275 * * `detach()` - Called on when owning scope is destroyed.
276 */
277 class NgDirective extends NgAnnotation {
278 static const String LOCAL_VISIBILITY = 'local';
279 static const String CHILDREN_VISIBILITY = 'children';
280 static const String DIRECT_CHILDREN_VISIBILITY = 'direct_children';
281
282 const NgDirective({
283 children: NgAnnotation.COMPILE_CHILDREN,
284 publishAs,
285 map,
286 selector,
287 visibility,
288 publishTypes : const <Type>[],
289 exportExpressions,
290 exportExpressionAttrs
291 }) : super(selector: selector, children: children, visibilit y: visibility,
292 publishTypes: publishTypes, publishAs: publishAs, map: map,
293 exportExpressions: exportExpressions,
294 exportExpressionAttrs: exportExpressionAttrs);
295
296 NgAnnotation cloneWithNewMap(newMap) =>
297 new NgDirective(
298 children: this.children,
299 publishAs: this.publishAs,
300 map: newMap,
301 selector: this.selector,
302 visibility: this.visibility,
303 publishTypes: this.publishTypes,
304 exportExpressions: this.exportExpressions,
305 exportExpressionAttrs: this.exportExpressionAttrs);
306 }
307
308 /**
309 * Meta-data marker placed on a class which should act as a controller for your application.
310 *
311 * Controllers are essentially [NgDirectives] with few key differences:
312 *
313 * * Controllers create a new scope at the element.
314 * * Controllers should not do any DOM manipulation.
315 * * Controllers are meant for application-logic
316 * (rather then DOM monipulation logic which directives are meant for.)
317 *
318 * Controllers can implement [NgAttachAware], [NgDetachAware] and
319 * declare these optional methods:
320 *
321 * * `attach()` - Called on first [Scope.$digest()].
322 * * `detach()` - Called on when owning scope is destroyed.
323 */
324 class NgController extends NgDirective {
325 static const String LOCAL_VISIBILITY = 'local';
326 static const String CHILDREN_VISIBILITY = 'children';
327 static const String DIRECT_CHILDREN_VISIBILITY = 'direct_children';
328
329 const NgController({
330 children: NgAnnotation.COMPILE_CHILDREN,
331 publishAs,
332 map,
333 selector,
334 visibility,
335 publishTypes : const <Type>[],
336 exportExpressions,
337 exportExpressionAttrs
338 }) : super(selector: selector, children: children, visibilit y: visibility,
339 publishTypes: publishTypes, publishAs: publishAs, map: map,
340 exportExpressions: exportExpressions,
341 exportExpressionAttrs: exportExpressionAttrs);
342
343 NgAnnotation cloneWithNewMap(newMap) =>
344 new NgController(
345 children: this.children,
346 publishAs: this.publishAs,
347 map: newMap,
348 selector: this.selector,
349 visibility: this.visibility,
350 publishTypes: this.publishTypes,
351 exportExpressions: this.exportExpressions,
352 exportExpressionAttrs: this.exportExpressionAttrs);
353 }
354
355 abstract class AttrFieldAnnotation {
356 final String attrName;
357 const AttrFieldAnnotation(this.attrName);
358 String get mappingSpec;
359 }
360
361 /**
362 * When applied as an annotation on a directive field specifies that
363 * the field is to be mapped to DOM attribute with the provided [attrName].
364 * The value of the attribute to be treated as a string, equivalent
365 * to `@` specification.
366 */
367 class NgAttr extends AttrFieldAnnotation {
368 final mappingSpec = '@';
369 const NgAttr(String attrName) : super(attrName);
370 }
371
372 /**
373 * When applied as an annotation on a directive field specifies that
374 * the field is to be mapped to DOM attribute with the provided [attrName].
375 * The value of the attribute to be treated as a one-way expession, equivalent
376 * to `=>` specification.
377 */
378 class NgOneWay extends AttrFieldAnnotation {
379 final mappingSpec = '=>';
380 const NgOneWay(String attrName) : super(attrName);
381 }
382
383 /**
384 * When applied as an annotation on a directive field specifies that
385 * the field is to be mapped to DOM attribute with the provided [attrName].
386 * The value of the attribute to be treated as a one time one-way expession,
387 * equivalent to `=>!` specification.
388 */
389 class NgOneWayOneTime extends AttrFieldAnnotation {
390 final mappingSpec = '=>!';
391 const NgOneWayOneTime(String attrName) : super(attrName);
392 }
393
394 /**
395 * When applied as an annotation on a directive field specifies that
396 * the field is to be mapped to DOM attribute with the provided [attrName].
397 * The value of the attribute to be treated as a two-way expession,
398 * equivalent to `<=>` specification.
399 */
400 class NgTwoWay extends AttrFieldAnnotation {
401 final mappingSpec = '<=>';
402 const NgTwoWay(String attrName) : super(attrName);
403 }
404
405 /**
406 * When applied as an annotation on a directive field specifies that
407 * the field is to be mapped to DOM attribute with the provided [attrName].
408 * The value of the attribute to be treated as a callback expession,
409 * equivalent to `&` specification.
410 */
411 class NgCallback extends AttrFieldAnnotation {
412 final mappingSpec = '&';
413 const NgCallback(String attrName) : super(attrName);
414 }
415
416 /**
417 * Implementing directives or components [attach] method will be called when
418 * the next scope digest occurs after component instantiation. It is guaranteed
419 * that when [attach] is invoked, that all attribute mappings have already
420 * been processed.
421 */
422 abstract class NgAttachAware {
423 void attach();
424 }
425
426 /**
427 * Implementing directives or components [detach] method will be called when
428 * the associated scope is destroyed.
429 */
430 abstract class NgDetachAware {
431 void detach();
432 }
433
434 @NgInjectableService()
435 class DirectiveMap extends AnnotationMap<NgAnnotation> {
436 DirectiveMap(Injector injector, MetadataExtractor metadataExtractor,
437 FieldMetadataExtractor fieldMetadataExtractor)
438 : super(injector, metadataExtractor) {
439 Map<NgAnnotation, Type> directives = {};
440 forEach((NgAnnotation annotation, Type type) {
441 var match;
442 var fieldMetadata = fieldMetadataExtractor(type);
443 if (fieldMetadata.isNotEmpty) {
444 var newMap = annotation.map == null ? {} : new Map.from(annotation.map);
445 fieldMetadata.forEach((String fieldName, AttrFieldAnnotation ann) {
446 var attrName = ann.attrName;
447 if (newMap.containsKey(attrName)) {
448 throw 'Mapping for attribute $attrName is already defined (while '
449 'processing annottation for field $fieldName of $type)';
450 }
451 newMap[attrName] = '${ann.mappingSpec}$fieldName';
452 });
453 annotation = annotation.cloneWithNewMap(newMap);
454 }
455 directives[annotation] = type;
456 });
457 _map.clear();
458 _map.addAll(directives);
459 }
460 }
461
462 @NgInjectableService()
463 class FieldMetadataExtractor {
464 List<TypeMirror> _fieldAnnotations = [reflectType(NgAttr),
465 reflectType(NgOneWay), reflectType(NgOneWayOneTime),
466 reflectType(NgTwoWay), reflectType(NgCallback)];
467
468 Map<String, AttrFieldAnnotation> call(Type type) {
469 ClassMirror cm = reflectType(type);
470 Map<String, AttrFieldAnnotation> fields = <String, AttrFieldAnnotation>{};
471 cm.declarations.forEach((Symbol name, DeclarationMirror decl) {
472 if (decl is VariableMirror ||
473 (decl is MethodMirror && (decl.isGetter || decl.isSetter))) {
474 var fieldName = MirrorSystem.getName(name);
475 if (decl is MethodMirror && decl.isSetter) {
476 // Remove = from the end of the setter.
477 fieldName = fieldName.substring(0, fieldName.length - 1);
478 }
479 decl.metadata.forEach((InstanceMirror meta) {
480 if (_fieldAnnotations.contains(meta.type)) {
481 if (fields[fieldName] != null) {
482 throw 'Attribute annotation for $fieldName is defined more '
483 'than once in $type';
484 }
485 fields[fieldName] = meta.reflectee as AttrFieldAnnotation;
486 }
487 });
488 }
489 });
490 return fields;
491 }
492 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698