feat(view): reimplemented property setters using change detection
This commit is contained in:
parent
8a92a1f13e
commit
8ccafb0524
2
modules/angular2/change_detection.js
vendored
2
modules/angular2/change_detection.js
vendored
@ -18,7 +18,7 @@ export {ProtoChangeDetector, ChangeDispatcher, ChangeDetector, ChangeDetection}
|
|||||||
export {CHECK_ONCE, CHECK_ALWAYS, DETACHED, CHECKED, ON_PUSH, DEFAULT} from './src/change_detection/constants';
|
export {CHECK_ONCE, CHECK_ALWAYS, DETACHED, CHECKED, ON_PUSH, DEFAULT} from './src/change_detection/constants';
|
||||||
export {DynamicProtoChangeDetector, JitProtoChangeDetector} from './src/change_detection/proto_change_detector';
|
export {DynamicProtoChangeDetector, JitProtoChangeDetector} from './src/change_detection/proto_change_detector';
|
||||||
export {BindingRecord} from './src/change_detection/binding_record';
|
export {BindingRecord} from './src/change_detection/binding_record';
|
||||||
export {DirectiveRecord} from './src/change_detection/directive_record';
|
export {DirectiveIndex, DirectiveRecord} from './src/change_detection/directive_record';
|
||||||
export {DynamicChangeDetector} from './src/change_detection/dynamic_change_detector';
|
export {DynamicChangeDetector} from './src/change_detection/dynamic_change_detector';
|
||||||
export {ChangeDetectorRef} from './src/change_detection/change_detector_ref';
|
export {ChangeDetectorRef} from './src/change_detection/change_detector_ref';
|
||||||
export {PipeRegistry} from './src/change_detection/pipes/pipe_registry';
|
export {PipeRegistry} from './src/change_detection/pipes/pipe_registry';
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||||
import {SetterFn} from 'angular2/src/reflection/types';
|
import {SetterFn} from 'angular2/src/reflection/types';
|
||||||
import {AST} from './parser/ast';
|
import {AST} from './parser/ast';
|
||||||
import {DirectiveRecord} from './directive_record';
|
import {DirectiveIndex, DirectiveRecord} from './directive_record';
|
||||||
|
|
||||||
const DIRECTIVE="directive";
|
const DIRECTIVE="directive";
|
||||||
const ELEMENT="element";
|
const ELEMENT="element";
|
||||||
@ -11,14 +11,16 @@ export class BindingRecord {
|
|||||||
mode:string;
|
mode:string;
|
||||||
ast:AST;
|
ast:AST;
|
||||||
|
|
||||||
|
implicitReceiver:any; //number | DirectiveIndex
|
||||||
elementIndex:number;
|
elementIndex:number;
|
||||||
propertyName:string;
|
propertyName:string;
|
||||||
setter:SetterFn;
|
setter:SetterFn;
|
||||||
|
|
||||||
directiveRecord:DirectiveRecord;
|
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.mode = mode;
|
||||||
|
this.implicitReceiver = implicitReceiver;
|
||||||
this.ast = ast;
|
this.ast = ast;
|
||||||
|
|
||||||
this.elementIndex = elementIndex;
|
this.elementIndex = elementIndex;
|
||||||
@ -49,14 +51,18 @@ export class BindingRecord {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static createForDirective(ast:AST, propertyName:string, setter:SetterFn, directiveRecord:DirectiveRecord) {
|
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) {
|
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) {
|
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 {AbstractChangeDetector} from './abstract_change_detector';
|
||||||
import {ChangeDetectionUtil} from './change_detection_util';
|
import {ChangeDetectionUtil} from './change_detection_util';
|
||||||
import {DirectiveRecord} from './directive_record';
|
import {DirectiveIndex, DirectiveRecord} from './directive_record';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ProtoRecord,
|
ProtoRecord,
|
||||||
@ -81,12 +81,12 @@ function hydrateTemplate(type:string, mode:string, fieldDefinitions:string, pipe
|
|||||||
directiveFieldNames:List<String>, detectorFieldNames:List<String>):string {
|
directiveFieldNames:List<String>, detectorFieldNames:List<String>):string {
|
||||||
var directiveInit = "";
|
var directiveInit = "";
|
||||||
for(var i = 0; i < directiveFieldNames.length; ++i) {
|
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 = "";
|
var detectorInit = "";
|
||||||
for(var i = 0; i < detectorFieldNames.length; ++i) {
|
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 `
|
return `
|
||||||
@ -313,18 +313,18 @@ export class ChangeDetectorJITGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getDirectiveFieldNames():List<string> {
|
getDirectiveFieldNames():List<string> {
|
||||||
return this.directiveRecords.map((d) => this.getDirective(d));
|
return this.directiveRecords.map((d) => this.getDirective(d.directiveIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
getDetectorFieldNames():List<string> {
|
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}`;
|
return `this.directive_${d.name}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
getDetector(d:DirectiveRecord) {
|
getDetector(d:DirectiveIndex) {
|
||||||
return `this.detector_${d.name}`;
|
return `this.detector_${d.name}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -359,7 +359,7 @@ export class ChangeDetectorJITGenerator {
|
|||||||
for (var i = dirs.length - 1; i >= 0; --i) {
|
for (var i = dirs.length - 1; i >= 0; --i) {
|
||||||
var dir = dirs[i];
|
var dir = dirs[i];
|
||||||
if (dir.callOnAllChangesDone) {
|
if (dir.callOnAllChangesDone) {
|
||||||
var directive = `this.directive_${dir.name}`;
|
var directive = `this.directive_${dir.directiveIndex.name}`;
|
||||||
notifications.push(onAllChangesDoneTemplate(directive));
|
notifications.push(onAllChangesDoneTemplate(directive));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -425,7 +425,7 @@ export class ChangeDetectorJITGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
genUpdateCurrentValue(r:ProtoRecord):string {
|
genUpdateCurrentValue(r:ProtoRecord):string {
|
||||||
var context = this.localNames[r.contextIndex];
|
var context = this.getContext(r);
|
||||||
var newValue = this.localNames[r.selfIndex];
|
var newValue = this.localNames[r.selfIndex];
|
||||||
var args = this.genArgs(r);
|
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 {
|
ifChangedGuard(r:ProtoRecord, body:string):string {
|
||||||
return ifChangedGuardTemplate(r.args.map((a) => this.changeNames[a]), body);
|
return ifChangedGuardTemplate(r.args.map((a) => this.changeNames[a]), body);
|
||||||
}
|
}
|
||||||
@ -491,7 +499,7 @@ export class ChangeDetectorJITGenerator {
|
|||||||
|
|
||||||
var br = r.bindingRecord;
|
var br = r.bindingRecord;
|
||||||
if (br.isDirective()) {
|
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);
|
return updateDirectiveTemplate(oldValue, newValue, directiveProperty);
|
||||||
} else {
|
} else {
|
||||||
return updateElementTemplate(oldValue, newValue);
|
return updateElementTemplate(oldValue, newValue);
|
||||||
@ -513,7 +521,7 @@ export class ChangeDetectorJITGenerator {
|
|||||||
genNotifyOnChanges(r:ProtoRecord):string{
|
genNotifyOnChanges(r:ProtoRecord):string{
|
||||||
var br = r.bindingRecord;
|
var br = r.bindingRecord;
|
||||||
if (r.lastInDirective && br.callOnChange()) {
|
if (r.lastInDirective && br.callOnChange()) {
|
||||||
return notifyOnChangesTemplate(this.getDirective(br.directiveRecord));
|
return notifyOnChangesTemplate(this.getDirective(br.directiveRecord.directiveIndex));
|
||||||
} else {
|
} else {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@ -522,7 +530,7 @@ export class ChangeDetectorJITGenerator {
|
|||||||
genNotifyOnPushDetectors(r:ProtoRecord):string{
|
genNotifyOnPushDetectors(r:ProtoRecord):string{
|
||||||
var br = r.bindingRecord;
|
var br = r.bindingRecord;
|
||||||
if (r.lastInDirective && br.isOnPushChangeDetection()) {
|
if (r.lastInDirective && br.isOnPushChangeDetection()) {
|
||||||
return notifyOnPushDetectorsTemplate(this.getDetector(br.directiveRecord));
|
return notifyOnPushDetectorsTemplate(this.getDetector(br.directiveRecord.directiveIndex));
|
||||||
} else {
|
} else {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,7 @@ function _selfRecord(r:ProtoRecord, contextIndex:number, selfIndex:number):Proto
|
|||||||
[],
|
[],
|
||||||
r.fixedArgs,
|
r.fixedArgs,
|
||||||
contextIndex,
|
contextIndex,
|
||||||
|
r.directiveIndex,
|
||||||
selfIndex,
|
selfIndex,
|
||||||
r.bindingRecord,
|
r.bindingRecord,
|
||||||
r.expressionAsString,
|
r.expressionAsString,
|
||||||
@ -72,6 +73,7 @@ function _replaceIndices(r:ProtoRecord, selfIndex:number, indexMap:Map) {
|
|||||||
args,
|
args,
|
||||||
r.fixedArgs,
|
r.fixedArgs,
|
||||||
contextIndex,
|
contextIndex,
|
||||||
|
r.directiveIndex,
|
||||||
selfIndex,
|
selfIndex,
|
||||||
r.bindingRecord,
|
r.bindingRecord,
|
||||||
r.expressionAsString,
|
r.expressionAsString,
|
||||||
|
@ -1,16 +1,27 @@
|
|||||||
import {ON_PUSH} from './constants';
|
import {ON_PUSH} from './constants';
|
||||||
import {StringWrapper} from 'angular2/src/facade/lang';
|
import {StringWrapper} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
export class DirectiveRecord {
|
export class DirectiveIndex {
|
||||||
elementIndex:number;
|
elementIndex:number;
|
||||||
directiveIndex: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;
|
callOnAllChangesDone:boolean;
|
||||||
callOnChange:boolean;
|
callOnChange:boolean;
|
||||||
changeDetection:string;
|
changeDetection:string;
|
||||||
|
|
||||||
constructor(elementIndex:number, directiveIndex:number,
|
constructor(directiveIndex:DirectiveIndex, callOnAllChangesDone:boolean, callOnChange:boolean, changeDetection:string) {
|
||||||
callOnAllChangesDone:boolean, callOnChange:boolean, changeDetection:string) {
|
|
||||||
this.elementIndex = elementIndex;
|
|
||||||
this.directiveIndex = directiveIndex;
|
this.directiveIndex = directiveIndex;
|
||||||
this.callOnAllChangesDone = callOnAllChangesDone;
|
this.callOnAllChangesDone = callOnAllChangesDone;
|
||||||
this.callOnChange = callOnChange;
|
this.callOnChange = callOnChange;
|
||||||
@ -20,8 +31,4 @@ export class DirectiveRecord {
|
|||||||
isOnPushChangeDetection():boolean {
|
isOnPushChangeDetection():boolean {
|
||||||
return StringWrapper.equals(this.changeDetection, ON_PUSH);
|
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 {AbstractChangeDetector} from './abstract_change_detector';
|
||||||
import {BindingRecord} from './binding_record';
|
import {BindingRecord} from './binding_record';
|
||||||
import {DirectiveRecord} from './directive_record';
|
|
||||||
import {PipeRegistry} from './pipes/pipe_registry';
|
import {PipeRegistry} from './pipes/pipe_registry';
|
||||||
import {ChangeDetectionUtil, uninitialized} from './change_detection_util';
|
import {ChangeDetectionUtil, uninitialized} from './change_detection_util';
|
||||||
|
|
||||||
@ -111,12 +110,12 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
|||||||
|
|
||||||
if (proto.lastInDirective) {
|
if (proto.lastInDirective) {
|
||||||
if (isPresent(changes)) {
|
if (isPresent(changes)) {
|
||||||
this._getDirectiveFor(directiveRecord).onChange(changes);
|
this._getDirectiveFor(directiveRecord.directiveIndex).onChange(changes);
|
||||||
changes = null;
|
changes = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isChanged && bindingRecord.isOnPushChangeDetection()) {
|
if (isChanged && bindingRecord.isOnPushChangeDetection()) {
|
||||||
this._getDetectorFor(directiveRecord).markAsCheckOnce();
|
this._getDetectorFor(directiveRecord.directiveIndex).markAsCheckOnce();
|
||||||
}
|
}
|
||||||
|
|
||||||
isChanged = false;
|
isChanged = false;
|
||||||
@ -129,7 +128,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
|||||||
for (var i = dirs.length - 1; i >= 0; --i) {
|
for (var i = dirs.length - 1; i >= 0; --i) {
|
||||||
var dir = dirs[i];
|
var dir = dirs[i];
|
||||||
if (dir.callOnAllChangesDone) {
|
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)) {
|
if (isBlank(bindingRecord.directiveRecord)) {
|
||||||
this.dispatcher.notifyOnBinding(bindingRecord, change.currentValue);
|
this.dispatcher.notifyOnBinding(bindingRecord, change.currentValue);
|
||||||
} else {
|
} 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) {
|
_getDirectiveFor(directiveIndex) {
|
||||||
return this.directives.getDirectiveFor(directive);
|
return this.directives.getDirectiveFor(directiveIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
_getDetectorFor(directive:DirectiveRecord) {
|
_getDetectorFor(directiveIndex) {
|
||||||
return this.directives.getDetectorFor(directive);
|
return this.directives.getDetectorFor(directiveIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
_check(proto:ProtoRecord) {
|
_check(proto:ProtoRecord) {
|
||||||
@ -235,6 +235,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
|||||||
var pipe = this._pipeFor(proto, context);
|
var pipe = this._pipeFor(proto, context);
|
||||||
|
|
||||||
var newValue = pipe.transform(context);
|
var newValue = pipe.transform(context);
|
||||||
|
|
||||||
if (! ChangeDetectionUtil.noChangeMarker(newValue)) {
|
if (! ChangeDetectionUtil.noChangeMarker(newValue)) {
|
||||||
var prevValue = this._readSelf(proto);
|
var prevValue = this._readSelf(proto);
|
||||||
this._writeSelf(proto, newValue);
|
this._writeSelf(proto, newValue);
|
||||||
@ -272,6 +273,12 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_readContext(proto:ProtoRecord) {
|
_readContext(proto:ProtoRecord) {
|
||||||
|
if (proto.contextIndex == -1) {
|
||||||
|
return this._getDirectiveFor(proto.directiveIndex);
|
||||||
|
} else {
|
||||||
|
return this.values[proto.contextIndex];
|
||||||
|
}
|
||||||
|
|
||||||
return this.values[proto.contextIndex];
|
return this.values[proto.contextIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -450,7 +450,7 @@ export class AstVisitor {
|
|||||||
|
|
||||||
export class AstTransformer {
|
export class AstTransformer {
|
||||||
visitImplicitReceiver(ast:ImplicitReceiver) {
|
visitImplicitReceiver(ast:ImplicitReceiver) {
|
||||||
return new ImplicitReceiver();
|
return ast;
|
||||||
}
|
}
|
||||||
|
|
||||||
visitInterpolation(ast:Interpolation) {
|
visitInterpolation(ast:Interpolation) {
|
||||||
|
@ -28,6 +28,7 @@ import {DynamicChangeDetector} from './dynamic_change_detector';
|
|||||||
import {ChangeDetectorJITGenerator} from './change_detection_jit_generator';
|
import {ChangeDetectorJITGenerator} from './change_detection_jit_generator';
|
||||||
import {PipeRegistry} from './pipes/pipe_registry';
|
import {PipeRegistry} from './pipes/pipe_registry';
|
||||||
import {BindingRecord} from './binding_record';
|
import {BindingRecord} from './binding_record';
|
||||||
|
import {DirectiveIndex} from './directive_record';
|
||||||
|
|
||||||
import {coalesce} from './coalesce';
|
import {coalesce} from './coalesce';
|
||||||
|
|
||||||
@ -153,7 +154,7 @@ class _ConvertAstIntoProtoRecords {
|
|||||||
}
|
}
|
||||||
|
|
||||||
visitImplicitReceiver(ast:ImplicitReceiver) {
|
visitImplicitReceiver(ast:ImplicitReceiver) {
|
||||||
return 0;
|
return this.bindingRecord.implicitReceiver;
|
||||||
}
|
}
|
||||||
|
|
||||||
visitInterpolation(ast:Interpolation) {
|
visitInterpolation(ast:Interpolation) {
|
||||||
@ -247,9 +248,15 @@ class _ConvertAstIntoProtoRecords {
|
|||||||
|
|
||||||
_addRecord(type, name, funcOrValue, args, fixedArgs, context) {
|
_addRecord(type, name, funcOrValue, args, fixedArgs, context) {
|
||||||
var selfIndex = ++ this.contextIndex;
|
var selfIndex = ++ this.contextIndex;
|
||||||
ListWrapper.push(this.protoRecords,
|
if (context instanceof DirectiveIndex) {
|
||||||
new ProtoRecord(type, name, funcOrValue, args, fixedArgs, context, selfIndex,
|
ListWrapper.push(this.protoRecords,
|
||||||
this.bindingRecord, this.expressionAsString, false, false));
|
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;
|
return selfIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import {List} from 'angular2/src/facade/collection';
|
import {List} from 'angular2/src/facade/collection';
|
||||||
import {BindingRecord} from './binding_record';
|
import {BindingRecord} from './binding_record';
|
||||||
|
import {DirectiveIndex} from './directive_record';
|
||||||
|
|
||||||
export const RECORD_TYPE_SELF = 0;
|
export const RECORD_TYPE_SELF = 0;
|
||||||
export const RECORD_TYPE_CONST = 1;
|
export const RECORD_TYPE_CONST = 1;
|
||||||
@ -19,7 +20,10 @@ export class ProtoRecord {
|
|||||||
funcOrValue:any;
|
funcOrValue:any;
|
||||||
args:List;
|
args:List;
|
||||||
fixedArgs:List;
|
fixedArgs:List;
|
||||||
|
|
||||||
contextIndex:number;
|
contextIndex:number;
|
||||||
|
directiveIndex:DirectiveIndex;
|
||||||
|
|
||||||
selfIndex:number;
|
selfIndex:number;
|
||||||
bindingRecord:BindingRecord;
|
bindingRecord:BindingRecord;
|
||||||
lastInBinding:boolean;
|
lastInBinding:boolean;
|
||||||
@ -32,6 +36,7 @@ export class ProtoRecord {
|
|||||||
args:List,
|
args:List,
|
||||||
fixedArgs:List,
|
fixedArgs:List,
|
||||||
contextIndex:number,
|
contextIndex:number,
|
||||||
|
directiveIndex:DirectiveIndex,
|
||||||
selfIndex:number,
|
selfIndex:number,
|
||||||
bindingRecord:BindingRecord,
|
bindingRecord:BindingRecord,
|
||||||
expressionAsString:string,
|
expressionAsString:string,
|
||||||
@ -43,7 +48,10 @@ export class ProtoRecord {
|
|||||||
this.funcOrValue = funcOrValue;
|
this.funcOrValue = funcOrValue;
|
||||||
this.args = args;
|
this.args = args;
|
||||||
this.fixedArgs = fixedArgs;
|
this.fixedArgs = fixedArgs;
|
||||||
|
|
||||||
this.contextIndex = contextIndex;
|
this.contextIndex = contextIndex;
|
||||||
|
this.directiveIndex = directiveIndex;
|
||||||
|
|
||||||
this.selfIndex = selfIndex;
|
this.selfIndex = selfIndex;
|
||||||
this.bindingRecord = bindingRecord;
|
this.bindingRecord = bindingRecord;
|
||||||
this.lastInBinding = lastInBinding;
|
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
|
* 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
|
* 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
|
* 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`.
|
* {@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.
|
* 5 would not be included, because it is not a direct child.
|
||||||
*
|
*
|
||||||
* ### Injecting a live collection of descendant directives
|
* ### Injecting a live collection of descendant directives
|
||||||
@ -444,6 +444,29 @@ export class Directive extends Injectable {
|
|||||||
*/
|
*/
|
||||||
hostListeners:any; // StringMap
|
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.
|
* Specifies a set of lifecycle hostListeners in which the directive participates.
|
||||||
*
|
*
|
||||||
@ -457,12 +480,14 @@ export class Directive extends Injectable {
|
|||||||
properties,
|
properties,
|
||||||
events,
|
events,
|
||||||
hostListeners,
|
hostListeners,
|
||||||
|
hostProperties,
|
||||||
lifecycle
|
lifecycle
|
||||||
}:{
|
}:{
|
||||||
selector:string,
|
selector:string,
|
||||||
properties:any,
|
properties:any,
|
||||||
events:List,
|
events:List,
|
||||||
hostListeners: any,
|
hostListeners: any,
|
||||||
|
hostProperties: any,
|
||||||
lifecycle:List
|
lifecycle:List
|
||||||
}={})
|
}={})
|
||||||
{
|
{
|
||||||
@ -471,6 +496,7 @@ export class Directive extends Injectable {
|
|||||||
this.properties = properties;
|
this.properties = properties;
|
||||||
this.events = events;
|
this.events = events;
|
||||||
this.hostListeners = hostListeners;
|
this.hostListeners = hostListeners;
|
||||||
|
this.hostProperties = hostProperties;
|
||||||
this.lifecycle = lifecycle;
|
this.lifecycle = lifecycle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -579,20 +605,22 @@ export class Component extends Directive {
|
|||||||
*/
|
*/
|
||||||
injectables:List;
|
injectables:List;
|
||||||
|
|
||||||
@CONST()
|
@CONST()
|
||||||
constructor({
|
constructor({
|
||||||
selector,
|
selector,
|
||||||
properties,
|
properties,
|
||||||
events,
|
events,
|
||||||
hostListeners,
|
hostListeners,
|
||||||
injectables,
|
hostProperties,
|
||||||
lifecycle,
|
injectables,
|
||||||
changeDetection = DEFAULT
|
lifecycle,
|
||||||
|
changeDetection = DEFAULT
|
||||||
}:{
|
}:{
|
||||||
selector:string,
|
selector:string,
|
||||||
properties:Object,
|
properties:Object,
|
||||||
events:List,
|
events:List,
|
||||||
hostListeners:Object,
|
hostListeners:any,
|
||||||
|
hostProperties:any,
|
||||||
injectables:List,
|
injectables:List,
|
||||||
lifecycle:List,
|
lifecycle:List,
|
||||||
changeDetection:string
|
changeDetection:string
|
||||||
@ -603,6 +631,7 @@ export class Component extends Directive {
|
|||||||
properties: properties,
|
properties: properties,
|
||||||
events: events,
|
events: events,
|
||||||
hostListeners: hostListeners,
|
hostListeners: hostListeners,
|
||||||
|
hostProperties: hostProperties,
|
||||||
lifecycle: lifecycle
|
lifecycle: lifecycle
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -667,17 +696,19 @@ export class DynamicComponent extends Directive {
|
|||||||
|
|
||||||
@CONST()
|
@CONST()
|
||||||
constructor({
|
constructor({
|
||||||
selector,
|
selector,
|
||||||
properties,
|
properties,
|
||||||
events,
|
events,
|
||||||
hostListeners,
|
hostListeners,
|
||||||
injectables,
|
hostProperties,
|
||||||
lifecycle
|
injectables,
|
||||||
|
lifecycle
|
||||||
}:{
|
}:{
|
||||||
selector:string,
|
selector:string,
|
||||||
properties:Object,
|
properties:any,
|
||||||
events:List,
|
events:List,
|
||||||
hostListeners:Object,
|
hostListeners:any,
|
||||||
|
hostProperties:any,
|
||||||
injectables:List,
|
injectables:List,
|
||||||
lifecycle:List
|
lifecycle:List
|
||||||
}={}) {
|
}={}) {
|
||||||
@ -686,6 +717,7 @@ export class DynamicComponent extends Directive {
|
|||||||
properties: properties,
|
properties: properties,
|
||||||
events: events,
|
events: events,
|
||||||
hostListeners: hostListeners,
|
hostListeners: hostListeners,
|
||||||
|
hostProperties: hostProperties,
|
||||||
lifecycle: lifecycle
|
lifecycle: lifecycle
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -767,6 +799,7 @@ export class Decorator extends Directive {
|
|||||||
properties,
|
properties,
|
||||||
events,
|
events,
|
||||||
hostListeners,
|
hostListeners,
|
||||||
|
hostProperties,
|
||||||
lifecycle,
|
lifecycle,
|
||||||
compileChildren = true,
|
compileChildren = true,
|
||||||
}:{
|
}:{
|
||||||
@ -774,16 +807,18 @@ export class Decorator extends Directive {
|
|||||||
properties:any,
|
properties:any,
|
||||||
events:List,
|
events:List,
|
||||||
hostListeners:any,
|
hostListeners:any,
|
||||||
|
hostProperties:any,
|
||||||
lifecycle:List,
|
lifecycle:List,
|
||||||
compileChildren:boolean
|
compileChildren:boolean
|
||||||
}={})
|
}={})
|
||||||
{
|
{
|
||||||
super({
|
super({
|
||||||
selector: selector,
|
selector: selector,
|
||||||
properties: properties,
|
properties: properties,
|
||||||
events: events,
|
events: events,
|
||||||
hostListeners: hostListeners,
|
hostListeners: hostListeners,
|
||||||
lifecycle: lifecycle
|
hostProperties: hostProperties,
|
||||||
|
lifecycle: lifecycle
|
||||||
});
|
});
|
||||||
this.compileChildren = compileChildren;
|
this.compileChildren = compileChildren;
|
||||||
}
|
}
|
||||||
@ -889,20 +924,24 @@ export class Viewport extends Directive {
|
|||||||
properties,
|
properties,
|
||||||
events,
|
events,
|
||||||
hostListeners,
|
hostListeners,
|
||||||
|
hostProperties,
|
||||||
lifecycle
|
lifecycle
|
||||||
}:{
|
}:{
|
||||||
selector:string,
|
selector:string,
|
||||||
properties:any,
|
properties:any,
|
||||||
events:List,
|
hostListeners:any,
|
||||||
lifecycle:List
|
hostProperties:any,
|
||||||
|
events:List,
|
||||||
|
lifecycle:List
|
||||||
}={})
|
}={})
|
||||||
{
|
{
|
||||||
super({
|
super({
|
||||||
selector: selector,
|
selector: selector,
|
||||||
properties: properties,
|
properties: properties,
|
||||||
events: events,
|
events: events,
|
||||||
hostListeners: hostListeners,
|
hostListeners: hostListeners,
|
||||||
lifecycle: lifecycle
|
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 {CONST} from 'angular2/src/facade/lang';
|
||||||
import {DependencyAnnotation} from 'angular2/di';
|
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.
|
* Specifies that a constant attribute value should be injected.
|
||||||
*
|
*
|
||||||
|
@ -223,12 +223,8 @@ export class Compiler {
|
|||||||
renderType = renderApi.DirectiveMetadata.DECORATOR_TYPE;
|
renderType = renderApi.DirectiveMetadata.DECORATOR_TYPE;
|
||||||
compileChildren = ann.compileChildren;
|
compileChildren = ann.compileChildren;
|
||||||
}
|
}
|
||||||
var setters = [];
|
|
||||||
var readAttributes = [];
|
var readAttributes = [];
|
||||||
ListWrapper.forEach(directiveBinding.dependencies, (dep) => {
|
ListWrapper.forEach(directiveBinding.dependencies, (dep) => {
|
||||||
if (isPresent(dep.propSetterName)) {
|
|
||||||
ListWrapper.push(setters, dep.propSetterName);
|
|
||||||
}
|
|
||||||
if (isPresent(dep.attributeName)) {
|
if (isPresent(dep.attributeName)) {
|
||||||
ListWrapper.push(readAttributes, dep.attributeName);
|
ListWrapper.push(readAttributes, dep.attributeName);
|
||||||
}
|
}
|
||||||
@ -239,8 +235,8 @@ export class Compiler {
|
|||||||
selector: ann.selector,
|
selector: ann.selector,
|
||||||
compileChildren: compileChildren,
|
compileChildren: compileChildren,
|
||||||
hostListeners: isPresent(ann.hostListeners) ? MapWrapper.createFromStringMap(ann.hostListeners) : null,
|
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,
|
properties: isPresent(ann.properties) ? MapWrapper.createFromStringMap(ann.properties) : null,
|
||||||
setters: setters,
|
|
||||||
readAttributes: readAttributes
|
readAttributes: readAttributes
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import {List, ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
|||||||
import {Injector, Key, Dependency, bind, Binding, ResolvedBinding, NoBindingError,
|
import {Injector, Key, Dependency, bind, Binding, ResolvedBinding, NoBindingError,
|
||||||
AbstractBindingError, CyclicDependencyError} from 'angular2/di';
|
AbstractBindingError, CyclicDependencyError} from 'angular2/di';
|
||||||
import {Parent, Ancestor} from 'angular2/src/core/annotations/visibility';
|
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 * as viewModule from 'angular2/src/core/compiler/view';
|
||||||
import {ViewContainer} from 'angular2/src/core/compiler/view_container';
|
import {ViewContainer} from 'angular2/src/core/compiler/view_container';
|
||||||
import {NgElement} from 'angular2/src/core/compiler/ng_element';
|
import {NgElement} from 'angular2/src/core/compiler/ng_element';
|
||||||
@ -195,15 +195,13 @@ export class TreeNode {
|
|||||||
|
|
||||||
export class DirectiveDependency extends Dependency {
|
export class DirectiveDependency extends Dependency {
|
||||||
depth:int;
|
depth:int;
|
||||||
propSetterName:string;
|
|
||||||
attributeName:string;
|
attributeName:string;
|
||||||
queryDirective;
|
queryDirective;
|
||||||
|
|
||||||
constructor(key:Key, asPromise:boolean, lazy:boolean, optional:boolean, properties:List,
|
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);
|
super(key, asPromise, lazy, optional, properties);
|
||||||
this.depth = depth;
|
this.depth = depth;
|
||||||
this.propSetterName = propSetterName;
|
|
||||||
this.attributeName = attributeName;
|
this.attributeName = attributeName;
|
||||||
this.queryDirective = queryDirective;
|
this.queryDirective = queryDirective;
|
||||||
this._verify();
|
this._verify();
|
||||||
@ -211,17 +209,15 @@ export class DirectiveDependency extends Dependency {
|
|||||||
|
|
||||||
_verify():void {
|
_verify():void {
|
||||||
var count = 0;
|
var count = 0;
|
||||||
if (isPresent(this.propSetterName)) count++;
|
|
||||||
if (isPresent(this.queryDirective)) count++;
|
if (isPresent(this.queryDirective)) count++;
|
||||||
if (isPresent(this.attributeName)) count++;
|
if (isPresent(this.attributeName)) count++;
|
||||||
if (count > 1) throw new BaseException(
|
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 {
|
static createFrom(d:Dependency):Dependency {
|
||||||
return new DirectiveDependency(d.key, d.asPromise, d.lazy, d.optional,
|
return new DirectiveDependency(d.key, d.asPromise, d.lazy, d.optional,
|
||||||
d.properties, DirectiveDependency._depth(d.properties),
|
d.properties, DirectiveDependency._depth(d.properties),
|
||||||
DirectiveDependency._propSetterName(d.properties),
|
|
||||||
DirectiveDependency._attributeName(d.properties),
|
DirectiveDependency._attributeName(d.properties),
|
||||||
DirectiveDependency._query(d.properties)
|
DirectiveDependency._query(d.properties)
|
||||||
);
|
);
|
||||||
@ -234,11 +230,6 @@ export class DirectiveDependency extends Dependency {
|
|||||||
return 0;
|
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 {
|
static _attributeName(properties):string {
|
||||||
var p = ListWrapper.find(properties, (p) => p instanceof Attribute);
|
var p = ListWrapper.find(properties, (p) => p instanceof Attribute);
|
||||||
return isPresent(p) ? p.attributeName : null;
|
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> {
|
get eventEmitters():List<string> {
|
||||||
return isPresent(this.annotation) && isPresent(this.annotation.events) ? this.annotation.events : [];
|
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) {
|
_getByDependency(dep:DirectiveDependency, requestor:Key) {
|
||||||
if (isPresent(dep.propSetterName)) return this._buildPropSetter(dep);
|
|
||||||
if (isPresent(dep.attributeName)) return this._buildAttribute(dep);
|
if (isPresent(dep.attributeName)) return this._buildAttribute(dep);
|
||||||
if (isPresent(dep.queryDirective)) return this._findQuery(dep.queryDirective).list;
|
if (isPresent(dep.queryDirective)) return this._findQuery(dep.queryDirective).list;
|
||||||
if (dep.key.id === StaticKeys.instance().elementRefId) {
|
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);
|
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 {
|
_buildAttribute(dep): string {
|
||||||
var attributes = this._proto.attributes;
|
var attributes = this._proto.attributes;
|
||||||
if (isPresent(attributes) && MapWrapper.contains(attributes, dep.attributeName)) {
|
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 {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||||
import {reflector} from 'angular2/src/reflection/reflection';
|
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 {Component, Viewport, DynamicComponent} from '../annotations/annotations';
|
||||||
|
|
||||||
import * as renderApi from 'angular2/src/render/api';
|
import * as renderApi from 'angular2/src/render/api';
|
||||||
@ -18,7 +18,8 @@ export class ProtoViewFactory {
|
|||||||
this._changeDetection = changeDetection;
|
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;
|
var protoChangeDetector;
|
||||||
if (isBlank(componentBinding)) {
|
if (isBlank(componentBinding)) {
|
||||||
protoChangeDetector = this._changeDetection.createProtoChangeDetector('root', null);
|
protoChangeDetector = this._changeDetection.createProtoChangeDetector('root', null);
|
||||||
@ -43,7 +44,7 @@ export class ProtoViewFactory {
|
|||||||
this._createElementBinder(
|
this._createElementBinder(
|
||||||
protoView, renderElementBinder, protoElementInjector, sortedDirectives
|
protoView, renderElementBinder, protoElementInjector, sortedDirectives
|
||||||
);
|
);
|
||||||
this._createDirectiveBinders(protoView, sortedDirectives);
|
this._createDirectiveBinders(protoView, i, sortedDirectives);
|
||||||
}
|
}
|
||||||
MapWrapper.forEach(renderProtoView.variableBindings, (mappedName, varName) => {
|
MapWrapper.forEach(renderProtoView.variableBindings, (mappedName, varName) => {
|
||||||
protoView.bindVariable(varName, mappedName);
|
protoView.bindVariable(varName, mappedName);
|
||||||
@ -128,27 +129,34 @@ export class ProtoViewFactory {
|
|||||||
return elBinder;
|
return elBinder;
|
||||||
}
|
}
|
||||||
|
|
||||||
_createDirectiveBinders(protoView, sortedDirectives) {
|
_createDirectiveBinders(protoView, boundElementIndex, sortedDirectives) {
|
||||||
for (var i=0; i<sortedDirectives.renderDirectives.length; i++) {
|
for (var i = 0; i < sortedDirectives.renderDirectives.length; i++) {
|
||||||
var renderDirectiveMetadata = sortedDirectives.renderDirectives[i];
|
var directiveBinder = sortedDirectives.renderDirectives[i];
|
||||||
|
|
||||||
// directive properties
|
// 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
|
// TODO: these setters should eventually be created by change detection, to make
|
||||||
// it monomorphic!
|
// it monomorphic!
|
||||||
var setter = reflector.setter(propertyName);
|
var setter = reflector.setter(propertyName);
|
||||||
protoView.bindDirectiveProperty(i, astWithSource, propertyName, setter);
|
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
|
// directive events
|
||||||
protoView.bindEvent(renderDirectiveMetadata.eventBindings, i);
|
protoView.bindEvent(directiveBinder.eventBindings, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class SortedDirectives {
|
class SortedDirectives {
|
||||||
componentDirective: DirectiveBinding;
|
componentDirective: DirectiveBinding;
|
||||||
viewportDirective: DirectiveBinding;
|
viewportDirective: DirectiveBinding;
|
||||||
renderDirectives: List<renderApi.DirectiveMetadata>;
|
renderDirectives: List<renderApi.DirectiveBinder>;
|
||||||
directives: List<DirectiveBinding>;
|
directives: List<DirectiveBinding>;
|
||||||
|
|
||||||
constructor(renderDirectives, allDirectives) {
|
constructor(renderDirectives, allDirectives) {
|
||||||
@ -156,18 +164,18 @@ class SortedDirectives {
|
|||||||
this.directives = [];
|
this.directives = [];
|
||||||
this.viewportDirective = null;
|
this.viewportDirective = null;
|
||||||
this.componentDirective = null;
|
this.componentDirective = null;
|
||||||
ListWrapper.forEach(renderDirectives, (renderDirectiveMetadata) => {
|
ListWrapper.forEach(renderDirectives, (renderDirectiveBinder) => {
|
||||||
var directiveBinding = allDirectives[renderDirectiveMetadata.directiveIndex];
|
var directiveBinding = allDirectives[renderDirectiveBinder.directiveIndex];
|
||||||
if ((directiveBinding.annotation instanceof Component) || (directiveBinding.annotation instanceof DynamicComponent)) {
|
if ((directiveBinding.annotation instanceof Component) || (directiveBinding.annotation instanceof DynamicComponent)) {
|
||||||
// component directives need to be the first binding in ElementInjectors!
|
// component directives need to be the first binding in ElementInjectors!
|
||||||
this.componentDirective = directiveBinding;
|
this.componentDirective = directiveBinding;
|
||||||
ListWrapper.insert(this.renderDirectives, 0, renderDirectiveMetadata);
|
ListWrapper.insert(this.renderDirectives, 0, renderDirectiveBinder);
|
||||||
ListWrapper.insert(this.directives, 0, directiveBinding);
|
ListWrapper.insert(this.directives, 0, directiveBinding);
|
||||||
} else {
|
} else {
|
||||||
if (directiveBinding.annotation instanceof Viewport) {
|
if (directiveBinding.annotation instanceof Viewport) {
|
||||||
this.viewportDirective = directiveBinding;
|
this.viewportDirective = directiveBinding;
|
||||||
}
|
}
|
||||||
ListWrapper.push(this.renderDirectives, renderDirectiveMetadata);
|
ListWrapper.push(this.renderDirectives, renderDirectiveBinder);
|
||||||
ListWrapper.push(this.directives, directiveBinding);
|
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 {ListWrapper, MapWrapper, Map, StringMapWrapper, List} from 'angular2/src/facade/collection';
|
||||||
import {AST, Locals, ChangeDispatcher, ProtoChangeDetector, ChangeDetector,
|
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 {ProtoElementInjector, ElementInjector, PreBuiltObjects, DirectiveBinding} from './element_injector';
|
||||||
import {ElementBinder} from './element_binder';
|
import {ElementBinder} from './element_binder';
|
||||||
@ -124,12 +124,12 @@ export class AppView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getDirectiveFor(directive:DirectiveRecord) {
|
getDirectiveFor(directive:DirectiveIndex) {
|
||||||
var elementInjector = this.elementInjectors[directive.elementIndex];
|
var elementInjector = this.elementInjectors[directive.elementIndex];
|
||||||
return elementInjector.getDirectiveAtIndex(directive.directiveIndex);
|
return elementInjector.getDirectiveAtIndex(directive.directiveIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
getDetectorFor(directive:DirectiveRecord) {
|
getDetectorFor(directive:DirectiveIndex) {
|
||||||
var elementInjector = this.elementInjectors[directive.elementIndex];
|
var elementInjector = this.elementInjectors[directive.elementIndex];
|
||||||
return elementInjector.getChangeDetector();
|
return elementInjector.getChangeDetector();
|
||||||
}
|
}
|
||||||
@ -267,6 +267,14 @@ export class AppProtoView {
|
|||||||
ListWrapper.push(this.bindings, b);
|
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.
|
* Adds an event binding for the last created ElementBinder via bindElement.
|
||||||
*
|
*
|
||||||
@ -323,7 +331,7 @@ export class AppProtoView {
|
|||||||
var changeDetection = binding.changeDetection;
|
var changeDetection = binding.changeDetection;
|
||||||
|
|
||||||
MapWrapper.set(this._directiveRecordsMap, id,
|
MapWrapper.set(this._directiveRecordsMap, id,
|
||||||
new DirectiveRecord(elementInjectorIndex, directiveIndex,
|
new DirectiveRecord(new DirectiveIndex(elementInjectorIndex, directiveIndex),
|
||||||
binding.callOnAllChangesDone, binding.callOnChange, changeDetection));
|
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 {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
|
// TODO: uncoment `int` once https://github.com/angular/angular/issues/1414 is fixed
|
||||||
|
|
||||||
@ -23,6 +23,10 @@ export class Key {
|
|||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get displayName() {
|
||||||
|
return stringify(this.token);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a `Key` for a 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 {Optional} from 'angular2/di';
|
||||||
|
import {Renderer} from 'angular2/src/render/api';
|
||||||
import {isBlank, isPresent, isString, CONST} from 'angular2/src/facade/lang';
|
import {isBlank, isPresent, isString, CONST} from 'angular2/src/facade/lang';
|
||||||
import {StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
import {StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||||
import {ControlGroup, Control} from './model';
|
import {ControlGroup, Control} from './model';
|
||||||
@ -27,19 +28,21 @@ import {Validators} from './validators';
|
|||||||
hostListeners: {
|
hostListeners: {
|
||||||
'change' : 'onChange($event.target.value)',
|
'change' : 'onChange($event.target.value)',
|
||||||
'input' : 'onChange($event.target.value)'
|
'input' : 'onChange($event.target.value)'
|
||||||
|
},
|
||||||
|
hostProperties: {
|
||||||
|
'value' : 'value'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
export class DefaultValueAccessor {
|
export class DefaultValueAccessor {
|
||||||
_setValueProperty:Function;
|
value;
|
||||||
onChange:Function;
|
onChange:Function;
|
||||||
|
|
||||||
constructor(@PropertySetter('value') setValueProperty:Function) {
|
constructor() {
|
||||||
this._setValueProperty = setValueProperty;
|
|
||||||
this.onChange = (_) => {};
|
this.onChange = (_) => {};
|
||||||
}
|
}
|
||||||
|
|
||||||
writeValue(value) {
|
writeValue(value) {
|
||||||
this._setValueProperty(value);
|
this.value = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,20 +61,28 @@ export class DefaultValueAccessor {
|
|||||||
selector: 'input[type=checkbox][control]',
|
selector: 'input[type=checkbox][control]',
|
||||||
hostListeners: {
|
hostListeners: {
|
||||||
'change' : 'onChange($event.target.checked)'
|
'change' : 'onChange($event.target.checked)'
|
||||||
|
},
|
||||||
|
hostProperties: {
|
||||||
|
'checked' : 'checked'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
export class CheckboxControlValueAccessor {
|
export class CheckboxControlValueAccessor {
|
||||||
_setCheckedProperty:Function;
|
_elementRef:ElementRef;
|
||||||
|
_renderer:Renderer;
|
||||||
|
|
||||||
|
checked:boolean;
|
||||||
onChange:Function;
|
onChange:Function;
|
||||||
|
|
||||||
constructor(cd:ControlDirective, @PropertySetter('checked') setCheckedProperty:Function) {
|
constructor(cd:ControlDirective, elementRef:ElementRef, renderer:Renderer) {
|
||||||
this._setCheckedProperty = setCheckedProperty;
|
|
||||||
this.onChange = (_) => {};
|
this.onChange = (_) => {};
|
||||||
|
this._elementRef = elementRef;
|
||||||
|
this._renderer = renderer;
|
||||||
cd.valueAccessor = this; //ControlDirective should inject CheckboxControlDirective
|
cd.valueAccessor = this; //ControlDirective should inject CheckboxControlDirective
|
||||||
}
|
}
|
||||||
|
|
||||||
writeValue(value) {
|
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
|
// that replaced the values that should be extracted from the element
|
||||||
// with a local name
|
// with a local name
|
||||||
eventBindings: List<EventBinding>;
|
eventBindings: List<EventBinding>;
|
||||||
|
hostPropertyBindings: Map<string, ASTWithSource>;
|
||||||
constructor({
|
constructor({
|
||||||
directiveIndex, propertyBindings, eventBindings
|
directiveIndex, propertyBindings, eventBindings, hostPropertyBindings
|
||||||
}) {
|
}) {
|
||||||
this.directiveIndex = directiveIndex;
|
this.directiveIndex = directiveIndex;
|
||||||
this.propertyBindings = propertyBindings;
|
this.propertyBindings = propertyBindings;
|
||||||
this.eventBindings = eventBindings;
|
this.eventBindings = eventBindings;
|
||||||
|
this.hostPropertyBindings = hostPropertyBindings;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,17 +116,17 @@ export class DirectiveMetadata {
|
|||||||
selector:string;
|
selector:string;
|
||||||
compileChildren:boolean;
|
compileChildren:boolean;
|
||||||
hostListeners:Map<string, string>;
|
hostListeners:Map<string, string>;
|
||||||
|
hostProperties:Map<string, string>;
|
||||||
properties:Map<string, string>;
|
properties:Map<string, string>;
|
||||||
setters:List<string>;
|
|
||||||
readAttributes:List<string>;
|
readAttributes:List<string>;
|
||||||
type:number;
|
type:number;
|
||||||
constructor({id, selector, compileChildren, hostListeners, properties, setters, readAttributes, type}) {
|
constructor({id, selector, compileChildren, hostListeners, hostProperties, properties, readAttributes, type}) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.selector = selector;
|
this.selector = selector;
|
||||||
this.compileChildren = isPresent(compileChildren) ? compileChildren : true;
|
this.compileChildren = isPresent(compileChildren) ? compileChildren : true;
|
||||||
this.hostListeners = hostListeners;
|
this.hostListeners = hostListeners;
|
||||||
|
this.hostProperties = hostProperties;
|
||||||
this.properties = properties;
|
this.properties = properties;
|
||||||
this.setters = setters;
|
|
||||||
this.readAttributes = readAttributes;
|
this.readAttributes = readAttributes;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
@ -248,7 +250,7 @@ export class Renderer {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a property on an element.
|
* 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.
|
* in the View.
|
||||||
*/
|
*/
|
||||||
setElementProperty(view:ViewRef, elementIndex:number, propertyName:string, propertyValue:any):void {}
|
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) => {
|
this._selectorMatcher.match(cssSelector, (selector, directiveIndex) => {
|
||||||
var elementBinder = current.bindElement();
|
var elementBinder = current.bindElement();
|
||||||
var directive = this._directives[directiveIndex];
|
var directive = this._directives[directiveIndex];
|
||||||
var directiveBinder = elementBinder.bindDirective(directiveIndex);
|
var directiveBinderBuilder = elementBinder.bindDirective(directiveIndex);
|
||||||
current.compileChildren = current.compileChildren && directive.compileChildren;
|
current.compileChildren = current.compileChildren && directive.compileChildren;
|
||||||
if (isPresent(directive.properties)) {
|
if (isPresent(directive.properties)) {
|
||||||
MapWrapper.forEach(directive.properties, (bindConfig, dirProperty) => {
|
MapWrapper.forEach(directive.properties, (bindConfig, dirProperty) => {
|
||||||
this._bindDirectiveProperty(dirProperty, bindConfig, current, directiveBinder);
|
this._bindDirectiveProperty(dirProperty, bindConfig, current, directiveBinderBuilder);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (isPresent(directive.hostListeners)) {
|
if (isPresent(directive.hostListeners)) {
|
||||||
MapWrapper.forEach(directive.hostListeners, (action, eventName) => {
|
MapWrapper.forEach(directive.hostListeners, (action, eventName) => {
|
||||||
this._bindDirectiveEvent(eventName, action, current, directiveBinder);
|
this._bindDirectiveEvent(eventName, action, current, directiveBinderBuilder);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (isPresent(directive.setters)) {
|
if (isPresent(directive.hostProperties)) {
|
||||||
ListWrapper.forEach(directive.setters, (propertyName) => {
|
MapWrapper.forEach(directive.hostProperties, (hostPropertyName, directivePropertyName) => {
|
||||||
elementBinder.bindPropertySetter(propertyName);
|
this._bindHostProperty(hostPropertyName, directivePropertyName, current, directiveBinderBuilder);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (isPresent(directive.readAttributes)) {
|
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 pipes = this._splitBindConfig(bindConfig);
|
||||||
var elProp = ListWrapper.removeAt(pipes, 0);
|
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.
|
// Bindings are optional, so this binding only needs to be set up if an expression is given.
|
||||||
if (isPresent(bindingAst)) {
|
if (isPresent(bindingAst)) {
|
||||||
var fullExpAstWithBindPipes = this._parser.addPipes(bindingAst, pipes);
|
var fullExpAstWithBindPipes = this._parser.addPipes(bindingAst, pipes);
|
||||||
directiveBinder.bindProperty(
|
directiveBinderBuilder.bindProperty(
|
||||||
dirProperty, fullExpAstWithBindPipes
|
dirProperty, fullExpAstWithBindPipes
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_bindDirectiveEvent(eventName, action, compileElement, directiveBinder) {
|
_bindDirectiveEvent(eventName, action, compileElement, directiveBinderBuilder) {
|
||||||
var ast = this._parser.parseAction(action, compileElement.elementDescription);
|
var ast = this._parser.parseAction(action, compileElement.elementDescription);
|
||||||
if (StringWrapper.contains(eventName, EVENT_TARGET_SEPARATOR)) {
|
if (StringWrapper.contains(eventName, EVENT_TARGET_SEPARATOR)) {
|
||||||
var parts = eventName.split(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 {
|
} 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) {
|
_splitBindConfig(bindConfig:string) {
|
||||||
|
@ -5,7 +5,6 @@ import {DOM} from 'angular2/src/dom/dom_adapter';
|
|||||||
import {
|
import {
|
||||||
ASTWithSource, AST, AstTransformer, AccessMember, LiteralArray, ImplicitReceiver
|
ASTWithSource, AST, AstTransformer, AccessMember, LiteralArray, ImplicitReceiver
|
||||||
} from 'angular2/change_detection';
|
} from 'angular2/change_detection';
|
||||||
import {SetterFn} from 'angular2/src/reflection/types';
|
|
||||||
|
|
||||||
import {RenderProtoView} from './proto_view';
|
import {RenderProtoView} from './proto_view';
|
||||||
import {ElementBinder, Event} from './element_binder';
|
import {ElementBinder, Event} from './element_binder';
|
||||||
@ -57,17 +56,26 @@ export class ProtoViewBuilder {
|
|||||||
var apiElementBinders = [];
|
var apiElementBinders = [];
|
||||||
ListWrapper.forEach(this.elements, (ebb) => {
|
ListWrapper.forEach(this.elements, (ebb) => {
|
||||||
var propertySetters = MapWrapper.create();
|
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({
|
return new api.DirectiveBinder({
|
||||||
directiveIndex: db.directiveIndex,
|
directiveIndex: dbb.directiveIndex,
|
||||||
propertyBindings: db.propertyBindings,
|
propertyBindings: dbb.propertyBindings,
|
||||||
eventBindings: db.eventBindings
|
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 =
|
var nestedProtoView =
|
||||||
isPresent(ebb.nestedProtoView) ? ebb.nestedProtoView.build() : null;
|
isPresent(ebb.nestedProtoView) ? ebb.nestedProtoView.build() : null;
|
||||||
var parentIndex = isPresent(ebb.parent) ? ebb.parent.index : -1;
|
var parentIndex = isPresent(ebb.parent) ? ebb.parent.index : -1;
|
||||||
@ -119,7 +127,6 @@ export class ElementBinderBuilder {
|
|||||||
textBindingIndices: List<number>;
|
textBindingIndices: List<number>;
|
||||||
textBindings: List<ASTWithSource>;
|
textBindings: List<ASTWithSource>;
|
||||||
contentTagSelector:string;
|
contentTagSelector:string;
|
||||||
propertySetters: Map<string, SetterFn>;
|
|
||||||
readAttributes: Map<string, string>;
|
readAttributes: Map<string, string>;
|
||||||
componentId: string;
|
componentId: string;
|
||||||
|
|
||||||
@ -137,7 +144,6 @@ export class ElementBinderBuilder {
|
|||||||
this.textBindings = [];
|
this.textBindings = [];
|
||||||
this.textBindingIndices = [];
|
this.textBindingIndices = [];
|
||||||
this.contentTagSelector = null;
|
this.contentTagSelector = null;
|
||||||
this.propertySetters = MapWrapper.create();
|
|
||||||
this.componentId = null;
|
this.componentId = null;
|
||||||
this.readAttributes = MapWrapper.create();
|
this.readAttributes = MapWrapper.create();
|
||||||
}
|
}
|
||||||
@ -172,11 +178,10 @@ export class ElementBinderBuilder {
|
|||||||
|
|
||||||
bindProperty(name, expression) {
|
bindProperty(name, expression) {
|
||||||
MapWrapper.set(this.propertyBindings, name, expression);
|
MapWrapper.set(this.propertyBindings, name, expression);
|
||||||
this.bindPropertySetter(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
bindPropertySetter(name) {
|
//TODO: required for Dart transformers. Remove when Dart transformers
|
||||||
MapWrapper.set(this.propertySetters, name, setterFactory(name));
|
//run all the steps of the render compiler
|
||||||
|
setterFactory(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
bindVariable(name, value) {
|
bindVariable(name, value) {
|
||||||
@ -216,12 +221,14 @@ export class ElementBinderBuilder {
|
|||||||
export class DirectiveBuilder {
|
export class DirectiveBuilder {
|
||||||
directiveIndex:number;
|
directiveIndex:number;
|
||||||
propertyBindings: Map<string, ASTWithSource>;
|
propertyBindings: Map<string, ASTWithSource>;
|
||||||
|
hostPropertyBindings: Map<string, ASTWithSource>;
|
||||||
eventBindings: List<api.EventBinding>;
|
eventBindings: List<api.EventBinding>;
|
||||||
eventBuilder: EventBuilder;
|
eventBuilder: EventBuilder;
|
||||||
|
|
||||||
constructor(directiveIndex) {
|
constructor(directiveIndex) {
|
||||||
this.directiveIndex = directiveIndex;
|
this.directiveIndex = directiveIndex;
|
||||||
this.propertyBindings = MapWrapper.create();
|
this.propertyBindings = MapWrapper.create();
|
||||||
|
this.hostPropertyBindings = MapWrapper.create();
|
||||||
this.eventBindings = ListWrapper.create();
|
this.eventBindings = ListWrapper.create();
|
||||||
this.eventBuilder = new EventBuilder();
|
this.eventBuilder = new EventBuilder();
|
||||||
}
|
}
|
||||||
@ -230,6 +237,10 @@ export class DirectiveBuilder {
|
|||||||
MapWrapper.set(this.propertyBindings, name, expression);
|
MapWrapper.set(this.propertyBindings, name, expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bindHostProperty(name, expression) {
|
||||||
|
MapWrapper.set(this.hostPropertyBindings, name, expression);
|
||||||
|
}
|
||||||
|
|
||||||
bindEvent(name, expression, target = null) {
|
bindEvent(name, expression, target = null) {
|
||||||
ListWrapper.push(this.eventBindings, this.eventBuilder.add(name, expression, target));
|
ListWrapper.push(this.eventBindings, this.eventBuilder.add(name, expression, target));
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ class _DirectiveMetadataVisitor extends Object
|
|||||||
compileChildren: false,
|
compileChildren: false,
|
||||||
properties: {},
|
properties: {},
|
||||||
hostListeners: {},
|
hostListeners: {},
|
||||||
setters: [],
|
hostProperties: {},
|
||||||
readAttributes: []);
|
readAttributes: []);
|
||||||
super.visitInstanceCreationExpression(node);
|
super.visitInstanceCreationExpression(node);
|
||||||
}
|
}
|
||||||
@ -76,6 +76,9 @@ class _DirectiveMetadataVisitor extends Object
|
|||||||
case 'properties':
|
case 'properties':
|
||||||
_populateProperties(node.expression);
|
_populateProperties(node.expression);
|
||||||
break;
|
break;
|
||||||
|
case 'hostProperties':
|
||||||
|
_populateHostProperties(node.expression);
|
||||||
|
break;
|
||||||
case 'hostListeners':
|
case 'hostListeners':
|
||||||
_populateHostListeners(node.expression);
|
_populateHostListeners(node.expression);
|
||||||
}
|
}
|
||||||
@ -135,4 +138,19 @@ class _DirectiveMetadataVisitor extends Object
|
|||||||
meta.hostListeners[sKey] = sVal;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import {Parser} from 'angular2/src/change_detection/parser/parser';
|
|||||||
import {Lexer} from 'angular2/src/change_detection/parser/lexer';
|
import {Lexer} from 'angular2/src/change_detection/parser/lexer';
|
||||||
import {Locals} from 'angular2/src/change_detection/parser/locals';
|
import {Locals} from 'angular2/src/change_detection/parser/locals';
|
||||||
|
|
||||||
import {ChangeDispatcher, DynamicChangeDetector, ChangeDetectionError, BindingRecord, DirectiveRecord,
|
import {ChangeDispatcher, DynamicChangeDetector, ChangeDetectionError, BindingRecord, DirectiveRecord, DirectiveIndex,
|
||||||
PipeRegistry, Pipe, NO_CHANGE, CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED, ON_PUSH, DEFAULT} from 'angular2/change_detection';
|
PipeRegistry, Pipe, NO_CHANGE, CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED, ON_PUSH, DEFAULT} from 'angular2/change_detection';
|
||||||
|
|
||||||
import {JitProtoChangeDetector, DynamicProtoChangeDetector} from 'angular2/src/change_detection/proto_change_detector';
|
import {JitProtoChangeDetector, DynamicProtoChangeDetector} from 'angular2/src/change_detection/proto_change_detector';
|
||||||
@ -22,8 +22,9 @@ export function main() {
|
|||||||
|
|
||||||
if (name == "JIT" && IS_DARTIUM) return;
|
if (name == "JIT" && IS_DARTIUM) return;
|
||||||
|
|
||||||
|
var parser = new Parser(new Lexer());
|
||||||
|
|
||||||
function ast(exp:string, location:string = 'location') {
|
function ast(exp:string, location:string = 'location') {
|
||||||
var parser = new Parser(new Lexer());
|
|
||||||
return parser.parseBinding(exp, location);
|
return parser.parseBinding(exp, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,7 +205,6 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should support interpolation", () => {
|
it("should support interpolation", () => {
|
||||||
var parser = new Parser(new Lexer());
|
|
||||||
var pcd = createProtoChangeDetector();
|
var pcd = createProtoChangeDetector();
|
||||||
var ast = parser.parseInterpolation("B{{a}}A", "location");
|
var ast = parser.parseInterpolation("B{{a}}A", "location");
|
||||||
|
|
||||||
@ -246,9 +246,9 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("updating directives", () => {
|
describe("updating directives", () => {
|
||||||
var dirRecord1 = new DirectiveRecord(0, 0, true, true, DEFAULT);
|
var dirRecord1 = new DirectiveRecord(new DirectiveIndex(0, 0), true, true, DEFAULT);
|
||||||
var dirRecord2 = new DirectiveRecord(0, 1, true, true, DEFAULT);
|
var dirRecord2 = new DirectiveRecord(new DirectiveIndex(0, 1), true, true, DEFAULT);
|
||||||
var dirRecordNoCallbacks = new DirectiveRecord(0, 0, false, false, DEFAULT);
|
var dirRecordNoCallbacks = new DirectiveRecord(new DirectiveIndex(0, 0), false, false, DEFAULT);
|
||||||
|
|
||||||
function updateA(exp:string, dirRecord) {
|
function updateA(exp:string, dirRecord) {
|
||||||
return BindingRecord.createForDirective(ast(exp), "a", (o,v) => o.a = v, dirRecord);
|
return BindingRecord.createForDirective(ast(exp), "a", (o,v) => o.a = v, dirRecord);
|
||||||
@ -381,6 +381,24 @@ export function main() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("reading directives", () => {
|
||||||
|
var index = new DirectiveIndex(0, 0);
|
||||||
|
var dirRecord = new DirectiveRecord(index, false, false, DEFAULT);
|
||||||
|
|
||||||
|
it("should read directive properties", () => {
|
||||||
|
var directive = new TestDirective();
|
||||||
|
directive.a = "aaa";
|
||||||
|
|
||||||
|
var pcd = createProtoChangeDetector();
|
||||||
|
var cd = instantiate(pcd, dispatcher, [BindingRecord.createForHostProperty(index, ast("a"), "prop")], [dirRecord]);
|
||||||
|
cd.hydrate(null, null, dirs([directive]));
|
||||||
|
|
||||||
|
cd.detectChanges();
|
||||||
|
|
||||||
|
expect(dispatcher.loggedValues).toEqual(['aaa']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("enforce no new changes", () => {
|
describe("enforce no new changes", () => {
|
||||||
it("should throw when a record gets changed after it has been checked", () => {
|
it("should throw when a record gets changed after it has been checked", () => {
|
||||||
var pcd = createProtoChangeDetector();
|
var pcd = createProtoChangeDetector();
|
||||||
@ -567,7 +585,7 @@ export function main() {
|
|||||||
checkedDetector.mode = CHECKED;
|
checkedDetector.mode = CHECKED;
|
||||||
|
|
||||||
// this directive is a component with ON_PUSH change detection
|
// this directive is a component with ON_PUSH change detection
|
||||||
dirRecordWithOnPush = new DirectiveRecord(0, 0, false, false, ON_PUSH);
|
dirRecordWithOnPush = new DirectiveRecord(new DirectiveIndex(0, 0), false, false, ON_PUSH);
|
||||||
|
|
||||||
// a record updating a component
|
// a record updating a component
|
||||||
updateDirWithOnPushRecord =
|
updateDirWithOnPushRecord =
|
||||||
@ -898,12 +916,12 @@ class FakeDirectives {
|
|||||||
this.detectors = detectors;
|
this.detectors = detectors;
|
||||||
}
|
}
|
||||||
|
|
||||||
getDirectiveFor(directiveRecord:DirectiveRecord) {
|
getDirectiveFor(di:DirectiveIndex) {
|
||||||
return this.directives[directiveRecord.directiveIndex];
|
return this.directives[di.directiveIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
getDetectorFor(directiveRecord:DirectiveRecord) {
|
getDetectorFor(di:DirectiveIndex) {
|
||||||
return this.detectors[directiveRecord.directiveIndex];
|
return this.detectors[di.directiveIndex];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import {RECORD_TYPE_SELF, ProtoRecord} from 'angular2/src/change_detection/proto
|
|||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
function r(funcOrValue, args, contextIndex, selfIndex, lastInBinding = false) {
|
function r(funcOrValue, args, contextIndex, selfIndex, lastInBinding = false) {
|
||||||
return new ProtoRecord(99, "name", funcOrValue, args, null, contextIndex, selfIndex,
|
return new ProtoRecord(99, "name", funcOrValue, args, null, contextIndex, null, selfIndex,
|
||||||
null, null, lastInBinding, false);
|
null, null, lastInBinding, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ export function main() {
|
|||||||
|
|
||||||
expect(rs[1]).toEqual(new ProtoRecord(
|
expect(rs[1]).toEqual(new ProtoRecord(
|
||||||
RECORD_TYPE_SELF, "self", null,
|
RECORD_TYPE_SELF, "self", null,
|
||||||
[], null, 1, 2,
|
[], null, 1, null, 2,
|
||||||
null, null,
|
null, null,
|
||||||
true, false)
|
true, false)
|
||||||
);
|
);
|
||||||
|
@ -22,7 +22,7 @@ import {AppProtoView} from 'angular2/src/core/compiler/view';
|
|||||||
import {ElementBinder} from 'angular2/src/core/compiler/element_binder';
|
import {ElementBinder} from 'angular2/src/core/compiler/element_binder';
|
||||||
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
|
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
|
||||||
import {Component, DynamicComponent, Viewport, Decorator} from 'angular2/src/core/annotations/annotations';
|
import {Component, DynamicComponent, Viewport, Decorator} from 'angular2/src/core/annotations/annotations';
|
||||||
import {PropertySetter, Attribute} from 'angular2/src/core/annotations/di';
|
import {Attribute} from 'angular2/src/core/annotations/di';
|
||||||
import {View} from 'angular2/src/core/annotations/view';
|
import {View} from 'angular2/src/core/annotations/view';
|
||||||
import {DirectiveBinding} from 'angular2/src/core/compiler/element_injector';
|
import {DirectiveBinding} from 'angular2/src/core/compiler/element_injector';
|
||||||
import {TemplateResolver} from 'angular2/src/core/compiler/template_resolver';
|
import {TemplateResolver} from 'angular2/src/core/compiler/template_resolver';
|
||||||
@ -185,18 +185,20 @@ export function main() {
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should set directive.bind', inject([AsyncTestCompleter], (async) => {
|
it('should set directive.hostProperties', inject([AsyncTestCompleter], (async) => {
|
||||||
captureDirective(DirectiveWithBind).then( (renderDir) => {
|
captureDirective(DirectiveWithProperties).then( (renderDir) => {
|
||||||
expect(renderDir.properties).toEqual(MapWrapper.createFromStringMap({
|
expect(renderDir.hostProperties).toEqual(MapWrapper.createFromStringMap({
|
||||||
'a': 'b'
|
'someField': 'someProp'
|
||||||
}));
|
}));
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should read @PropertySetter', inject([AsyncTestCompleter], (async) => {
|
it('should set directive.bind', inject([AsyncTestCompleter], (async) => {
|
||||||
captureDirective(DirectiveWithPropertySetters).then( (renderDir) => {
|
captureDirective(DirectiveWithBind).then( (renderDir) => {
|
||||||
expect(renderDir.setters).toEqual(['someProp']);
|
expect(renderDir.properties).toEqual(MapWrapper.createFromStringMap({
|
||||||
|
'a': 'b'
|
||||||
|
}));
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
@ -500,16 +502,16 @@ class IgnoreChildrenDecoratorDirective {}
|
|||||||
})
|
})
|
||||||
class DirectiveWithEvents {}
|
class DirectiveWithEvents {}
|
||||||
|
|
||||||
|
@Decorator({
|
||||||
|
hostProperties: {'someField': 'someProp'}
|
||||||
|
})
|
||||||
|
class DirectiveWithProperties {}
|
||||||
|
|
||||||
@Decorator({
|
@Decorator({
|
||||||
properties: {'a': 'b'}
|
properties: {'a': 'b'}
|
||||||
})
|
})
|
||||||
class DirectiveWithBind {}
|
class DirectiveWithBind {}
|
||||||
|
|
||||||
@Decorator()
|
|
||||||
class DirectiveWithPropertySetters {
|
|
||||||
constructor(@PropertySetter('someProp') someProp) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Decorator()
|
@Decorator()
|
||||||
class DirectiveWithAttributes {
|
class DirectiveWithAttributes {
|
||||||
constructor(@Attribute('someAttr') someAttr:string) {}
|
constructor(@Attribute('someAttr') someAttr:string) {}
|
||||||
@ -568,7 +570,8 @@ class FakeProtoViewFactory extends ProtoViewFactory {
|
|||||||
this._results = results;
|
this._results = results;
|
||||||
}
|
}
|
||||||
|
|
||||||
createProtoView(componentBinding:DirectiveBinding, renderProtoView: renderApi.ProtoViewDto, directives:List<DirectiveBinding>):AppProtoView {
|
createProtoView(componentBinding:DirectiveBinding, renderProtoView: renderApi.ProtoViewDto,
|
||||||
|
directives:List<DirectiveBinding>):AppProtoView {
|
||||||
ListWrapper.push(this.requests, [componentBinding, renderProtoView, directives]);
|
ListWrapper.push(this.requests, [componentBinding, renderProtoView, directives]);
|
||||||
return ListWrapper.removeAt(this._results, 0);
|
return ListWrapper.removeAt(this._results, 0);
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import {ListWrapper, MapWrapper, List, StringMapWrapper, iterateListLike} from '
|
|||||||
import {ProtoElementInjector, PreBuiltObjects, DirectiveBinding, TreeNode, ElementRef}
|
import {ProtoElementInjector, PreBuiltObjects, DirectiveBinding, TreeNode, ElementRef}
|
||||||
from 'angular2/src/core/compiler/element_injector';
|
from 'angular2/src/core/compiler/element_injector';
|
||||||
import {Parent, Ancestor} from 'angular2/src/core/annotations/visibility';
|
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 {onDestroy} from 'angular2/src/core/annotations/annotations';
|
import {onDestroy} from 'angular2/src/core/annotations/annotations';
|
||||||
import {Optional, Injector, Inject, bind} from 'angular2/di';
|
import {Optional, Injector, Inject, bind} from 'angular2/di';
|
||||||
import {AppProtoView, AppView} from 'angular2/src/core/compiler/view';
|
import {AppProtoView, AppView} from 'angular2/src/core/compiler/view';
|
||||||
@ -87,51 +87,6 @@ class HasEventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class NeedsPropertySetter {
|
|
||||||
propSetter;
|
|
||||||
roleSetter;
|
|
||||||
classSetter;
|
|
||||||
classWithDashSetter;
|
|
||||||
styleSetter;
|
|
||||||
unitSetter;
|
|
||||||
constructor(@PropertySetter('title') propSetter: Function, @PropertySetter('attr.role') roleSetter: Function,
|
|
||||||
@PropertySetter('class.active') classSetter: Function, @PropertySetter('class.foo-bar') classWithDashSetter: Function,
|
|
||||||
@PropertySetter('style.width') styleSetter: Function, @PropertySetter('style.height.px') unitSetter: Function) {
|
|
||||||
this.propSetter = propSetter;
|
|
||||||
this.roleSetter = roleSetter;
|
|
||||||
this.classSetter = classSetter;
|
|
||||||
this.classWithDashSetter = classWithDashSetter;
|
|
||||||
this.styleSetter = styleSetter;
|
|
||||||
this.unitSetter = unitSetter;
|
|
||||||
}
|
|
||||||
setProp(value) {
|
|
||||||
this.propSetter(value);
|
|
||||||
}
|
|
||||||
setRole(value) {
|
|
||||||
this.roleSetter(value);
|
|
||||||
}
|
|
||||||
setClass(value) {
|
|
||||||
this.classSetter(value);
|
|
||||||
}
|
|
||||||
setStyle(value) {
|
|
||||||
this.styleSetter(value);
|
|
||||||
}
|
|
||||||
setStyleWithUnit(value) {
|
|
||||||
this.unitSetter(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class NeedsPropertySetterNoType {
|
|
||||||
propSetter;
|
|
||||||
constructor(@PropertySetter('title') propSetter) {
|
|
||||||
this.propSetter = propSetter;
|
|
||||||
}
|
|
||||||
|
|
||||||
setProp(value) {
|
|
||||||
this.propSetter(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class NeedsAttribute {
|
class NeedsAttribute {
|
||||||
typeAttribute;
|
typeAttribute;
|
||||||
titleAttribute;
|
titleAttribute;
|
||||||
@ -708,45 +663,6 @@ export function main() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('property setter', () => {
|
|
||||||
var renderer, view;
|
|
||||||
|
|
||||||
beforeEach( () => {
|
|
||||||
renderer = new FakeRenderer();
|
|
||||||
var protoView = new AppProtoView(null, null);
|
|
||||||
view = new AppView(renderer, null, protoView, MapWrapper.create());
|
|
||||||
view.render = new ViewRef();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be injectable and callable', () => {
|
|
||||||
var preBuildObject = new PreBuiltObjects(view, null, null);
|
|
||||||
var inj = injector([NeedsPropertySetter], null, null, preBuildObject);
|
|
||||||
var component = inj.get(NeedsPropertySetter);
|
|
||||||
component.setProp('foobar');
|
|
||||||
component.setRole('button');
|
|
||||||
component.setClass(true);
|
|
||||||
component.classWithDashSetter(true);
|
|
||||||
component.setStyle('40px');
|
|
||||||
component.setStyleWithUnit(50);
|
|
||||||
|
|
||||||
expect(renderer.log[0]).toEqual([view.render, 0, 'title', 'foobar']);
|
|
||||||
expect(renderer.log[1]).toEqual([view.render, 0, 'attr.role', 'button']);
|
|
||||||
expect(renderer.log[2]).toEqual([view.render, 0, 'class.active', true]);
|
|
||||||
expect(renderer.log[3]).toEqual([view.render, 0, 'class.foo-bar', true]);
|
|
||||||
expect(renderer.log[4]).toEqual([view.render, 0, 'style.width', '40px']);
|
|
||||||
expect(renderer.log[5]).toEqual([view.render, 0, 'style.height.px', 50]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be injectable and callable without specifying param type annotation', () => {
|
|
||||||
var preBuildObject = new PreBuiltObjects(view, null, null);
|
|
||||||
var inj = injector([NeedsPropertySetterNoType], null, null, preBuildObject);
|
|
||||||
var component = inj.get(NeedsPropertySetterNoType);
|
|
||||||
component.setProp('foobar');
|
|
||||||
|
|
||||||
expect(renderer.log[0]).toEqual([view.render, 0, 'title', 'foobar']);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('static attributes', () => {
|
describe('static attributes', () => {
|
||||||
it('should be injectable', () => {
|
it('should be injectable', () => {
|
||||||
var attributes = MapWrapper.create();
|
var attributes = MapWrapper.create();
|
||||||
|
@ -190,7 +190,6 @@ export function main() {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
tb.createView(MyComp, {context: ctx}).then((view) => {
|
tb.createView(MyComp, {context: ctx}).then((view) => {
|
||||||
|
|
||||||
ctx.ctxProp = 'a';
|
ctx.ctxProp = 'a';
|
||||||
view.detectChanges();
|
view.detectChanges();
|
||||||
|
|
||||||
@ -591,6 +590,26 @@ export function main() {
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should support updating host element via hostProperties', inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||||
|
tb.overrideView(MyComp, new View({
|
||||||
|
template: '<div update-host-properties></div>',
|
||||||
|
directives: [DecoratorUpdatingHostProperties]
|
||||||
|
}));
|
||||||
|
|
||||||
|
tb.createView(MyComp, {context: ctx}).then((view) => {
|
||||||
|
var injector = view.rawView.elementInjectors[0];
|
||||||
|
var updateHost = injector.get(DecoratorUpdatingHostProperties);
|
||||||
|
|
||||||
|
updateHost.id = "newId";
|
||||||
|
|
||||||
|
view.detectChanges();
|
||||||
|
|
||||||
|
expect(view.rootNodes[0].id).toEqual("newId");
|
||||||
|
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
if (DOM.supportsDOMEvents()) {
|
if (DOM.supportsDOMEvents()) {
|
||||||
it('should support preventing default on render events', inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
it('should support preventing default on render events', inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||||
tb.overrideView(MyComp, new View({
|
tb.overrideView(MyComp, new View({
|
||||||
@ -1047,6 +1066,20 @@ class DecoratorEmitingEvent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Decorator({
|
||||||
|
selector: '[update-host-properties]',
|
||||||
|
hostProperties: {
|
||||||
|
'id' : 'id'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
class DecoratorUpdatingHostProperties {
|
||||||
|
id:string;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.id = "one";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Decorator({
|
@Decorator({
|
||||||
selector: '[listener]',
|
selector: '[listener]',
|
||||||
hostListeners: {'event': 'onEvent($event)'}
|
hostListeners: {'event': 'onEvent($event)'}
|
||||||
|
16
modules/angular2/test/forms/integration_spec.js
vendored
16
modules/angular2/test/forms/integration_spec.js
vendored
@ -16,7 +16,7 @@ import {DOM} from 'angular2/src/dom/dom_adapter';
|
|||||||
|
|
||||||
import {Inject} from 'angular2/di';
|
import {Inject} from 'angular2/di';
|
||||||
|
|
||||||
import {Component, Decorator, View, PropertySetter} from 'angular2/angular2';
|
import {Component, Decorator, View} from 'angular2/angular2';
|
||||||
|
|
||||||
import {TestBed} from 'angular2/src/test_lib/test_bed';
|
import {TestBed} from 'angular2/src/test_lib/test_bed';
|
||||||
|
|
||||||
@ -374,8 +374,8 @@ export function main() {
|
|||||||
ControlDirective,
|
ControlDirective,
|
||||||
WrappedValue,
|
WrappedValue,
|
||||||
RequiredValidatorDirective,
|
RequiredValidatorDirective,
|
||||||
CheckboxControlValueAccessor,
|
DefaultValueAccessor,
|
||||||
DefaultValueAccessor]})
|
CheckboxControlValueAccessor]})
|
||||||
class MyComp {
|
class MyComp {
|
||||||
form:any;
|
form:any;
|
||||||
name:string;
|
name:string;
|
||||||
@ -390,19 +390,21 @@ class MyComp {
|
|||||||
selector:'[wrapped-value]',
|
selector:'[wrapped-value]',
|
||||||
hostListeners: {
|
hostListeners: {
|
||||||
'change' : 'handleOnChange($event.target.value)'
|
'change' : 'handleOnChange($event.target.value)'
|
||||||
|
},
|
||||||
|
hostProperties: {
|
||||||
|
'value' : 'value'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
class WrappedValue {
|
class WrappedValue {
|
||||||
_setProperty:Function;
|
value;
|
||||||
onChange:Function;
|
onChange:Function;
|
||||||
|
|
||||||
constructor(cd:ControlDirective, @PropertySetter('value') setProperty:Function) {
|
constructor(cd:ControlDirective) {
|
||||||
this._setProperty = setProperty;
|
|
||||||
cd.valueAccessor = this;
|
cd.valueAccessor = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
writeValue(value) {
|
writeValue(value) {
|
||||||
this._setProperty(`!${value}!`);
|
this.value = `!${value}!`;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleOnChange(value) {
|
handleOnChange(value) {
|
||||||
|
@ -23,6 +23,7 @@ export function main() {
|
|||||||
someDecorator,
|
someDecorator,
|
||||||
someDecoratorIgnoringChildren,
|
someDecoratorIgnoringChildren,
|
||||||
someDecoratorWithProps,
|
someDecoratorWithProps,
|
||||||
|
someDecoratorWithHostProperties,
|
||||||
someDecoratorWithEvents,
|
someDecoratorWithEvents,
|
||||||
someDecoratorWithGlobalEvents
|
someDecoratorWithGlobalEvents
|
||||||
];
|
];
|
||||||
@ -112,12 +113,14 @@ export function main() {
|
|||||||
expect(simpleProp.source).toEqual('someValue');
|
expect(simpleProp.source).toEqual('someValue');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should store working property setters', () => {
|
it('should bind host directive properties', () => {
|
||||||
var element = el('<input some-decor-props>');
|
var element = el('<input some-decor-with-host-props>');
|
||||||
var results = process(element);
|
var results = process(element);
|
||||||
var setter = MapWrapper.get(results[0].propertySetters, 'value');
|
|
||||||
setter(element, 'abc');
|
var directiveBinding = results[0].directives[0];
|
||||||
expect(element.value).toEqual('abc');
|
|
||||||
|
var ast = MapWrapper.get(directiveBinding.hostPropertyBindings, 'hostProperty');
|
||||||
|
expect(ast.source).toEqual('dirProp');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should read attribute values', () => {
|
it('should read attribute values', () => {
|
||||||
@ -256,10 +259,16 @@ var someDecoratorWithProps = new DirectiveMetadata({
|
|||||||
'dirProp': 'elProp',
|
'dirProp': 'elProp',
|
||||||
'doubleProp': 'elProp | double'
|
'doubleProp': 'elProp | double'
|
||||||
}),
|
}),
|
||||||
setters: ['value'],
|
|
||||||
readAttributes: ['some-attr']
|
readAttributes: ['some-attr']
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var someDecoratorWithHostProperties = new DirectiveMetadata({
|
||||||
|
selector: '[some-decor-with-host-props]',
|
||||||
|
hostProperties: MapWrapper.createFromStringMap({
|
||||||
|
'dirProp': 'hostProperty'
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
var someDecoratorWithEvents = new DirectiveMetadata({
|
var someDecoratorWithEvents = new DirectiveMetadata({
|
||||||
selector: '[some-decor-events]',
|
selector: '[some-decor-events]',
|
||||||
hostListeners: MapWrapper.createFromStringMap({
|
hostListeners: MapWrapper.createFromStringMap({
|
||||||
|
@ -52,6 +52,12 @@ export function main() {
|
|||||||
expect(MapWrapper.get(results[0].propertyBindings, 'a').source).toEqual('{{b}}');
|
expect(MapWrapper.get(results[0].propertyBindings, 'a').source).toEqual('{{b}}');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should store property setters as camel case', () => {
|
||||||
|
var element = el('<div bind-some-prop="1">');
|
||||||
|
var results = process(element);
|
||||||
|
expect(MapWrapper.get(results[0].propertyBindings, 'someProp')).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
it('should detect var- syntax', () => {
|
it('should detect var- syntax', () => {
|
||||||
var results = process(el('<template var-a="b"></template>'));
|
var results = process(el('<template var-a="b"></template>'));
|
||||||
expect(MapWrapper.get(results[0].variableBindings, 'b')).toEqual('a');
|
expect(MapWrapper.get(results[0].variableBindings, 'b')).toEqual('a');
|
||||||
@ -143,20 +149,6 @@ export function main() {
|
|||||||
expect(MapWrapper.get(results[0].attrs(), 'a')).toEqual('b');
|
expect(MapWrapper.get(results[0].attrs(), 'a')).toEqual('b');
|
||||||
expect(MapWrapper.get(results[0].attrs(), 'c')).toEqual('d');
|
expect(MapWrapper.get(results[0].attrs(), 'c')).toEqual('d');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should store working property setters', () => {
|
|
||||||
var element = el('<input bind-value="1">');
|
|
||||||
var results = process(element);
|
|
||||||
var setter = MapWrapper.get(results[0].propertySetters, 'value');
|
|
||||||
setter(element, 'abc');
|
|
||||||
expect(element.value).toEqual('abc');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should store property setters as camel case', () => {
|
|
||||||
var element = el('<div bind-some-prop="1">');
|
|
||||||
var results = process(element);
|
|
||||||
expect(MapWrapper.get(results[0].propertySetters, 'someProp')).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import {Component, View} from 'angular2/angular2';
|
import {Component, View} from 'angular2/angular2';
|
||||||
import {PropertySetter, EventEmitter} from 'angular2/src/core/annotations/di';
|
|
||||||
import {onChange} from 'angular2/src/core/annotations/annotations';
|
import {onChange} from 'angular2/src/core/annotations/annotations';
|
||||||
import {isPresent, StringWrapper} from 'angular2/src/facade/lang';
|
import {isPresent} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
|
|
||||||
@Component({selector: '[md-button]:not([href])'})
|
@Component({selector: '[md-button]:not([href])'})
|
||||||
@ -17,21 +16,18 @@ export class MdButton {
|
|||||||
'disabled': 'disabled'
|
'disabled': 'disabled'
|
||||||
},
|
},
|
||||||
hostListeners: {'click': 'onClick($event)'},
|
hostListeners: {'click': 'onClick($event)'},
|
||||||
|
hostProperties: {'tabIndex': 'tabIndex'},
|
||||||
lifecycle: [onChange]
|
lifecycle: [onChange]
|
||||||
})
|
})
|
||||||
@View({
|
@View({
|
||||||
templateUrl: 'angular2_material/src/components/button/button.html'
|
templateUrl: 'angular2_material/src/components/button/button.html'
|
||||||
})
|
})
|
||||||
export class MdAnchor {
|
export class MdAnchor {
|
||||||
tabIndexSetter: Function;
|
tabIndex: number;
|
||||||
|
|
||||||
/** Whether the component is disabled. */
|
/** Whether the component is disabled. */
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
|
|
||||||
constructor(@PropertySetter('tabIndex') tabIndexSetter: Function) {
|
|
||||||
this.tabIndexSetter = tabIndexSetter;
|
|
||||||
}
|
|
||||||
|
|
||||||
onClick(event) {
|
onClick(event) {
|
||||||
// A disabled anchor shouldn't navigate anywhere.
|
// A disabled anchor shouldn't navigate anywhere.
|
||||||
if (isPresent(this.disabled) && this.disabled !== false) {
|
if (isPresent(this.disabled) && this.disabled !== false) {
|
||||||
@ -42,6 +38,6 @@ export class MdAnchor {
|
|||||||
/** Invoked when a change is detected. */
|
/** Invoked when a change is detected. */
|
||||||
onChange(_) {
|
onChange(_) {
|
||||||
// A disabled anchor should not be in the tab flow.
|
// A disabled anchor should not be in the tab flow.
|
||||||
this.tabIndexSetter(this.disabled ? -1 : 0);
|
this.tabIndex = this.disabled ? -1 : 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {Component, View, Attribute, PropertySetter} from 'angular2/angular2';
|
import {Component, View, Attribute} from 'angular2/angular2';
|
||||||
import {isPresent} from 'angular2/src/facade/lang';
|
import {isPresent} from 'angular2/src/facade/lang';
|
||||||
import {KEY_SPACE} from 'angular2_material/src/core/constants'
|
import {KEY_SPACE} from 'angular2_material/src/core/constants'
|
||||||
import {KeyboardEvent} from 'angular2/src/facade/browser';
|
import {KeyboardEvent} from 'angular2/src/facade/browser';
|
||||||
@ -11,6 +11,12 @@ import {KeyboardEvent} from 'angular2/src/facade/browser';
|
|||||||
},
|
},
|
||||||
hostListeners: {
|
hostListeners: {
|
||||||
'keydown': 'onKeydown($event)'
|
'keydown': 'onKeydown($event)'
|
||||||
|
},
|
||||||
|
hostProperties: {
|
||||||
|
'tabindex': 'tabindex',
|
||||||
|
'role': 'attr.role',
|
||||||
|
'checked': 'attr.aria-checked',
|
||||||
|
'disabled_': 'attr.aria-disabled'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@View({
|
@View({
|
||||||
@ -19,38 +25,21 @@ import {KeyboardEvent} from 'angular2/src/facade/browser';
|
|||||||
})
|
})
|
||||||
export class MdCheckbox {
|
export class MdCheckbox {
|
||||||
/** Whether this checkbox is checked. */
|
/** Whether this checkbox is checked. */
|
||||||
checked_: boolean;
|
checked: boolean;
|
||||||
|
|
||||||
/** Whether this checkbox is disabled. */
|
/** Whether this checkbox is disabled. */
|
||||||
disabled_: boolean;
|
disabled_: boolean;
|
||||||
|
|
||||||
/** Setter for `aria-checked` attribute. */
|
/** Setter for `role` attribute. */
|
||||||
ariaCheckedSetter: Function;
|
role: string;
|
||||||
|
|
||||||
/** Setter for `aria-disabled` attribute. */
|
/** Setter for tabindex */
|
||||||
ariaDisabledSetter: Function;
|
tabindex: any;
|
||||||
|
|
||||||
constructor(
|
constructor(@Attribute('tabindex') tabindex: string) {
|
||||||
@Attribute('tabindex') tabindex: string,
|
this.role = 'checkbox';
|
||||||
@PropertySetter('tabindex') tabindexSetter: Function,
|
|
||||||
@PropertySetter('attr.role') roleSetter: Function,
|
|
||||||
@PropertySetter('attr.aria-checked') ariaCheckedSetter: Function,
|
|
||||||
@PropertySetter('attr.aria-disabled') ariaDisabledSetter: Function) {
|
|
||||||
this.ariaCheckedSetter = ariaCheckedSetter;
|
|
||||||
this.ariaDisabledSetter = ariaDisabledSetter;
|
|
||||||
|
|
||||||
roleSetter('checkbox');
|
|
||||||
this.checked = false;
|
this.checked = false;
|
||||||
tabindexSetter(isPresent(tabindex) ? tabindex : '0');
|
this.tabindex = isPresent(tabindex) ? tabindex : '0';
|
||||||
}
|
|
||||||
|
|
||||||
get checked() {
|
|
||||||
return this.checked_;
|
|
||||||
}
|
|
||||||
|
|
||||||
set checked(value) {
|
|
||||||
this.checked_ = value;
|
|
||||||
this.ariaCheckedSetter(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get disabled() {
|
get disabled() {
|
||||||
@ -59,7 +48,6 @@ export class MdCheckbox {
|
|||||||
|
|
||||||
set disabled(value) {
|
set disabled(value) {
|
||||||
this.disabled_ = isPresent(value) && value !== false;
|
this.disabled_ = isPresent(value) && value !== false;
|
||||||
this.ariaDisabledSetter(this.disabled_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onKeydown(event: KeyboardEvent) {
|
onKeydown(event: KeyboardEvent) {
|
||||||
@ -76,6 +64,5 @@ export class MdCheckbox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.checked = !this.checked;
|
this.checked = !this.checked;
|
||||||
this.ariaCheckedSetter(this.checked);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ import {Component, View, onAllChangesDone, Parent} from 'angular2/angular2';
|
|||||||
import {onDestroy, onChange} from 'angular2/src/core/annotations/annotations';
|
import {onDestroy, onChange} from 'angular2/src/core/annotations/annotations';
|
||||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||||
import {isPresent, isString, NumberWrapper, stringify} from 'angular2/src/facade/lang';
|
import {isPresent, isString, NumberWrapper, stringify} from 'angular2/src/facade/lang';
|
||||||
import {PropertySetter} from 'angular2/src/core/annotations/di';
|
|
||||||
|
|
||||||
// TODO(jelbourn): Set appropriate aria attributes for grid list elements.
|
// TODO(jelbourn): Set appropriate aria attributes for grid list elements.
|
||||||
|
|
||||||
@ -172,6 +171,15 @@ export class MdGridList {
|
|||||||
'rowspan': 'rowspan',
|
'rowspan': 'rowspan',
|
||||||
'colspan': 'colspan'
|
'colspan': 'colspan'
|
||||||
},
|
},
|
||||||
|
hostProperties: {
|
||||||
|
'styleHeight': 'style.height',
|
||||||
|
'styleWidth': 'style.width',
|
||||||
|
'styleTop': 'style.top',
|
||||||
|
'styleLeft': 'style.left',
|
||||||
|
'styleMarginTop': 'style.marginTop',
|
||||||
|
'stylePaddingTop': 'style.paddingTop',
|
||||||
|
'role': 'role'
|
||||||
|
},
|
||||||
lifecycle: [onDestroy, onChange]
|
lifecycle: [onDestroy, onChange]
|
||||||
})
|
})
|
||||||
@View({
|
@View({
|
||||||
@ -181,40 +189,28 @@ export class MdGridTile {
|
|||||||
gridList: MdGridList;
|
gridList: MdGridList;
|
||||||
rowspan: number;
|
rowspan: number;
|
||||||
colspan: number;
|
colspan: number;
|
||||||
heightSetter;
|
|
||||||
widthSetter;
|
styleHeight:any;
|
||||||
topSetter;
|
styleWidth:any;
|
||||||
leftSetter;
|
styleTop:any;
|
||||||
marginTopSetter;
|
styleLeft:any;
|
||||||
paddingTopSetter;
|
styleMarginTop:any;
|
||||||
|
stylePaddingTop:any;
|
||||||
|
role:any;
|
||||||
|
|
||||||
isRegisteredWithGridList: boolean;
|
isRegisteredWithGridList: boolean;
|
||||||
|
|
||||||
constructor(
|
constructor(@Parent() gridList: MdGridList) {
|
||||||
@Parent() gridList: MdGridList,
|
|
||||||
@PropertySetter('style.height') heightSetter: Function,
|
|
||||||
@PropertySetter('style.width') widthSetter: Function,
|
|
||||||
@PropertySetter('style.top') topSetter: Function,
|
|
||||||
@PropertySetter('style.left') leftSetter: Function,
|
|
||||||
@PropertySetter('style.marginTop') marginTopSetter: Function,
|
|
||||||
@PropertySetter('style.paddingTop') paddingTopSetter: Function,
|
|
||||||
@PropertySetter('role') roleSetter: Function
|
|
||||||
) {
|
|
||||||
this.gridList = gridList;
|
this.gridList = gridList;
|
||||||
this.heightSetter = heightSetter;
|
|
||||||
this.widthSetter = widthSetter;
|
this.role = 'listitem';
|
||||||
this.topSetter = topSetter;
|
|
||||||
this.leftSetter = leftSetter;
|
|
||||||
this.marginTopSetter = marginTopSetter;
|
|
||||||
this.paddingTopSetter = paddingTopSetter;
|
|
||||||
roleSetter('listitem');
|
|
||||||
|
|
||||||
// Tiles default to 1x1, but rowspan and colspan can be changed via binding.
|
// Tiles default to 1x1, but rowspan and colspan can be changed via binding.
|
||||||
this.rowspan = 1;
|
this.rowspan = 1;
|
||||||
this.colspan = 1;
|
this.colspan = 1;
|
||||||
|
|
||||||
// DEBUG
|
// DEBUG
|
||||||
heightSetter(`${gridList.tiles.length * 100}px`);
|
this.styleHeight = `${gridList.tiles.length * 100}px`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {Component, View, Attribute, PropertySetter, onChange} from 'angular2/angular2';
|
import {Component, View, Attribute, onChange} from 'angular2/angular2';
|
||||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||||
import {Math} from 'angular2/src/facade/math';
|
import {Math} from 'angular2/src/facade/math';
|
||||||
|
|
||||||
@ -8,6 +8,12 @@ import {Math} from 'angular2/src/facade/math';
|
|||||||
properties: {
|
properties: {
|
||||||
'value': 'value',
|
'value': 'value',
|
||||||
'bufferValue': 'buffer-value'
|
'bufferValue': 'buffer-value'
|
||||||
|
},
|
||||||
|
hostProperties: {
|
||||||
|
'role': 'attr.role',
|
||||||
|
'ariaValuemin': 'attr.aria-valuemin',
|
||||||
|
'ariaValuemax': 'attr.aria-valuemax',
|
||||||
|
'ariaValuenow': 'attr.aria-valuenow'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@View({
|
@View({
|
||||||
@ -33,19 +39,20 @@ export class MdProgressLinear {
|
|||||||
/** CSS `transform` property applied to the secondary bar. */
|
/** CSS `transform` property applied to the secondary bar. */
|
||||||
secondaryBarTransform: string;
|
secondaryBarTransform: string;
|
||||||
|
|
||||||
constructor(
|
|
||||||
@Attribute('md-mode') mode: string,
|
role:any;
|
||||||
@PropertySetter('attr.role') roleSetter: Function,
|
ariaValuemin:any;
|
||||||
@PropertySetter('attr.aria-valuemin') ariaValueMinSetter: Function,
|
ariaValuemax:any;
|
||||||
@PropertySetter('attr.aria-valuemax') ariaValueMaxSetter: Function,
|
ariaValuenow:any;
|
||||||
@PropertySetter('attr.aria-valuenow') ariaValueNowSetter: Function) {
|
|
||||||
|
constructor(@Attribute('md-mode') mode: string) {
|
||||||
this.ariaValueNowSetter = ariaValueNowSetter;
|
this.ariaValueNowSetter = ariaValueNowSetter;
|
||||||
this.primaryBarTransform = '';
|
this.primaryBarTransform = '';
|
||||||
this.secondaryBarTransform = '';
|
this.secondaryBarTransform = '';
|
||||||
|
|
||||||
roleSetter('progressbar');
|
this.role = 'progressbar';
|
||||||
ariaValueMinSetter('0');
|
this.ariaValuemin = '0';
|
||||||
ariaValueMaxSetter('100');
|
this.ariaValuemax = '100';
|
||||||
|
|
||||||
this.mode = isPresent(mode) ? mode : Mode.DETERMINATE;
|
this.mode = isPresent(mode) ? mode : Mode.DETERMINATE;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {Component, View, Parent, Ancestor, Attribute, PropertySetter} from 'angular2/angular2';
|
import {Component, View, Parent, Ancestor, Attribute} from 'angular2/angular2';
|
||||||
import {Optional} from 'angular2/src/di/annotations';
|
import {Optional} from 'angular2/src/di/annotations';
|
||||||
import {MdRadioDispatcher} from 'angular2_material/src/components/radio/radio_dispatcher'
|
import {MdRadioDispatcher} from 'angular2_material/src/components/radio/radio_dispatcher'
|
||||||
import {onChange} from 'angular2/src/core/annotations/annotations';
|
import {onChange} from 'angular2/src/core/annotations/annotations';
|
||||||
@ -34,6 +34,13 @@ var _uniqueIdCounter:number = 0;
|
|||||||
},
|
},
|
||||||
hostListeners: {
|
hostListeners: {
|
||||||
'keydown': 'onKeydown($event)'
|
'keydown': 'onKeydown($event)'
|
||||||
|
},
|
||||||
|
hostProperties: {
|
||||||
|
'id': 'id',
|
||||||
|
'tabindex': 'tabindex',
|
||||||
|
'role': 'attr.role',
|
||||||
|
'checked': 'attr.aria-checked',
|
||||||
|
'disabled': 'attr.aria-disabled'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@View({
|
@View({
|
||||||
@ -42,7 +49,7 @@ var _uniqueIdCounter:number = 0;
|
|||||||
})
|
})
|
||||||
export class MdRadioButton {
|
export class MdRadioButton {
|
||||||
/** Whether this radio is checked. */
|
/** Whether this radio is checked. */
|
||||||
checked_: boolean;
|
checked: boolean;
|
||||||
|
|
||||||
/** Whether the radio is disabled. */
|
/** Whether the radio is disabled. */
|
||||||
disabled_: boolean;
|
disabled_: boolean;
|
||||||
@ -62,36 +69,26 @@ export class MdRadioButton {
|
|||||||
/** Dispatcher for coordinating radio unique-selection by name. */
|
/** Dispatcher for coordinating radio unique-selection by name. */
|
||||||
radioDispatcher: MdRadioDispatcher;
|
radioDispatcher: MdRadioDispatcher;
|
||||||
|
|
||||||
/** Setter for `aria-checked` attribute. */
|
tabindex:any;
|
||||||
ariaCheckedSetter: Function;
|
|
||||||
|
role:any;
|
||||||
/** Setter for `aria-disabled` attribute. */
|
|
||||||
ariaDisabledSetter: Function;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Optional() @Parent() radioGroup: MdRadioGroup,
|
@Optional() @Parent() radioGroup: MdRadioGroup,
|
||||||
@Attribute('id') id: string,
|
@Attribute('id') id: string,
|
||||||
@Attribute('tabindex') tabindex: string,
|
@Attribute('tabindex') tabindex: string,
|
||||||
@PropertySetter('id') idSetter: Function,
|
|
||||||
@PropertySetter('tabindex') tabindexSetter: Function,
|
|
||||||
@PropertySetter('attr.role') roleSetter: Function,
|
|
||||||
@PropertySetter('attr.aria-checked') ariaCheckedSetter: Function,
|
|
||||||
@PropertySetter('attr.aria-disabled') ariaDisabledSetter: Function,
|
|
||||||
radioDispatcher: MdRadioDispatcher) {
|
radioDispatcher: MdRadioDispatcher) {
|
||||||
// Assertions. Ideally these should be stripped out by the compiler.
|
// Assertions. Ideally these should be stripped out by the compiler.
|
||||||
// TODO(jelbourn): Assert that there's no name binding AND a parent radio group.
|
// TODO(jelbourn): Assert that there's no name binding AND a parent radio group.
|
||||||
|
|
||||||
this.radioGroup = radioGroup;
|
this.radioGroup = radioGroup;
|
||||||
this.radioDispatcher = radioDispatcher;
|
this.radioDispatcher = radioDispatcher;
|
||||||
this.ariaCheckedSetter = ariaCheckedSetter;
|
|
||||||
this.ariaDisabledSetter = ariaDisabledSetter;
|
|
||||||
this.value = null;
|
this.value = null;
|
||||||
|
|
||||||
roleSetter('radio');
|
this.role = 'radio';
|
||||||
this.checked = false;
|
this.checked = false;
|
||||||
|
|
||||||
this.id = isPresent(id) ? id : `md-radio-${_uniqueIdCounter++}`;
|
this.id = isPresent(id) ? id : `md-radio-${_uniqueIdCounter++}`;;
|
||||||
idSetter(this.id);
|
|
||||||
|
|
||||||
// Whenever a radio button with the same name is checked, uncheck this radio button.
|
// Whenever a radio button with the same name is checked, uncheck this radio button.
|
||||||
radioDispatcher.listen((name) => {
|
radioDispatcher.listen((name) => {
|
||||||
@ -108,7 +105,7 @@ export class MdRadioButton {
|
|||||||
|
|
||||||
// If the user has not set a tabindex, default to zero (in the normal document flow).
|
// If the user has not set a tabindex, default to zero (in the normal document flow).
|
||||||
if (!isPresent(radioGroup)) {
|
if (!isPresent(radioGroup)) {
|
||||||
tabindexSetter(isPresent(tabindex) ? tabindex : '0');
|
this.tabindex = isPresent(tabindex) ? tabindex : '0';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,22 +126,12 @@ export class MdRadioButton {
|
|||||||
(isPresent(this.radioGroup) && this.radioGroup.disabled);
|
(isPresent(this.radioGroup) && this.radioGroup.disabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
get checked() {
|
|
||||||
return this.checked_;
|
|
||||||
}
|
|
||||||
|
|
||||||
set checked(value) {
|
|
||||||
this.checked_ = value;
|
|
||||||
this.ariaCheckedSetter(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
get disabled() {
|
get disabled() {
|
||||||
return this.disabled_;
|
return this.disabled_;
|
||||||
}
|
}
|
||||||
|
|
||||||
set disabled(value) {
|
set disabled(value) {
|
||||||
this.disabled_ = isPresent(value) && value !== false;
|
this.disabled_ = isPresent(value) && value !== false;
|
||||||
this.ariaDisabledSetter(this.disabled_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Select this radio button. */
|
/** Select this radio button. */
|
||||||
@ -183,6 +170,13 @@ export class MdRadioButton {
|
|||||||
},
|
},
|
||||||
hostListeners: {
|
hostListeners: {
|
||||||
'keydown': 'onKeydown($event)'
|
'keydown': 'onKeydown($event)'
|
||||||
|
},
|
||||||
|
hostProperties: {
|
||||||
|
'tabindex': 'tabindex',
|
||||||
|
'role': 'attr.role',
|
||||||
|
'checked': 'attr.aria-checked',
|
||||||
|
'disabled': 'attr.aria-disabled',
|
||||||
|
'activedescendant': 'attr.aria-activedescendant'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@View({
|
@View({
|
||||||
@ -201,11 +195,7 @@ export class MdRadioGroup {
|
|||||||
/** List of child radio buttons. */
|
/** List of child radio buttons. */
|
||||||
radios_: List<MdRadioButton>;
|
radios_: List<MdRadioButton>;
|
||||||
|
|
||||||
changeEmitter: Function;
|
activedescendant: any;
|
||||||
|
|
||||||
ariaActiveDescendantSetter: Function;
|
|
||||||
|
|
||||||
ariaDisabledSetter: Function;
|
|
||||||
|
|
||||||
disabled_: boolean;
|
disabled_: boolean;
|
||||||
|
|
||||||
@ -214,30 +204,28 @@ export class MdRadioGroup {
|
|||||||
|
|
||||||
change:EventEmitter;
|
change:EventEmitter;
|
||||||
|
|
||||||
|
tabindex:any;
|
||||||
|
|
||||||
|
role:any;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Attribute('tabindex') tabindex: string,
|
@Attribute('tabindex') tabindex: string,
|
||||||
@Attribute('disabled') disabled: string,
|
@Attribute('disabled') disabled: string,
|
||||||
@PropertySetter('tabindex') tabindexSetter: Function,
|
|
||||||
@PropertySetter('attr.role') roleSetter: Function,
|
|
||||||
@PropertySetter('attr.aria-disabled') ariaDisabledSetter: Function,
|
|
||||||
@PropertySetter('attr.aria-activedescendant') ariaActiveDescendantSetter: Function,
|
|
||||||
radioDispatcher: MdRadioDispatcher) {
|
radioDispatcher: MdRadioDispatcher) {
|
||||||
this.name_ = `md-radio-group-${_uniqueIdCounter++}`;
|
this.name_ = `md-radio-group-${_uniqueIdCounter++}`;
|
||||||
this.radios_ = [];
|
this.radios_ = [];
|
||||||
this.change = new EventEmitter();
|
this.change = new EventEmitter();
|
||||||
this.ariaActiveDescendantSetter = ariaActiveDescendantSetter;
|
|
||||||
this.ariaDisabledSetter = ariaDisabledSetter;
|
|
||||||
this.radioDispatcher = radioDispatcher;
|
this.radioDispatcher = radioDispatcher;
|
||||||
this.selectedRadioId = '';
|
this.selectedRadioId = '';
|
||||||
this.disabled_ = false;
|
this.disabled_ = false;
|
||||||
|
|
||||||
roleSetter('radiogroup');
|
this.role = 'radiogroup';
|
||||||
|
|
||||||
// The simple presence of the `disabled` attribute dictates disabled state.
|
// The simple presence of the `disabled` attribute dictates disabled state.
|
||||||
this.disabled = isPresent(disabled);
|
this.disabled = isPresent(disabled);
|
||||||
|
|
||||||
// If the user has not set a tabindex, default to zero (in the normal document flow).
|
// If the user has not set a tabindex, default to zero (in the normal document flow).
|
||||||
tabindexSetter(isPresent(tabindex) ? tabindex : '0');
|
this.tabindex = isPresent(tabindex) ? tabindex : '0';
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gets the name of this group, as to be applied in the HTML 'name' attribute. */
|
/** Gets the name of this group, as to be applied in the HTML 'name' attribute. */
|
||||||
@ -251,7 +239,6 @@ export class MdRadioGroup {
|
|||||||
|
|
||||||
set disabled(value) {
|
set disabled(value) {
|
||||||
this.disabled_ = isPresent(value) && value !== false;
|
this.disabled_ = isPresent(value) && value !== false;
|
||||||
this.ariaDisabledSetter(this.disabled_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Change handler invoked when bindings are resolved or when bindings have changed. */
|
/** Change handler invoked when bindings are resolved or when bindings have changed. */
|
||||||
@ -267,7 +254,7 @@ export class MdRadioGroup {
|
|||||||
if (radio.value == this.value) {
|
if (radio.value == this.value) {
|
||||||
radio.checked = true;
|
radio.checked = true;
|
||||||
this.selectedRadioId = radio.id;
|
this.selectedRadioId = radio.id;
|
||||||
this.ariaActiveDescendantSetter(radio.id);
|
this.activedescendant = radio.id;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -277,7 +264,7 @@ export class MdRadioGroup {
|
|||||||
updateValue(value: any, id: string) {
|
updateValue(value: any, id: string) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.selectedRadioId = id;
|
this.selectedRadioId = id;
|
||||||
this.ariaActiveDescendantSetter(id);
|
this.activedescendant = id;
|
||||||
ObservableWrapper.callNext(this.change, null);
|
ObservableWrapper.callNext(this.change, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -335,6 +322,6 @@ export class MdRadioGroup {
|
|||||||
|
|
||||||
this.value = radio.value;
|
this.value = radio.value;
|
||||||
this.selectedRadioId = radio.id;
|
this.selectedRadioId = radio.id;
|
||||||
this.ariaActiveDescendantSetter(radio.id);
|
this.activedescendant = radio.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {Component, View, Attribute, PropertySetter} from 'angular2/angular2';
|
import {Component, View, Attribute} from 'angular2/angular2';
|
||||||
import {isPresent} from 'angular2/src/facade/lang';
|
import {isPresent} from 'angular2/src/facade/lang';
|
||||||
import {KEY_SPACE} from 'angular2_material/src/core/constants'
|
import {KEY_SPACE} from 'angular2_material/src/core/constants'
|
||||||
import {KeyboardEvent} from 'angular2/src/facade/browser';
|
import {KeyboardEvent} from 'angular2/src/facade/browser';
|
||||||
@ -13,6 +13,11 @@ import {KeyboardEvent} from 'angular2/src/facade/browser';
|
|||||||
},
|
},
|
||||||
hostListeners: {
|
hostListeners: {
|
||||||
'keydown': 'onKeydown($event)'
|
'keydown': 'onKeydown($event)'
|
||||||
|
},
|
||||||
|
hostProperties: {
|
||||||
|
'checked': 'attr.aria-checked',
|
||||||
|
'disabled_': 'attr.aria-disabled',
|
||||||
|
'role': 'attr.role'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@View({
|
@View({
|
||||||
@ -21,38 +26,18 @@ import {KeyboardEvent} from 'angular2/src/facade/browser';
|
|||||||
})
|
})
|
||||||
export class MdSwitch {
|
export class MdSwitch {
|
||||||
/** Whether this switch is checked. */
|
/** Whether this switch is checked. */
|
||||||
checked_: boolean;
|
checked: boolean;
|
||||||
|
|
||||||
/** Whether this switch is disabled. */
|
/** Whether this switch is disabled. */
|
||||||
disabled_: boolean;
|
disabled_: boolean;
|
||||||
|
|
||||||
/** Setter for `aria-checked` attribute. */
|
tabindex:any;
|
||||||
ariaCheckedSetter: Function;
|
role:any;
|
||||||
|
|
||||||
/** Setter for `aria-disabled` attribute. */
|
constructor(@Attribute('tabindex') tabindex: string) {
|
||||||
ariaDisabledSetter: Function;
|
this.role = 'checkbox';
|
||||||
|
|
||||||
constructor(
|
|
||||||
@Attribute('tabindex') tabindex: string,
|
|
||||||
@PropertySetter('tabindex') tabindexSetter: Function,
|
|
||||||
@PropertySetter('attr.role') roleSetter: Function,
|
|
||||||
@PropertySetter('attr.aria-checked') ariaCheckedSetter: Function,
|
|
||||||
@PropertySetter('attr.aria-disabled') ariaDisabledSetter: Function) {
|
|
||||||
this.ariaCheckedSetter = ariaCheckedSetter;
|
|
||||||
this.ariaDisabledSetter = ariaDisabledSetter;
|
|
||||||
|
|
||||||
roleSetter('checkbox');
|
|
||||||
this.checked = false;
|
this.checked = false;
|
||||||
tabindexSetter(isPresent(tabindex) ? tabindex : '0');
|
this.tabindex = isPresent(tabindex) ? tabindex : '0';
|
||||||
}
|
|
||||||
|
|
||||||
get checked() {
|
|
||||||
return this.checked_;
|
|
||||||
}
|
|
||||||
|
|
||||||
set checked(value) {
|
|
||||||
this.checked_ = value;
|
|
||||||
this.ariaCheckedSetter(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get disabled() {
|
get disabled() {
|
||||||
@ -61,7 +46,6 @@ export class MdSwitch {
|
|||||||
|
|
||||||
set disabled(value) {
|
set disabled(value) {
|
||||||
this.disabled_ = isPresent(value) && value !== false;
|
this.disabled_ = isPresent(value) && value !== false;
|
||||||
this.ariaDisabledSetter(this.disabled_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onKeydown(event: KeyboardEvent) {
|
onKeydown(event: KeyboardEvent) {
|
||||||
@ -78,7 +62,6 @@ export class MdSwitch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.checked = !this.checked;
|
this.checked = !this.checked;
|
||||||
this.ariaCheckedSetter(this.checked);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ import {
|
|||||||
JitChangeDetection,
|
JitChangeDetection,
|
||||||
BindingRecord,
|
BindingRecord,
|
||||||
DirectiveRecord,
|
DirectiveRecord,
|
||||||
|
DirectiveIndex,
|
||||||
DEFAULT
|
DEFAULT
|
||||||
} from 'angular2/change_detection';
|
} from 'angular2/change_detection';
|
||||||
|
|
||||||
@ -191,7 +192,7 @@ function setUpChangeDetection(changeDetection:ChangeDetection, iterations, objec
|
|||||||
|
|
||||||
var proto = changeDetection.createProtoChangeDetector("proto");
|
var proto = changeDetection.createProtoChangeDetector("proto");
|
||||||
|
|
||||||
var directiveRecord = new DirectiveRecord(0, 0, false, false, DEFAULT);
|
var directiveRecord = new DirectiveRecord(new DirectiveIndex(0, 0), false, false, DEFAULT);
|
||||||
var bindings = [
|
var bindings = [
|
||||||
BindingRecord.createForDirective(parser.parseBinding('field0', null), "field0", reflector.setter("field0"), directiveRecord),
|
BindingRecord.createForDirective(parser.parseBinding('field0', null), "field0", reflector.setter("field0"), directiveRecord),
|
||||||
BindingRecord.createForDirective(parser.parseBinding('field1', null), "field1", reflector.setter("field1"), directiveRecord),
|
BindingRecord.createForDirective(parser.parseBinding('field1', null), "field1", reflector.setter("field1"), directiveRecord),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user