feat(view): reimplemented property setters using change detection
This commit is contained in:
@ -1,7 +1,7 @@
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {SetterFn} from 'angular2/src/reflection/types';
|
||||
import {AST} from './parser/ast';
|
||||
import {DirectiveRecord} from './directive_record';
|
||||
import {DirectiveIndex, DirectiveRecord} from './directive_record';
|
||||
|
||||
const DIRECTIVE="directive";
|
||||
const ELEMENT="element";
|
||||
@ -11,14 +11,16 @@ export class BindingRecord {
|
||||
mode:string;
|
||||
ast:AST;
|
||||
|
||||
implicitReceiver:any; //number | DirectiveIndex
|
||||
elementIndex:number;
|
||||
propertyName:string;
|
||||
setter:SetterFn;
|
||||
|
||||
directiveRecord:DirectiveRecord;
|
||||
|
||||
constructor(mode:string, ast:AST, elementIndex:number, propertyName:string, setter:SetterFn, directiveRecord:DirectiveRecord) {
|
||||
constructor(mode:string, implicitReceiver:any, ast:AST, elementIndex:number, propertyName:string, setter:SetterFn, directiveRecord:DirectiveRecord) {
|
||||
this.mode = mode;
|
||||
this.implicitReceiver = implicitReceiver;
|
||||
this.ast = ast;
|
||||
|
||||
this.elementIndex = elementIndex;
|
||||
@ -49,14 +51,18 @@ export class BindingRecord {
|
||||
}
|
||||
|
||||
static createForDirective(ast:AST, propertyName:string, setter:SetterFn, directiveRecord:DirectiveRecord) {
|
||||
return new BindingRecord(DIRECTIVE, ast, 0, propertyName, setter, directiveRecord);
|
||||
return new BindingRecord(DIRECTIVE, 0, ast, 0, propertyName, setter, directiveRecord);
|
||||
}
|
||||
|
||||
static createForElement(ast:AST, elementIndex:number, propertyName:string) {
|
||||
return new BindingRecord(ELEMENT, ast, elementIndex, propertyName, null, null);
|
||||
return new BindingRecord(ELEMENT, 0, ast, elementIndex, propertyName, null, null);
|
||||
}
|
||||
|
||||
static createForHostProperty(directiveIndex:DirectiveIndex, ast:AST, propertyName:string) {
|
||||
return new BindingRecord(ELEMENT, directiveIndex, ast, directiveIndex.elementIndex, propertyName, null, null);
|
||||
}
|
||||
|
||||
static createForTextNode(ast:AST, elementIndex:number) {
|
||||
return new BindingRecord(TEXT_NODE, ast, elementIndex, null, null, null);
|
||||
return new BindingRecord(TEXT_NODE, 0, ast, elementIndex, null, null, null);
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/faca
|
||||
|
||||
import {AbstractChangeDetector} from './abstract_change_detector';
|
||||
import {ChangeDetectionUtil} from './change_detection_util';
|
||||
import {DirectiveRecord} from './directive_record';
|
||||
import {DirectiveIndex, DirectiveRecord} from './directive_record';
|
||||
|
||||
import {
|
||||
ProtoRecord,
|
||||
@ -81,12 +81,12 @@ function hydrateTemplate(type:string, mode:string, fieldDefinitions:string, pipe
|
||||
directiveFieldNames:List<String>, detectorFieldNames:List<String>):string {
|
||||
var directiveInit = "";
|
||||
for(var i = 0; i < directiveFieldNames.length; ++i) {
|
||||
directiveInit += `${directiveFieldNames[i]} = directives.getDirectiveFor(this.directiveRecords[${i}]);\n`;
|
||||
directiveInit += `${directiveFieldNames[i]} = directives.getDirectiveFor(this.directiveRecords[${i}].directiveIndex);\n`;
|
||||
}
|
||||
|
||||
var detectorInit = "";
|
||||
for(var i = 0; i < detectorFieldNames.length; ++i) {
|
||||
detectorInit += `${detectorFieldNames[i]} = directives.getDetectorFor(this.directiveRecords[${i}]);\n`;
|
||||
detectorInit += `${detectorFieldNames[i]} = directives.getDetectorFor(this.directiveRecords[${i}].directiveIndex);\n`;
|
||||
}
|
||||
|
||||
return `
|
||||
@ -313,18 +313,18 @@ export class ChangeDetectorJITGenerator {
|
||||
}
|
||||
|
||||
getDirectiveFieldNames():List<string> {
|
||||
return this.directiveRecords.map((d) => this.getDirective(d));
|
||||
return this.directiveRecords.map((d) => this.getDirective(d.directiveIndex));
|
||||
}
|
||||
|
||||
getDetectorFieldNames():List<string> {
|
||||
return this.directiveRecords.filter(r => r.isOnPushChangeDetection()).map((d) => this.getDetector(d));
|
||||
return this.directiveRecords.filter(r => r.isOnPushChangeDetection()).map((d) => this.getDetector(d.directiveIndex));
|
||||
}
|
||||
|
||||
getDirective(d:DirectiveRecord) {
|
||||
getDirective(d:DirectiveIndex) {
|
||||
return `this.directive_${d.name}`;
|
||||
}
|
||||
|
||||
getDetector(d:DirectiveRecord) {
|
||||
getDetector(d:DirectiveIndex) {
|
||||
return `this.detector_${d.name}`;
|
||||
}
|
||||
|
||||
@ -359,7 +359,7 @@ export class ChangeDetectorJITGenerator {
|
||||
for (var i = dirs.length - 1; i >= 0; --i) {
|
||||
var dir = dirs[i];
|
||||
if (dir.callOnAllChangesDone) {
|
||||
var directive = `this.directive_${dir.name}`;
|
||||
var directive = `this.directive_${dir.directiveIndex.name}`;
|
||||
notifications.push(onAllChangesDoneTemplate(directive));
|
||||
}
|
||||
}
|
||||
@ -425,7 +425,7 @@ export class ChangeDetectorJITGenerator {
|
||||
}
|
||||
|
||||
genUpdateCurrentValue(r:ProtoRecord):string {
|
||||
var context = this.localNames[r.contextIndex];
|
||||
var context = this.getContext(r);
|
||||
var newValue = this.localNames[r.selfIndex];
|
||||
var args = this.genArgs(r);
|
||||
|
||||
@ -463,6 +463,14 @@ export class ChangeDetectorJITGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
getContext(r:ProtoRecord):string {
|
||||
if (r.contextIndex == -1) {
|
||||
return this.getDirective(r.directiveIndex);
|
||||
} else {
|
||||
return this.localNames[r.contextIndex];
|
||||
}
|
||||
}
|
||||
|
||||
ifChangedGuard(r:ProtoRecord, body:string):string {
|
||||
return ifChangedGuardTemplate(r.args.map((a) => this.changeNames[a]), body);
|
||||
}
|
||||
@ -491,7 +499,7 @@ export class ChangeDetectorJITGenerator {
|
||||
|
||||
var br = r.bindingRecord;
|
||||
if (br.isDirective()) {
|
||||
var directiveProperty = `${this.getDirective(br.directiveRecord)}.${br.propertyName}`;
|
||||
var directiveProperty = `${this.getDirective(br.directiveRecord.directiveIndex)}.${br.propertyName}`;
|
||||
return updateDirectiveTemplate(oldValue, newValue, directiveProperty);
|
||||
} else {
|
||||
return updateElementTemplate(oldValue, newValue);
|
||||
@ -513,7 +521,7 @@ export class ChangeDetectorJITGenerator {
|
||||
genNotifyOnChanges(r:ProtoRecord):string{
|
||||
var br = r.bindingRecord;
|
||||
if (r.lastInDirective && br.callOnChange()) {
|
||||
return notifyOnChangesTemplate(this.getDirective(br.directiveRecord));
|
||||
return notifyOnChangesTemplate(this.getDirective(br.directiveRecord.directiveIndex));
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
@ -522,7 +530,7 @@ export class ChangeDetectorJITGenerator {
|
||||
genNotifyOnPushDetectors(r:ProtoRecord):string{
|
||||
var br = r.bindingRecord;
|
||||
if (r.lastInDirective && br.isOnPushChangeDetection()) {
|
||||
return notifyOnPushDetectorsTemplate(this.getDetector(br.directiveRecord));
|
||||
return notifyOnPushDetectorsTemplate(this.getDetector(br.directiveRecord.directiveIndex));
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
|
@ -45,6 +45,7 @@ function _selfRecord(r:ProtoRecord, contextIndex:number, selfIndex:number):Proto
|
||||
[],
|
||||
r.fixedArgs,
|
||||
contextIndex,
|
||||
r.directiveIndex,
|
||||
selfIndex,
|
||||
r.bindingRecord,
|
||||
r.expressionAsString,
|
||||
@ -72,6 +73,7 @@ function _replaceIndices(r:ProtoRecord, selfIndex:number, indexMap:Map) {
|
||||
args,
|
||||
r.fixedArgs,
|
||||
contextIndex,
|
||||
r.directiveIndex,
|
||||
selfIndex,
|
||||
r.bindingRecord,
|
||||
r.expressionAsString,
|
||||
|
@ -1,16 +1,27 @@
|
||||
import {ON_PUSH} from './constants';
|
||||
import {StringWrapper} from 'angular2/src/facade/lang';
|
||||
|
||||
export class DirectiveRecord {
|
||||
export class DirectiveIndex {
|
||||
elementIndex:number;
|
||||
directiveIndex:number;
|
||||
|
||||
constructor(elementIndex:number, directiveIndex:number) {
|
||||
this.elementIndex = elementIndex;
|
||||
this.directiveIndex = directiveIndex;
|
||||
}
|
||||
|
||||
get name() {
|
||||
return `${this.elementIndex}_${this.directiveIndex}`;
|
||||
}
|
||||
}
|
||||
|
||||
export class DirectiveRecord {
|
||||
directiveIndex:DirectiveIndex;
|
||||
callOnAllChangesDone:boolean;
|
||||
callOnChange:boolean;
|
||||
changeDetection:string;
|
||||
|
||||
constructor(elementIndex:number, directiveIndex:number,
|
||||
callOnAllChangesDone:boolean, callOnChange:boolean, changeDetection:string) {
|
||||
this.elementIndex = elementIndex;
|
||||
constructor(directiveIndex:DirectiveIndex, callOnAllChangesDone:boolean, callOnChange:boolean, changeDetection:string) {
|
||||
this.directiveIndex = directiveIndex;
|
||||
this.callOnAllChangesDone = callOnAllChangesDone;
|
||||
this.callOnChange = callOnChange;
|
||||
@ -20,8 +31,4 @@ export class DirectiveRecord {
|
||||
isOnPushChangeDetection():boolean {
|
||||
return StringWrapper.equals(this.changeDetection, ON_PUSH);
|
||||
}
|
||||
|
||||
get name() {
|
||||
return `${this.elementIndex}_${this.directiveIndex}`;
|
||||
}
|
||||
}
|
@ -3,7 +3,6 @@ import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/faca
|
||||
|
||||
import {AbstractChangeDetector} from './abstract_change_detector';
|
||||
import {BindingRecord} from './binding_record';
|
||||
import {DirectiveRecord} from './directive_record';
|
||||
import {PipeRegistry} from './pipes/pipe_registry';
|
||||
import {ChangeDetectionUtil, uninitialized} from './change_detection_util';
|
||||
|
||||
@ -111,12 +110,12 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||
|
||||
if (proto.lastInDirective) {
|
||||
if (isPresent(changes)) {
|
||||
this._getDirectiveFor(directiveRecord).onChange(changes);
|
||||
this._getDirectiveFor(directiveRecord.directiveIndex).onChange(changes);
|
||||
changes = null;
|
||||
}
|
||||
|
||||
if (isChanged && bindingRecord.isOnPushChangeDetection()) {
|
||||
this._getDetectorFor(directiveRecord).markAsCheckOnce();
|
||||
this._getDetectorFor(directiveRecord.directiveIndex).markAsCheckOnce();
|
||||
}
|
||||
|
||||
isChanged = false;
|
||||
@ -129,7 +128,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||
for (var i = dirs.length - 1; i >= 0; --i) {
|
||||
var dir = dirs[i];
|
||||
if (dir.callOnAllChangesDone) {
|
||||
this._getDirectiveFor(dir).onAllChangesDone();
|
||||
this._getDirectiveFor(dir.directiveIndex).onAllChangesDone();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -138,7 +137,8 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||
if (isBlank(bindingRecord.directiveRecord)) {
|
||||
this.dispatcher.notifyOnBinding(bindingRecord, change.currentValue);
|
||||
} else {
|
||||
bindingRecord.setter(this._getDirectiveFor(bindingRecord.directiveRecord), change.currentValue);
|
||||
var directiveIndex = bindingRecord.directiveRecord.directiveIndex;
|
||||
bindingRecord.setter(this._getDirectiveFor(directiveIndex), change.currentValue);
|
||||
}
|
||||
}
|
||||
|
||||
@ -150,12 +150,12 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||
}
|
||||
}
|
||||
|
||||
_getDirectiveFor(directive:DirectiveRecord) {
|
||||
return this.directives.getDirectiveFor(directive);
|
||||
_getDirectiveFor(directiveIndex) {
|
||||
return this.directives.getDirectiveFor(directiveIndex);
|
||||
}
|
||||
|
||||
_getDetectorFor(directive:DirectiveRecord) {
|
||||
return this.directives.getDetectorFor(directive);
|
||||
_getDetectorFor(directiveIndex) {
|
||||
return this.directives.getDetectorFor(directiveIndex);
|
||||
}
|
||||
|
||||
_check(proto:ProtoRecord) {
|
||||
@ -235,6 +235,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||
var pipe = this._pipeFor(proto, context);
|
||||
|
||||
var newValue = pipe.transform(context);
|
||||
|
||||
if (! ChangeDetectionUtil.noChangeMarker(newValue)) {
|
||||
var prevValue = this._readSelf(proto);
|
||||
this._writeSelf(proto, newValue);
|
||||
@ -272,6 +273,12 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||
}
|
||||
|
||||
_readContext(proto:ProtoRecord) {
|
||||
if (proto.contextIndex == -1) {
|
||||
return this._getDirectiveFor(proto.directiveIndex);
|
||||
} else {
|
||||
return this.values[proto.contextIndex];
|
||||
}
|
||||
|
||||
return this.values[proto.contextIndex];
|
||||
}
|
||||
|
||||
|
@ -450,7 +450,7 @@ export class AstVisitor {
|
||||
|
||||
export class AstTransformer {
|
||||
visitImplicitReceiver(ast:ImplicitReceiver) {
|
||||
return new ImplicitReceiver();
|
||||
return ast;
|
||||
}
|
||||
|
||||
visitInterpolation(ast:Interpolation) {
|
||||
|
@ -28,6 +28,7 @@ import {DynamicChangeDetector} from './dynamic_change_detector';
|
||||
import {ChangeDetectorJITGenerator} from './change_detection_jit_generator';
|
||||
import {PipeRegistry} from './pipes/pipe_registry';
|
||||
import {BindingRecord} from './binding_record';
|
||||
import {DirectiveIndex} from './directive_record';
|
||||
|
||||
import {coalesce} from './coalesce';
|
||||
|
||||
@ -153,7 +154,7 @@ class _ConvertAstIntoProtoRecords {
|
||||
}
|
||||
|
||||
visitImplicitReceiver(ast:ImplicitReceiver) {
|
||||
return 0;
|
||||
return this.bindingRecord.implicitReceiver;
|
||||
}
|
||||
|
||||
visitInterpolation(ast:Interpolation) {
|
||||
@ -247,9 +248,15 @@ class _ConvertAstIntoProtoRecords {
|
||||
|
||||
_addRecord(type, name, funcOrValue, args, fixedArgs, context) {
|
||||
var selfIndex = ++ this.contextIndex;
|
||||
ListWrapper.push(this.protoRecords,
|
||||
new ProtoRecord(type, name, funcOrValue, args, fixedArgs, context, selfIndex,
|
||||
this.bindingRecord, this.expressionAsString, false, false));
|
||||
if (context instanceof DirectiveIndex) {
|
||||
ListWrapper.push(this.protoRecords,
|
||||
new ProtoRecord(type, name, funcOrValue, args, fixedArgs, -1, context, selfIndex,
|
||||
this.bindingRecord, this.expressionAsString, false, false));
|
||||
} else {
|
||||
ListWrapper.push(this.protoRecords,
|
||||
new ProtoRecord(type, name, funcOrValue, args, fixedArgs, context, null, selfIndex,
|
||||
this.bindingRecord, this.expressionAsString, false, false));
|
||||
}
|
||||
return selfIndex;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import {List} from 'angular2/src/facade/collection';
|
||||
import {BindingRecord} from './binding_record';
|
||||
import {DirectiveIndex} from './directive_record';
|
||||
|
||||
export const RECORD_TYPE_SELF = 0;
|
||||
export const RECORD_TYPE_CONST = 1;
|
||||
@ -19,7 +20,10 @@ export class ProtoRecord {
|
||||
funcOrValue:any;
|
||||
args:List;
|
||||
fixedArgs:List;
|
||||
|
||||
contextIndex:number;
|
||||
directiveIndex:DirectiveIndex;
|
||||
|
||||
selfIndex:number;
|
||||
bindingRecord:BindingRecord;
|
||||
lastInBinding:boolean;
|
||||
@ -32,6 +36,7 @@ export class ProtoRecord {
|
||||
args:List,
|
||||
fixedArgs:List,
|
||||
contextIndex:number,
|
||||
directiveIndex:DirectiveIndex,
|
||||
selfIndex:number,
|
||||
bindingRecord:BindingRecord,
|
||||
expressionAsString:string,
|
||||
@ -43,7 +48,10 @@ export class ProtoRecord {
|
||||
this.funcOrValue = funcOrValue;
|
||||
this.args = args;
|
||||
this.fixedArgs = fixedArgs;
|
||||
|
||||
this.contextIndex = contextIndex;
|
||||
this.directiveIndex = directiveIndex;
|
||||
|
||||
this.selfIndex = selfIndex;
|
||||
this.bindingRecord = bindingRecord;
|
||||
this.lastInBinding = lastInBinding;
|
||||
|
107
modules/angular2/src/core/annotations/annotations.js
vendored
107
modules/angular2/src/core/annotations/annotations.js
vendored
@ -187,8 +187,8 @@ import {DEFAULT} from 'angular2/change_detection';
|
||||
*
|
||||
*
|
||||
* A directive can also query for other child directives. Since parent directives are instantiated before child
|
||||
* directives, a directive can't simply inject the list of child directives. Instead, the directive
|
||||
* injects a {@link QueryList}, which updates its contents as children are added, removed, or moved by any
|
||||
* directives, a directive can't simply inject the list of child directives. Instead, the directive
|
||||
* injects a {@link QueryList}, which updates its contents as children are added, removed, or moved by any
|
||||
* {@link Viewport} directive such as a `for`, an `if`, or a `switch`.
|
||||
*
|
||||
* ```
|
||||
@ -199,7 +199,7 @@ import {DEFAULT} from 'angular2/change_detection';
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* This directive would be instantiated with a {@link QueryList} which contains `Dependency` 4 and 6. Here, `Dependency`
|
||||
* This directive would be instantiated with a {@link QueryList} which contains `Dependency` 4 and 6. Here, `Dependency`
|
||||
* 5 would not be included, because it is not a direct child.
|
||||
*
|
||||
* ### Injecting a live collection of descendant directives
|
||||
@ -444,6 +444,29 @@ export class Directive extends Injectable {
|
||||
*/
|
||||
hostListeners:any; // StringMap
|
||||
|
||||
|
||||
/**
|
||||
* Specifies which DOM properties a directives updates.
|
||||
*
|
||||
* ## Syntax
|
||||
*
|
||||
* ```
|
||||
* @Decorator({
|
||||
* selector: 'input',
|
||||
* hostProperties: {
|
||||
* 'value': 'value'
|
||||
* }
|
||||
* })
|
||||
* class InputDecorator {
|
||||
* value:string;
|
||||
* }
|
||||
*
|
||||
* In this example every time the value property of the decorator changes, Angular will update the value property of
|
||||
* the host element.
|
||||
* ```
|
||||
*/
|
||||
hostProperties:any; // String map
|
||||
|
||||
/**
|
||||
* Specifies a set of lifecycle hostListeners in which the directive participates.
|
||||
*
|
||||
@ -457,12 +480,14 @@ export class Directive extends Injectable {
|
||||
properties,
|
||||
events,
|
||||
hostListeners,
|
||||
hostProperties,
|
||||
lifecycle
|
||||
}:{
|
||||
selector:string,
|
||||
properties:any,
|
||||
events:List,
|
||||
hostListeners: any,
|
||||
hostProperties: any,
|
||||
lifecycle:List
|
||||
}={})
|
||||
{
|
||||
@ -471,6 +496,7 @@ export class Directive extends Injectable {
|
||||
this.properties = properties;
|
||||
this.events = events;
|
||||
this.hostListeners = hostListeners;
|
||||
this.hostProperties = hostProperties;
|
||||
this.lifecycle = lifecycle;
|
||||
}
|
||||
|
||||
@ -579,20 +605,22 @@ export class Component extends Directive {
|
||||
*/
|
||||
injectables:List;
|
||||
|
||||
@CONST()
|
||||
@CONST()
|
||||
constructor({
|
||||
selector,
|
||||
properties,
|
||||
events,
|
||||
hostListeners,
|
||||
injectables,
|
||||
lifecycle,
|
||||
changeDetection = DEFAULT
|
||||
selector,
|
||||
properties,
|
||||
events,
|
||||
hostListeners,
|
||||
hostProperties,
|
||||
injectables,
|
||||
lifecycle,
|
||||
changeDetection = DEFAULT
|
||||
}:{
|
||||
selector:string,
|
||||
properties:Object,
|
||||
events:List,
|
||||
hostListeners:Object,
|
||||
hostListeners:any,
|
||||
hostProperties:any,
|
||||
injectables:List,
|
||||
lifecycle:List,
|
||||
changeDetection:string
|
||||
@ -603,6 +631,7 @@ export class Component extends Directive {
|
||||
properties: properties,
|
||||
events: events,
|
||||
hostListeners: hostListeners,
|
||||
hostProperties: hostProperties,
|
||||
lifecycle: lifecycle
|
||||
});
|
||||
|
||||
@ -667,17 +696,19 @@ export class DynamicComponent extends Directive {
|
||||
|
||||
@CONST()
|
||||
constructor({
|
||||
selector,
|
||||
properties,
|
||||
events,
|
||||
hostListeners,
|
||||
injectables,
|
||||
lifecycle
|
||||
selector,
|
||||
properties,
|
||||
events,
|
||||
hostListeners,
|
||||
hostProperties,
|
||||
injectables,
|
||||
lifecycle
|
||||
}:{
|
||||
selector:string,
|
||||
properties:Object,
|
||||
properties:any,
|
||||
events:List,
|
||||
hostListeners:Object,
|
||||
hostListeners:any,
|
||||
hostProperties:any,
|
||||
injectables:List,
|
||||
lifecycle:List
|
||||
}={}) {
|
||||
@ -686,6 +717,7 @@ export class DynamicComponent extends Directive {
|
||||
properties: properties,
|
||||
events: events,
|
||||
hostListeners: hostListeners,
|
||||
hostProperties: hostProperties,
|
||||
lifecycle: lifecycle
|
||||
});
|
||||
|
||||
@ -767,6 +799,7 @@ export class Decorator extends Directive {
|
||||
properties,
|
||||
events,
|
||||
hostListeners,
|
||||
hostProperties,
|
||||
lifecycle,
|
||||
compileChildren = true,
|
||||
}:{
|
||||
@ -774,16 +807,18 @@ export class Decorator extends Directive {
|
||||
properties:any,
|
||||
events:List,
|
||||
hostListeners:any,
|
||||
hostProperties:any,
|
||||
lifecycle:List,
|
||||
compileChildren:boolean
|
||||
}={})
|
||||
{
|
||||
super({
|
||||
selector: selector,
|
||||
properties: properties,
|
||||
events: events,
|
||||
hostListeners: hostListeners,
|
||||
lifecycle: lifecycle
|
||||
selector: selector,
|
||||
properties: properties,
|
||||
events: events,
|
||||
hostListeners: hostListeners,
|
||||
hostProperties: hostProperties,
|
||||
lifecycle: lifecycle
|
||||
});
|
||||
this.compileChildren = compileChildren;
|
||||
}
|
||||
@ -889,20 +924,24 @@ export class Viewport extends Directive {
|
||||
properties,
|
||||
events,
|
||||
hostListeners,
|
||||
hostProperties,
|
||||
lifecycle
|
||||
}:{
|
||||
selector:string,
|
||||
properties:any,
|
||||
events:List,
|
||||
lifecycle:List
|
||||
selector:string,
|
||||
properties:any,
|
||||
hostListeners:any,
|
||||
hostProperties:any,
|
||||
events:List,
|
||||
lifecycle:List
|
||||
}={})
|
||||
{
|
||||
super({
|
||||
selector: selector,
|
||||
properties: properties,
|
||||
events: events,
|
||||
hostListeners: hostListeners,
|
||||
lifecycle: lifecycle
|
||||
selector: selector,
|
||||
properties: properties,
|
||||
events: events,
|
||||
hostListeners: hostListeners,
|
||||
hostProperties: hostProperties,
|
||||
lifecycle: lifecycle
|
||||
});
|
||||
}
|
||||
}
|
||||
|
22
modules/angular2/src/core/annotations/di.js
vendored
22
modules/angular2/src/core/annotations/di.js
vendored
@ -1,28 +1,6 @@
|
||||
import {CONST} from 'angular2/src/facade/lang';
|
||||
import {DependencyAnnotation} from 'angular2/di';
|
||||
|
||||
/**
|
||||
* Specifies that a function for setting host properties should be injected.
|
||||
*
|
||||
* NOTE: This is changing pre 1.0.
|
||||
*
|
||||
* The directive can inject a property setter that would allow setting this property on the host element.
|
||||
*
|
||||
* @exportedAs angular2/annotations
|
||||
*/
|
||||
export class PropertySetter extends DependencyAnnotation {
|
||||
propName: string;
|
||||
@CONST()
|
||||
constructor(propName) {
|
||||
super();
|
||||
this.propName = propName;
|
||||
}
|
||||
|
||||
get token() {
|
||||
return Function;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that a constant attribute value should be injected.
|
||||
*
|
||||
|
@ -223,12 +223,8 @@ export class Compiler {
|
||||
renderType = renderApi.DirectiveMetadata.DECORATOR_TYPE;
|
||||
compileChildren = ann.compileChildren;
|
||||
}
|
||||
var setters = [];
|
||||
var readAttributes = [];
|
||||
ListWrapper.forEach(directiveBinding.dependencies, (dep) => {
|
||||
if (isPresent(dep.propSetterName)) {
|
||||
ListWrapper.push(setters, dep.propSetterName);
|
||||
}
|
||||
if (isPresent(dep.attributeName)) {
|
||||
ListWrapper.push(readAttributes, dep.attributeName);
|
||||
}
|
||||
@ -239,8 +235,8 @@ export class Compiler {
|
||||
selector: ann.selector,
|
||||
compileChildren: compileChildren,
|
||||
hostListeners: isPresent(ann.hostListeners) ? MapWrapper.createFromStringMap(ann.hostListeners) : null,
|
||||
hostProperties: isPresent(ann.hostProperties) ? MapWrapper.createFromStringMap(ann.hostProperties) : null,
|
||||
properties: isPresent(ann.properties) ? MapWrapper.createFromStringMap(ann.properties) : null,
|
||||
setters: setters,
|
||||
readAttributes: readAttributes
|
||||
});
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import {List, ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
||||
import {Injector, Key, Dependency, bind, Binding, ResolvedBinding, NoBindingError,
|
||||
AbstractBindingError, CyclicDependencyError} from 'angular2/di';
|
||||
import {Parent, Ancestor} from 'angular2/src/core/annotations/visibility';
|
||||
import {PropertySetter, Attribute, Query} from 'angular2/src/core/annotations/di';
|
||||
import {Attribute, Query} from 'angular2/src/core/annotations/di';
|
||||
import * as viewModule from 'angular2/src/core/compiler/view';
|
||||
import {ViewContainer} from 'angular2/src/core/compiler/view_container';
|
||||
import {NgElement} from 'angular2/src/core/compiler/ng_element';
|
||||
@ -195,15 +195,13 @@ export class TreeNode {
|
||||
|
||||
export class DirectiveDependency extends Dependency {
|
||||
depth:int;
|
||||
propSetterName:string;
|
||||
attributeName:string;
|
||||
queryDirective;
|
||||
|
||||
constructor(key:Key, asPromise:boolean, lazy:boolean, optional:boolean, properties:List,
|
||||
depth:int, propSetterName: string, attributeName:string, queryDirective) {
|
||||
depth:int, attributeName:string, queryDirective) {
|
||||
super(key, asPromise, lazy, optional, properties);
|
||||
this.depth = depth;
|
||||
this.propSetterName = propSetterName;
|
||||
this.attributeName = attributeName;
|
||||
this.queryDirective = queryDirective;
|
||||
this._verify();
|
||||
@ -211,17 +209,15 @@ export class DirectiveDependency extends Dependency {
|
||||
|
||||
_verify():void {
|
||||
var count = 0;
|
||||
if (isPresent(this.propSetterName)) count++;
|
||||
if (isPresent(this.queryDirective)) count++;
|
||||
if (isPresent(this.attributeName)) count++;
|
||||
if (count > 1) throw new BaseException(
|
||||
'A directive injectable can contain only one of the following @PropertySetter, @Attribute or @Query.');
|
||||
'A directive injectable can contain only one of the following @Attribute or @Query.');
|
||||
}
|
||||
|
||||
static createFrom(d:Dependency):Dependency {
|
||||
return new DirectiveDependency(d.key, d.asPromise, d.lazy, d.optional,
|
||||
d.properties, DirectiveDependency._depth(d.properties),
|
||||
DirectiveDependency._propSetterName(d.properties),
|
||||
DirectiveDependency._attributeName(d.properties),
|
||||
DirectiveDependency._query(d.properties)
|
||||
);
|
||||
@ -234,11 +230,6 @@ export class DirectiveDependency extends Dependency {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static _propSetterName(properties):string {
|
||||
var p = ListWrapper.find(properties, (p) => p instanceof PropertySetter);
|
||||
return isPresent(p) ? p.propName : null;
|
||||
}
|
||||
|
||||
static _attributeName(properties):string {
|
||||
var p = ListWrapper.find(properties, (p) => p instanceof Attribute);
|
||||
return isPresent(p) ? p.attributeName : null;
|
||||
@ -268,6 +259,10 @@ export class DirectiveBinding extends ResolvedBinding {
|
||||
}
|
||||
}
|
||||
|
||||
get displayName() {
|
||||
return this.key.displayName;
|
||||
}
|
||||
|
||||
get eventEmitters():List<string> {
|
||||
return isPresent(this.annotation) && isPresent(this.annotation.events) ? this.annotation.events : [];
|
||||
}
|
||||
@ -735,7 +730,6 @@ export class ElementInjector extends TreeNode {
|
||||
}
|
||||
|
||||
_getByDependency(dep:DirectiveDependency, requestor:Key) {
|
||||
if (isPresent(dep.propSetterName)) return this._buildPropSetter(dep);
|
||||
if (isPresent(dep.attributeName)) return this._buildAttribute(dep);
|
||||
if (isPresent(dep.queryDirective)) return this._findQuery(dep.queryDirective).list;
|
||||
if (dep.key.id === StaticKeys.instance().elementRefId) {
|
||||
@ -744,15 +738,6 @@ export class ElementInjector extends TreeNode {
|
||||
return this._getByKey(dep.key, dep.depth, dep.optional, requestor);
|
||||
}
|
||||
|
||||
_buildPropSetter(dep) {
|
||||
var view = this._getPreBuiltObjectByKeyId(StaticKeys.instance().viewId);
|
||||
var renderer = view.renderer;
|
||||
var index = this._proto.index;
|
||||
return function(v) {
|
||||
renderer.setElementProperty(view.render, index, dep.propSetterName, v);
|
||||
};
|
||||
}
|
||||
|
||||
_buildAttribute(dep): string {
|
||||
var attributes = this._proto.attributes;
|
||||
if (isPresent(attributes) && MapWrapper.contains(attributes, dep.attributeName)) {
|
||||
|
@ -3,7 +3,7 @@ import {List, ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {reflector} from 'angular2/src/reflection/reflection';
|
||||
|
||||
import {ChangeDetection} from 'angular2/change_detection';
|
||||
import {ChangeDetection, DirectiveIndex} from 'angular2/change_detection';
|
||||
import {Component, Viewport, DynamicComponent} from '../annotations/annotations';
|
||||
|
||||
import * as renderApi from 'angular2/src/render/api';
|
||||
@ -18,7 +18,8 @@ export class ProtoViewFactory {
|
||||
this._changeDetection = changeDetection;
|
||||
}
|
||||
|
||||
createProtoView(componentBinding:DirectiveBinding, renderProtoView: renderApi.ProtoViewDto, directives:List<DirectiveBinding>):AppProtoView {
|
||||
createProtoView(componentBinding:DirectiveBinding, renderProtoView: renderApi.ProtoViewDto,
|
||||
directives:List<DirectiveBinding>):AppProtoView {
|
||||
var protoChangeDetector;
|
||||
if (isBlank(componentBinding)) {
|
||||
protoChangeDetector = this._changeDetection.createProtoChangeDetector('root', null);
|
||||
@ -43,7 +44,7 @@ export class ProtoViewFactory {
|
||||
this._createElementBinder(
|
||||
protoView, renderElementBinder, protoElementInjector, sortedDirectives
|
||||
);
|
||||
this._createDirectiveBinders(protoView, sortedDirectives);
|
||||
this._createDirectiveBinders(protoView, i, sortedDirectives);
|
||||
}
|
||||
MapWrapper.forEach(renderProtoView.variableBindings, (mappedName, varName) => {
|
||||
protoView.bindVariable(varName, mappedName);
|
||||
@ -128,27 +129,34 @@ export class ProtoViewFactory {
|
||||
return elBinder;
|
||||
}
|
||||
|
||||
_createDirectiveBinders(protoView, sortedDirectives) {
|
||||
for (var i=0; i<sortedDirectives.renderDirectives.length; i++) {
|
||||
var renderDirectiveMetadata = sortedDirectives.renderDirectives[i];
|
||||
_createDirectiveBinders(protoView, boundElementIndex, sortedDirectives) {
|
||||
for (var i = 0; i < sortedDirectives.renderDirectives.length; i++) {
|
||||
var directiveBinder = sortedDirectives.renderDirectives[i];
|
||||
|
||||
// directive properties
|
||||
MapWrapper.forEach(renderDirectiveMetadata.propertyBindings, (astWithSource, propertyName) => {
|
||||
MapWrapper.forEach(directiveBinder.propertyBindings, (astWithSource, propertyName) => {
|
||||
// TODO: these setters should eventually be created by change detection, to make
|
||||
// it monomorphic!
|
||||
var setter = reflector.setter(propertyName);
|
||||
protoView.bindDirectiveProperty(i, astWithSource, propertyName, setter);
|
||||
});
|
||||
|
||||
// host properties
|
||||
MapWrapper.forEach(directiveBinder.hostPropertyBindings, (astWithSource, propertyName) => {
|
||||
var directiveIndex = new DirectiveIndex(boundElementIndex, i);
|
||||
protoView.bindHostElementProperty(astWithSource, propertyName, directiveIndex);
|
||||
});
|
||||
|
||||
// directive events
|
||||
protoView.bindEvent(renderDirectiveMetadata.eventBindings, i);
|
||||
protoView.bindEvent(directiveBinder.eventBindings, i);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SortedDirectives {
|
||||
componentDirective: DirectiveBinding;
|
||||
viewportDirective: DirectiveBinding;
|
||||
renderDirectives: List<renderApi.DirectiveMetadata>;
|
||||
renderDirectives: List<renderApi.DirectiveBinder>;
|
||||
directives: List<DirectiveBinding>;
|
||||
|
||||
constructor(renderDirectives, allDirectives) {
|
||||
@ -156,18 +164,18 @@ class SortedDirectives {
|
||||
this.directives = [];
|
||||
this.viewportDirective = null;
|
||||
this.componentDirective = null;
|
||||
ListWrapper.forEach(renderDirectives, (renderDirectiveMetadata) => {
|
||||
var directiveBinding = allDirectives[renderDirectiveMetadata.directiveIndex];
|
||||
ListWrapper.forEach(renderDirectives, (renderDirectiveBinder) => {
|
||||
var directiveBinding = allDirectives[renderDirectiveBinder.directiveIndex];
|
||||
if ((directiveBinding.annotation instanceof Component) || (directiveBinding.annotation instanceof DynamicComponent)) {
|
||||
// component directives need to be the first binding in ElementInjectors!
|
||||
this.componentDirective = directiveBinding;
|
||||
ListWrapper.insert(this.renderDirectives, 0, renderDirectiveMetadata);
|
||||
ListWrapper.insert(this.renderDirectives, 0, renderDirectiveBinder);
|
||||
ListWrapper.insert(this.directives, 0, directiveBinding);
|
||||
} else {
|
||||
if (directiveBinding.annotation instanceof Viewport) {
|
||||
this.viewportDirective = directiveBinding;
|
||||
}
|
||||
ListWrapper.push(this.renderDirectives, renderDirectiveMetadata);
|
||||
ListWrapper.push(this.renderDirectives, renderDirectiveBinder);
|
||||
ListWrapper.push(this.directives, directiveBinding);
|
||||
}
|
||||
});
|
||||
|
16
modules/angular2/src/core/compiler/view.js
vendored
16
modules/angular2/src/core/compiler/view.js
vendored
@ -1,6 +1,6 @@
|
||||
import {ListWrapper, MapWrapper, Map, StringMapWrapper, List} from 'angular2/src/facade/collection';
|
||||
import {AST, Locals, ChangeDispatcher, ProtoChangeDetector, ChangeDetector,
|
||||
ChangeRecord, BindingRecord, DirectiveRecord, ChangeDetectorRef} from 'angular2/change_detection';
|
||||
ChangeRecord, BindingRecord, DirectiveRecord, DirectiveIndex, ChangeDetectorRef} from 'angular2/change_detection';
|
||||
|
||||
import {ProtoElementInjector, ElementInjector, PreBuiltObjects, DirectiveBinding} from './element_injector';
|
||||
import {ElementBinder} from './element_binder';
|
||||
@ -124,12 +124,12 @@ export class AppView {
|
||||
}
|
||||
}
|
||||
|
||||
getDirectiveFor(directive:DirectiveRecord) {
|
||||
getDirectiveFor(directive:DirectiveIndex) {
|
||||
var elementInjector = this.elementInjectors[directive.elementIndex];
|
||||
return elementInjector.getDirectiveAtIndex(directive.directiveIndex);
|
||||
}
|
||||
|
||||
getDetectorFor(directive:DirectiveRecord) {
|
||||
getDetectorFor(directive:DirectiveIndex) {
|
||||
var elementInjector = this.elementInjectors[directive.elementIndex];
|
||||
return elementInjector.getChangeDetector();
|
||||
}
|
||||
@ -267,6 +267,14 @@ export class AppProtoView {
|
||||
ListWrapper.push(this.bindings, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an host property binding for the last created ElementBinder via bindElement
|
||||
*/
|
||||
bindHostElementProperty(expression:AST, setterName:string, directiveIndex:DirectiveIndex):void {
|
||||
var b = BindingRecord.createForHostProperty(directiveIndex, expression, setterName);
|
||||
ListWrapper.push(this.bindings, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an event binding for the last created ElementBinder via bindElement.
|
||||
*
|
||||
@ -323,7 +331,7 @@ export class AppProtoView {
|
||||
var changeDetection = binding.changeDetection;
|
||||
|
||||
MapWrapper.set(this._directiveRecordsMap, id,
|
||||
new DirectiveRecord(elementInjectorIndex, directiveIndex,
|
||||
new DirectiveRecord(new DirectiveIndex(elementInjectorIndex, directiveIndex),
|
||||
binding.callOnAllChangesDone, binding.callOnChange, changeDetection));
|
||||
}
|
||||
|
||||
|
6
modules/angular2/src/di/key.js
vendored
6
modules/angular2/src/di/key.js
vendored
@ -1,5 +1,5 @@
|
||||
import {MapWrapper} from 'angular2/src/facade/collection';
|
||||
//import {int} from 'angular2/src/facade/lang';
|
||||
import {stringify} from 'angular2/src/facade/lang';
|
||||
|
||||
// TODO: uncoment `int` once https://github.com/angular/angular/issues/1414 is fixed
|
||||
|
||||
@ -23,6 +23,10 @@ export class Key {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
get displayName() {
|
||||
return stringify(this.token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a `Key` for a token.
|
||||
*/
|
||||
|
29
modules/angular2/src/forms/directives.js
vendored
29
modules/angular2/src/forms/directives.js
vendored
@ -1,5 +1,6 @@
|
||||
import {View, Component, Decorator, Ancestor, onChange, PropertySetter} from 'angular2/angular2';
|
||||
import {View, Component, Decorator, Ancestor, onChange, ElementRef} from 'angular2/angular2';
|
||||
import {Optional} from 'angular2/di';
|
||||
import {Renderer} from 'angular2/src/render/api';
|
||||
import {isBlank, isPresent, isString, CONST} from 'angular2/src/facade/lang';
|
||||
import {StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {ControlGroup, Control} from './model';
|
||||
@ -27,19 +28,21 @@ import {Validators} from './validators';
|
||||
hostListeners: {
|
||||
'change' : 'onChange($event.target.value)',
|
||||
'input' : 'onChange($event.target.value)'
|
||||
},
|
||||
hostProperties: {
|
||||
'value' : 'value'
|
||||
}
|
||||
})
|
||||
export class DefaultValueAccessor {
|
||||
_setValueProperty:Function;
|
||||
value;
|
||||
onChange:Function;
|
||||
|
||||
constructor(@PropertySetter('value') setValueProperty:Function) {
|
||||
this._setValueProperty = setValueProperty;
|
||||
constructor() {
|
||||
this.onChange = (_) => {};
|
||||
}
|
||||
|
||||
writeValue(value) {
|
||||
this._setValueProperty(value);
|
||||
this.value = value
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,20 +61,28 @@ export class DefaultValueAccessor {
|
||||
selector: 'input[type=checkbox][control]',
|
||||
hostListeners: {
|
||||
'change' : 'onChange($event.target.checked)'
|
||||
},
|
||||
hostProperties: {
|
||||
'checked' : 'checked'
|
||||
}
|
||||
})
|
||||
export class CheckboxControlValueAccessor {
|
||||
_setCheckedProperty:Function;
|
||||
_elementRef:ElementRef;
|
||||
_renderer:Renderer;
|
||||
|
||||
checked:boolean;
|
||||
onChange:Function;
|
||||
|
||||
constructor(cd:ControlDirective, @PropertySetter('checked') setCheckedProperty:Function) {
|
||||
this._setCheckedProperty = setCheckedProperty;
|
||||
constructor(cd:ControlDirective, elementRef:ElementRef, renderer:Renderer) {
|
||||
this.onChange = (_) => {};
|
||||
this._elementRef = elementRef;
|
||||
this._renderer = renderer;
|
||||
cd.valueAccessor = this; //ControlDirective should inject CheckboxControlDirective
|
||||
}
|
||||
|
||||
writeValue(value) {
|
||||
this._setCheckedProperty(value);
|
||||
this._renderer.setElementProperty(this._elementRef.hostView.render, this._elementRef.boundElementIndex,
|
||||
'checked', value)
|
||||
}
|
||||
}
|
||||
|
||||
|
12
modules/angular2/src/render/api.js
vendored
12
modules/angular2/src/render/api.js
vendored
@ -72,12 +72,14 @@ export class DirectiveBinder {
|
||||
// that replaced the values that should be extracted from the element
|
||||
// with a local name
|
||||
eventBindings: List<EventBinding>;
|
||||
hostPropertyBindings: Map<string, ASTWithSource>;
|
||||
constructor({
|
||||
directiveIndex, propertyBindings, eventBindings
|
||||
directiveIndex, propertyBindings, eventBindings, hostPropertyBindings
|
||||
}) {
|
||||
this.directiveIndex = directiveIndex;
|
||||
this.propertyBindings = propertyBindings;
|
||||
this.eventBindings = eventBindings;
|
||||
this.hostPropertyBindings = hostPropertyBindings;
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,17 +116,17 @@ export class DirectiveMetadata {
|
||||
selector:string;
|
||||
compileChildren:boolean;
|
||||
hostListeners:Map<string, string>;
|
||||
hostProperties:Map<string, string>;
|
||||
properties:Map<string, string>;
|
||||
setters:List<string>;
|
||||
readAttributes:List<string>;
|
||||
type:number;
|
||||
constructor({id, selector, compileChildren, hostListeners, properties, setters, readAttributes, type}) {
|
||||
constructor({id, selector, compileChildren, hostListeners, hostProperties, properties, readAttributes, type}) {
|
||||
this.id = id;
|
||||
this.selector = selector;
|
||||
this.compileChildren = isPresent(compileChildren) ? compileChildren : true;
|
||||
this.hostListeners = hostListeners;
|
||||
this.hostProperties = hostProperties;
|
||||
this.properties = properties;
|
||||
this.setters = setters;
|
||||
this.readAttributes = readAttributes;
|
||||
this.type = type;
|
||||
}
|
||||
@ -248,7 +250,7 @@ export class Renderer {
|
||||
|
||||
/**
|
||||
* Sets a property on an element.
|
||||
* Note: This will fail if the property was not mentioned previously as a propertySetter
|
||||
* Note: This will fail if the property was not mentioned previously as a host property
|
||||
* in the View.
|
||||
*/
|
||||
setElementProperty(view:ViewRef, elementIndex:number, propertyName:string, propertyValue:any):void {}
|
||||
|
@ -56,21 +56,21 @@ export class DirectiveParser extends CompileStep {
|
||||
this._selectorMatcher.match(cssSelector, (selector, directiveIndex) => {
|
||||
var elementBinder = current.bindElement();
|
||||
var directive = this._directives[directiveIndex];
|
||||
var directiveBinder = elementBinder.bindDirective(directiveIndex);
|
||||
var directiveBinderBuilder = elementBinder.bindDirective(directiveIndex);
|
||||
current.compileChildren = current.compileChildren && directive.compileChildren;
|
||||
if (isPresent(directive.properties)) {
|
||||
MapWrapper.forEach(directive.properties, (bindConfig, dirProperty) => {
|
||||
this._bindDirectiveProperty(dirProperty, bindConfig, current, directiveBinder);
|
||||
this._bindDirectiveProperty(dirProperty, bindConfig, current, directiveBinderBuilder);
|
||||
});
|
||||
}
|
||||
if (isPresent(directive.hostListeners)) {
|
||||
MapWrapper.forEach(directive.hostListeners, (action, eventName) => {
|
||||
this._bindDirectiveEvent(eventName, action, current, directiveBinder);
|
||||
this._bindDirectiveEvent(eventName, action, current, directiveBinderBuilder);
|
||||
});
|
||||
}
|
||||
if (isPresent(directive.setters)) {
|
||||
ListWrapper.forEach(directive.setters, (propertyName) => {
|
||||
elementBinder.bindPropertySetter(propertyName);
|
||||
if (isPresent(directive.hostProperties)) {
|
||||
MapWrapper.forEach(directive.hostProperties, (hostPropertyName, directivePropertyName) => {
|
||||
this._bindHostProperty(hostPropertyName, directivePropertyName, current, directiveBinderBuilder);
|
||||
});
|
||||
}
|
||||
if (isPresent(directive.readAttributes)) {
|
||||
@ -102,7 +102,7 @@ export class DirectiveParser extends CompileStep {
|
||||
});
|
||||
}
|
||||
|
||||
_bindDirectiveProperty(dirProperty, bindConfig, compileElement, directiveBinder) {
|
||||
_bindDirectiveProperty(dirProperty, bindConfig, compileElement, directiveBinderBuilder) {
|
||||
var pipes = this._splitBindConfig(bindConfig);
|
||||
var elProp = ListWrapper.removeAt(pipes, 0);
|
||||
|
||||
@ -124,21 +124,27 @@ export class DirectiveParser extends CompileStep {
|
||||
// Bindings are optional, so this binding only needs to be set up if an expression is given.
|
||||
if (isPresent(bindingAst)) {
|
||||
var fullExpAstWithBindPipes = this._parser.addPipes(bindingAst, pipes);
|
||||
directiveBinder.bindProperty(
|
||||
directiveBinderBuilder.bindProperty(
|
||||
dirProperty, fullExpAstWithBindPipes
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
_bindDirectiveEvent(eventName, action, compileElement, directiveBinder) {
|
||||
_bindDirectiveEvent(eventName, action, compileElement, directiveBinderBuilder) {
|
||||
var ast = this._parser.parseAction(action, compileElement.elementDescription);
|
||||
if (StringWrapper.contains(eventName, EVENT_TARGET_SEPARATOR)) {
|
||||
var parts = eventName.split(EVENT_TARGET_SEPARATOR);
|
||||
directiveBinder.bindEvent(parts[1], ast, parts[0]);
|
||||
directiveBinderBuilder.bindEvent(parts[1], ast, parts[0]);
|
||||
} else {
|
||||
directiveBinder.bindEvent(eventName, ast);
|
||||
directiveBinderBuilder.bindEvent(eventName, ast);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
_bindHostProperty(hostPropertyName, directivePropertyName, compileElement, directiveBinderBuilder) {
|
||||
var ast = this._parser.parseBinding(directivePropertyName,
|
||||
`hostProperties of ${compileElement.elementDescription}`);
|
||||
directiveBinderBuilder.bindHostProperty(hostPropertyName, ast);
|
||||
}
|
||||
|
||||
_splitBindConfig(bindConfig:string) {
|
||||
|
@ -5,7 +5,6 @@ import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {
|
||||
ASTWithSource, AST, AstTransformer, AccessMember, LiteralArray, ImplicitReceiver
|
||||
} from 'angular2/change_detection';
|
||||
import {SetterFn} from 'angular2/src/reflection/types';
|
||||
|
||||
import {RenderProtoView} from './proto_view';
|
||||
import {ElementBinder, Event} from './element_binder';
|
||||
@ -57,17 +56,26 @@ export class ProtoViewBuilder {
|
||||
var apiElementBinders = [];
|
||||
ListWrapper.forEach(this.elements, (ebb) => {
|
||||
var propertySetters = MapWrapper.create();
|
||||
var apiDirectiveBinders = ListWrapper.map(ebb.directives, (db) => {
|
||||
ebb.eventBuilder.merge(db.eventBuilder);
|
||||
|
||||
var apiDirectiveBinders = ListWrapper.map(ebb.directives, (dbb) => {
|
||||
ebb.eventBuilder.merge(dbb.eventBuilder);
|
||||
|
||||
MapWrapper.forEach(dbb.hostPropertyBindings, (_, hostPropertyName) => {
|
||||
MapWrapper.set(propertySetters, hostPropertyName, setterFactory(hostPropertyName));
|
||||
});
|
||||
|
||||
return new api.DirectiveBinder({
|
||||
directiveIndex: db.directiveIndex,
|
||||
propertyBindings: db.propertyBindings,
|
||||
eventBindings: db.eventBindings
|
||||
directiveIndex: dbb.directiveIndex,
|
||||
propertyBindings: dbb.propertyBindings,
|
||||
eventBindings: dbb.eventBindings,
|
||||
hostPropertyBindings: dbb.hostPropertyBindings
|
||||
});
|
||||
});
|
||||
MapWrapper.forEach(ebb.propertySetters, (setter, propertyName) => {
|
||||
MapWrapper.set(propertySetters, propertyName, setter);
|
||||
|
||||
MapWrapper.forEach(ebb.propertyBindings, (_, propertyName) => {
|
||||
MapWrapper.set(propertySetters, propertyName, setterFactory(propertyName));
|
||||
});
|
||||
|
||||
var nestedProtoView =
|
||||
isPresent(ebb.nestedProtoView) ? ebb.nestedProtoView.build() : null;
|
||||
var parentIndex = isPresent(ebb.parent) ? ebb.parent.index : -1;
|
||||
@ -119,7 +127,6 @@ export class ElementBinderBuilder {
|
||||
textBindingIndices: List<number>;
|
||||
textBindings: List<ASTWithSource>;
|
||||
contentTagSelector:string;
|
||||
propertySetters: Map<string, SetterFn>;
|
||||
readAttributes: Map<string, string>;
|
||||
componentId: string;
|
||||
|
||||
@ -137,7 +144,6 @@ export class ElementBinderBuilder {
|
||||
this.textBindings = [];
|
||||
this.textBindingIndices = [];
|
||||
this.contentTagSelector = null;
|
||||
this.propertySetters = MapWrapper.create();
|
||||
this.componentId = null;
|
||||
this.readAttributes = MapWrapper.create();
|
||||
}
|
||||
@ -172,11 +178,10 @@ export class ElementBinderBuilder {
|
||||
|
||||
bindProperty(name, expression) {
|
||||
MapWrapper.set(this.propertyBindings, name, expression);
|
||||
this.bindPropertySetter(name);
|
||||
}
|
||||
|
||||
bindPropertySetter(name) {
|
||||
MapWrapper.set(this.propertySetters, name, setterFactory(name));
|
||||
//TODO: required for Dart transformers. Remove when Dart transformers
|
||||
//run all the steps of the render compiler
|
||||
setterFactory(name);
|
||||
}
|
||||
|
||||
bindVariable(name, value) {
|
||||
@ -216,12 +221,14 @@ export class ElementBinderBuilder {
|
||||
export class DirectiveBuilder {
|
||||
directiveIndex:number;
|
||||
propertyBindings: Map<string, ASTWithSource>;
|
||||
hostPropertyBindings: Map<string, ASTWithSource>;
|
||||
eventBindings: List<api.EventBinding>;
|
||||
eventBuilder: EventBuilder;
|
||||
|
||||
constructor(directiveIndex) {
|
||||
this.directiveIndex = directiveIndex;
|
||||
this.propertyBindings = MapWrapper.create();
|
||||
this.hostPropertyBindings = MapWrapper.create();
|
||||
this.eventBindings = ListWrapper.create();
|
||||
this.eventBuilder = new EventBuilder();
|
||||
}
|
||||
@ -230,6 +237,10 @@ export class DirectiveBuilder {
|
||||
MapWrapper.set(this.propertyBindings, name, expression);
|
||||
}
|
||||
|
||||
bindHostProperty(name, expression) {
|
||||
MapWrapper.set(this.hostPropertyBindings, name, expression);
|
||||
}
|
||||
|
||||
bindEvent(name, expression, target = null) {
|
||||
ListWrapper.push(this.eventBindings, this.eventBuilder.add(name, expression, target));
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ class _DirectiveMetadataVisitor extends Object
|
||||
compileChildren: false,
|
||||
properties: {},
|
||||
hostListeners: {},
|
||||
setters: [],
|
||||
hostProperties: {},
|
||||
readAttributes: []);
|
||||
super.visitInstanceCreationExpression(node);
|
||||
}
|
||||
@ -76,6 +76,9 @@ class _DirectiveMetadataVisitor extends Object
|
||||
case 'properties':
|
||||
_populateProperties(node.expression);
|
||||
break;
|
||||
case 'hostProperties':
|
||||
_populateHostProperties(node.expression);
|
||||
break;
|
||||
case 'hostListeners':
|
||||
_populateHostListeners(node.expression);
|
||||
}
|
||||
@ -135,4 +138,19 @@ class _DirectiveMetadataVisitor extends Object
|
||||
meta.hostListeners[sKey] = sVal;
|
||||
}
|
||||
}
|
||||
|
||||
void _populateHostProperties(Expression hostPropertyValue) {
|
||||
if (hostPropertyValue is! MapLiteral) {
|
||||
logger.error('Angular 2 currently only supports map literal values for '
|
||||
'Directive#hostProperties.'
|
||||
' Source: ${hostPropertyValue}');
|
||||
return;
|
||||
}
|
||||
for (MapLiteralEntry entry in (hostPropertyValue as MapLiteral).entries) {
|
||||
var sKey = _expressionToString(entry.key, 'Directive#hostProperties keys');
|
||||
var sVal =
|
||||
_expressionToString(entry.value, 'Directive#hostProperties values');
|
||||
meta.hostProperties[sKey] = sVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user