diff --git a/modules/angular2/src/core/annotations/annotations.js b/modules/angular2/src/core/annotations/annotations.js index 099c416fdc..c250910616 100644 --- a/modules/angular2/src/core/annotations/annotations.js +++ b/modules/angular2/src/core/annotations/annotations.js @@ -1,34 +1,173 @@ import {ABSTRACT, CONST, normalizeBlank, isPresent} from 'angular2/src/facade/lang'; import {ListWrapper, List} from 'angular2/src/facade/collection'; +// type StringMap = {[idx: string]: string}; + /** - * Directives allow you to attach behavior to the DOM elements. Directive is an abstract concept, instead use concrete - * directives such as: [Component], [Decorator] or [Viewport]. + * Directives allow you to attach behavior to the DOM elements. + * + * Directive is an abstract concept, instead use concrete directives such as: [Component], [Decorator] or [Viewport]. */ @ABSTRACT() export class Directive { - selector:any; //string; - bind:any; - implementsTypes:any; //List; - lifecycle:any; //List - events:any; //List + /** + * The CSS selector that triggers the instantiation of a directive. + * + * Angular only allows directives to trigger on CSS selectors that do not cross element boundaries. + * The supported selectors are: + * + * - `element-name` select by element name. + * - `.class` select by class name. + * - `[attribute]` select by attribute name. + * - `[attribute=value]` select by attribute name and value. + * - `:not(sub_selector)` select only if the element does not match the `sub_selector`. + * + * ## Example + * + * Suppose we have a directive with an `input[type=text]` selector. + * + * And the following HTML: + * + *
+ * + * + * + * + * The directive would only be instantiated on the `` element. + * + */ + selector:string; + + /** + * Enumerates the set of properties that accept data binding for a directive. + * + * The `bind` property defines a set of `directiveProperty` to `bindingProperty` key-value pairs: + * + * - `directiveProperty` specifies the component property where the value is written. + * - `bindingProperty` specifies the DOM property where the value is read from. + * + * You can include [Pipes] when specifying a `bindingProperty` to allow for data transformation and structural + * change detection of the value. + * + * ## Syntax + * @Directive({ + * bind: { + * 'directiveProperty1': 'bindingProperty1', + * 'directiveProperty2': 'bindingProperty2 | pipe1 | ...', + * ... + * } + * } + * + * + * ## Basic Property Binding: + * + * @Decorator({ + * selector: '[tooltip]', + * bind: { + * 'tooltipText': 'tooltip' + * } + * }) + * class Tooltip { + * set tooltipText(text) { + * // This will get called every time the 'tooltip' binding changes with the new value. + * } + * } + * + * As used in this example: + * + *
+ * + * Whenever the `someExpression` expression changes, the `bind` declaration instructs Angular to update the + * `Tooltip`'s `tooltipText` property. + * + * + * Similarly in this example: + * + *
+ * + * The `Tooltip`'s `tooltipText` property gets initialized to the `Some Text` literal. + * + * + * ## Bindings With Pipes: + * + * @Decorator({ + * selector: '[class-set]', + * bind: { + * 'classChanges': 'classSet | keyValDiff' + * } + * }) + * class ClassSet { + * set classChanges(changes:KeyValueChanges) { + * // This will get called every time the `class-set` expressions changes its structure. + * } + * } + * + * As used in this example: + *
+ * + * In the above example, the `ClassSet` uses the `keyValDiff` [Pipe] for watching structural changes. This means that + * the `classChanges` setter gets invoked if the expression changes to a different reference, or if the + * structure of the expression changes. (Shallow property watching of the object) + * + * NOTE: The `someExpression` can also contain its own [Pipe]s. In this case, the two pipes compose as if they were + * inlined. + * + */ + bind:any; // StringMap + + /** + * Specifies which DOM events the directive listens to and what the action should be. + * + * The `event` property defines a set of `event` to `method` key-value pairs: + * + * - `event` specifies the DOM event that the directive listens to. + * - `onMethod` specifies the method to execute when the event occurs. + * + * + * ## Syntax + * @Directive({ + * events: { + * 'event1': 'onMethod', + * ... + * } + * } + * + * ## Basic Event Binding: + * + * @Decorator({ + * selector: 'input', + * event: { + * 'change': 'onChange' + * } + * }) + * class InputDecorator { + * onChange(event:Event) { + * // invoked whenever the DOM element fires the 'change' event. + * } + * } + * + */ + events:any; // StringMap + + /** + * Specifies a set of lifecycle events in which the directive participates. + */ + lifecycle:any; //List + @CONST() constructor({ selector, bind, events, - implementsTypes, lifecycle }:{ selector:string, bind:any, events: any, - implementsTypes:List, lifecycle:List }={}) { this.selector = selector; - this.implementsTypes = implementsTypes; this.bind = bind; this.events = events; this.lifecycle = lifecycle; @@ -49,14 +188,12 @@ export class Component extends Directive { bind, events, services, - implementsTypes, lifecycle }:{ selector:String, bind:Object, events:Object, services:List, - implementsTypes:List, lifecycle:List }={}) { @@ -64,7 +201,6 @@ export class Component extends Directive { selector: selector, bind: bind, events: events, - implementsTypes: implementsTypes, lifecycle: lifecycle }); @@ -79,14 +215,12 @@ export class Decorator extends Directive { selector, bind, events, - implementsTypes, lifecycle, compileChildren = true, }:{ selector:string, bind:any, events:any, - implementsTypes:List, lifecycle:List, compileChildren:boolean }={}) @@ -96,7 +230,6 @@ export class Decorator extends Directive { selector: selector, bind: bind, events: events, - implementsTypes: implementsTypes, lifecycle: lifecycle }); } @@ -108,12 +241,10 @@ export class Viewport extends Directive { selector, bind, events, - implementsTypes, lifecycle }:{ selector:string, bind:any, - implementsTypes:List, lifecycle:List }={}) { @@ -121,11 +252,56 @@ export class Viewport extends Directive { selector: selector, bind: bind, events: events, - implementsTypes: implementsTypes, lifecycle: lifecycle }); } } +//TODO(misko): turn into LifecycleEvent class once we switch to TypeScript; + +/** + * Specify that a directive should be notified whenever a [View] that contains it is destroyed. + * + * ## Example + * + * @Decorator({ + * ..., + * lifecycle: [ onDestroy ] + * }) + * class ClassSet implements OnDestroy { + * onDestroy() { + * // invoked to notify directive of the containing view destruction. + * } + * } + */ export const onDestroy = "onDestroy"; + + +/** + * Specify that a directive should be notified when any of its bindings have changed. + * + * ## Example: + * + * @Decorator({ + * selector: '[class-set]', + * bind: { + * 'propA': 'propA' + * 'propB': 'propB' + * } + * }) + * class ClassSet { + * propA; + * propB; + * + * onChange(changes:{[idx: string, PropertyUpdate]}) { + * // This will get called after any of the properties have been updated. + * if (changes['propA']) { + * // if propA was updated + * } + * if (changes['propA']) { + * // if propB was updated + * } + * } + * } + */ export const onChange = "onChange";