feat(core): add support for @HostBinding and @HostListener
Example: @Directive({selector: 'my-directive'}) class MyDirective { @HostBinding("attr.my-attr") myAttr: string; @HostListener("click", ["$event.target"]) onClick(target) { this.target = target; } } Closes #3996
This commit is contained in:
@ -1,11 +1,13 @@
|
||||
import {resolveForwardRef, Injectable} from 'angular2/di';
|
||||
import {Type, isPresent, BaseException, stringify} from 'angular2/src/core/facade/lang';
|
||||
import {ListWrapper, StringMapWrapper} from 'angular2/src/core/facade/collection';
|
||||
import {ListWrapper, StringMap, StringMapWrapper} from 'angular2/src/core/facade/collection';
|
||||
import {
|
||||
DirectiveMetadata,
|
||||
ComponentMetadata,
|
||||
PropertyMetadata,
|
||||
EventMetadata
|
||||
EventMetadata,
|
||||
HostBindingMetadata,
|
||||
HostListenerMetadata
|
||||
} from 'angular2/metadata';
|
||||
import {reflector} from 'angular2/src/core/reflection/reflection';
|
||||
|
||||
@ -40,6 +42,7 @@ export class DirectiveResolver {
|
||||
StringMap<string, any[]>): DirectiveMetadata {
|
||||
var properties = [];
|
||||
var events = [];
|
||||
var host = {};
|
||||
|
||||
StringMapWrapper.forEach(propertyMetadata, (metadata: any[], propName: string) => {
|
||||
metadata.forEach(a => {
|
||||
@ -58,23 +61,37 @@ export class DirectiveResolver {
|
||||
events.push(propName);
|
||||
}
|
||||
}
|
||||
|
||||
if (a instanceof HostBindingMetadata) {
|
||||
if (isPresent(a.hostPropertyName)) {
|
||||
host[`[${a.hostPropertyName}]`] = propName;
|
||||
} else {
|
||||
host[`[${propName}]`] = propName;
|
||||
}
|
||||
}
|
||||
|
||||
if (a instanceof HostListenerMetadata) {
|
||||
var args = isPresent(a.args) ? a.args.join(', ') : '';
|
||||
host[`(${a.eventName})`] = `${propName}(${args})`;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return this._merge(dm, properties, events);
|
||||
return this._merge(dm, properties, events, host);
|
||||
}
|
||||
|
||||
private _merge(dm: DirectiveMetadata, properties: string[], events: string[]): DirectiveMetadata {
|
||||
private _merge(dm: DirectiveMetadata, properties: string[], events: string[],
|
||||
host: StringMap<string, string>): DirectiveMetadata {
|
||||
var mergedProperties =
|
||||
isPresent(dm.properties) ? ListWrapper.concat(dm.properties, properties) : properties;
|
||||
var mergedEvents = isPresent(dm.events) ? ListWrapper.concat(dm.events, events) : events;
|
||||
var mergedHost = isPresent(dm.host) ? StringMapWrapper.merge(dm.host, host) : host;
|
||||
|
||||
if (dm instanceof ComponentMetadata) {
|
||||
return new ComponentMetadata({
|
||||
selector: dm.selector,
|
||||
properties: mergedProperties,
|
||||
events: mergedEvents,
|
||||
host: dm.host,
|
||||
host: mergedHost,
|
||||
lifecycle: dm.lifecycle,
|
||||
bindings: dm.bindings,
|
||||
exportAs: dm.exportAs,
|
||||
@ -88,7 +105,7 @@ export class DirectiveResolver {
|
||||
selector: dm.selector,
|
||||
properties: mergedProperties,
|
||||
events: mergedEvents,
|
||||
host: dm.host,
|
||||
host: mergedHost,
|
||||
lifecycle: dm.lifecycle,
|
||||
bindings: dm.bindings,
|
||||
exportAs: dm.exportAs,
|
||||
|
@ -112,3 +112,19 @@ class Event extends EventMetadata {
|
||||
const Event([String bindingPropertyName])
|
||||
: super(bindingPropertyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* See: [HostBindingMetadata] for docs.
|
||||
*/
|
||||
class HostBinding extends HostBindingMetadata {
|
||||
const HostBinding([String hostPropertyName])
|
||||
: super(hostPropertyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* See: [HostListenerMetadata] for docs.
|
||||
*/
|
||||
class HostListener extends HostListenerMetadata {
|
||||
const HostListener(String eventName, [List<String> args])
|
||||
: super(eventName, args);
|
||||
}
|
@ -15,7 +15,9 @@ export {
|
||||
PipeMetadata,
|
||||
LifecycleEvent,
|
||||
PropertyMetadata,
|
||||
EventMetadata
|
||||
EventMetadata,
|
||||
HostBindingMetadata,
|
||||
HostListenerMetadata
|
||||
} from './metadata/directives';
|
||||
|
||||
export {ViewMetadata, ViewEncapsulation} from './metadata/view';
|
||||
@ -33,7 +35,9 @@ import {
|
||||
PipeMetadata,
|
||||
LifecycleEvent,
|
||||
PropertyMetadata,
|
||||
EventMetadata
|
||||
EventMetadata,
|
||||
HostBindingMetadata,
|
||||
HostListenerMetadata
|
||||
} from './metadata/directives';
|
||||
|
||||
import {ViewMetadata, ViewEncapsulation} from './metadata/view';
|
||||
@ -447,6 +451,45 @@ export interface EventFactory {
|
||||
new (bindingPropertyName?: string): any;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link HostBindingMetadata} factory for creating decorators.
|
||||
*
|
||||
* ## Example as TypeScript Decorator
|
||||
*
|
||||
* ```
|
||||
* @Directive({
|
||||
* selector: 'sample-dir'
|
||||
* })
|
||||
* class SampleDir {
|
||||
* @HostBinding() prop1; // Same as @HostBinding('prop1') prop1;
|
||||
* @HostBinding("el-prop") prop1;
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export interface HostBindingFactory {
|
||||
(hostPropertyName?: string): any;
|
||||
new (hostPropertyName?: string): any;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link HostListenerMetadata} factory for creating decorators.
|
||||
*
|
||||
* ## Example as TypeScript Decorator
|
||||
*
|
||||
* ```
|
||||
* @Directive({
|
||||
* selector: 'sample-dir'
|
||||
* })
|
||||
* class SampleDir {
|
||||
* @HostListener("change", ['$event.target.value']) onChange(value){}
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export interface HostListenerFactory {
|
||||
(eventName: string, args?: string[]): any;
|
||||
new (eventName: string, args?: string[]): any;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ComponentMetadata} factory function.
|
||||
*/
|
||||
@ -492,4 +535,14 @@ export var Property: PropertyFactory = makePropDecorator(PropertyMetadata);
|
||||
/**
|
||||
* {@link EventMetadata} factory function.
|
||||
*/
|
||||
export var Event: EventFactory = makePropDecorator(EventMetadata);
|
||||
export var Event: EventFactory = makePropDecorator(EventMetadata);
|
||||
|
||||
/**
|
||||
* {@link HostBindingMetadata} factory function.
|
||||
*/
|
||||
export var HostBinding: HostBindingFactory = makePropDecorator(HostBindingMetadata);
|
||||
|
||||
/**
|
||||
* {@link HostListenerMetadata} factory function.
|
||||
*/
|
||||
export var HostListener: HostListenerFactory = makePropDecorator(HostListenerMetadata);
|
@ -1109,4 +1109,68 @@ export class PropertyMetadata {
|
||||
@CONST()
|
||||
export class EventMetadata {
|
||||
constructor(public bindingPropertyName?: string) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Declare a host property binding.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```
|
||||
* @Directive({
|
||||
* selector: 'sample-dir'
|
||||
* })
|
||||
* class SampleDir {
|
||||
* @HostBinding() prop1; // Same as @HostBinding('prop1') prop1;
|
||||
* @HostBinding("el-prop") prop2;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* This is equivalent to
|
||||
*
|
||||
* ```
|
||||
* @Directive({
|
||||
* selector: 'sample-dir',
|
||||
* host: {'[prop1]': 'prop1', '[el-prop]': 'prop2'}
|
||||
* })
|
||||
* class SampleDir {
|
||||
* prop1;
|
||||
* prop2;
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
@CONST()
|
||||
export class HostBindingMetadata {
|
||||
constructor(public hostPropertyName?: string) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Declare a host listener.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```
|
||||
* @Directive({
|
||||
* selector: 'sample-dir'
|
||||
* })
|
||||
* class SampleDir {
|
||||
* @HostListener("change", ['$event.target.value']) onChange(value){}
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* This is equivalent to
|
||||
*
|
||||
* ```
|
||||
* @Directive({
|
||||
* selector: 'sample-dir',
|
||||
* host: {'(change)': 'onChange($event.target.value)'}
|
||||
* })
|
||||
* class SampleDir {
|
||||
* onChange(value){}
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
@CONST()
|
||||
export class HostListenerMetadata {
|
||||
constructor(public eventName: string, public args?: string[]) {}
|
||||
}
|
Reference in New Issue
Block a user