refactor(directives): directives use declare that they listen to onChange in the annotations

This commit is contained in:
vsavkin 2015-02-06 13:19:47 -08:00
parent ee3f709fbf
commit 9240b09011
7 changed files with 91 additions and 27 deletions

View File

@ -1,5 +1,5 @@
import {ABSTRACT, CONST, normalizeBlank} from 'angular2/src/facade/lang';
import {List} from 'angular2/src/facade/collection';
import {ABSTRACT, CONST, normalizeBlank, isPresent} from 'angular2/src/facade/lang';
import {ListWrapper, List} from 'angular2/src/facade/collection';
import {TemplateConfig} from './template_config';
@ABSTRACT()
@ -30,6 +30,10 @@ export class Directive {
this.bind = bind;
this.lifecycle = lifecycle;
}
hasLifecycleHook(hook:string):boolean {
return isPresent(this.lifecycle) ? ListWrapper.contains(this.lifecycle, hook) : false;
}
}
export class Component extends Directive {
@ -133,4 +137,5 @@ export class Template extends Directive {
}
}
export var onDestroy = "onDestroy";
export const onDestroy = "onDestroy";
export const onChange = "onChange";

View File

@ -4,12 +4,11 @@ import {List, ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
import {Injector, Key, Dependency, bind, Binding, NoProviderError, ProviderError, CyclicDependencyError} from 'angular2/di';
import {Parent, Ancestor} from 'angular2/src/core/annotations/visibility';
import {EventEmitter} from 'angular2/src/core/annotations/events';
import {onDestroy} from 'angular2/src/core/annotations/annotations';
import {View, ProtoView} from 'angular2/src/core/compiler/view';
import {LightDom, SourceLightDom, DestinationLightDom} from 'angular2/src/core/compiler/shadow_dom_emulation/light_dom';
import {ViewPort} from 'angular2/src/core/compiler/viewport';
import {NgElement} from 'angular2/src/core/dom/element';
import {Directive} from 'angular2/src/core/annotations/annotations'
import {Directive, onChange, onDestroy} from 'angular2/src/core/annotations/annotations'
import {BindingPropagationConfig} from 'angular2/src/core/compiler/binding_propagation_config'
var _MAX_DIRECTIVE_CONSTRUCTION_COUNTER = 10;
@ -123,18 +122,19 @@ export class DirectiveDependency extends Dependency {
export class DirectiveBinding extends Binding {
callOnDestroy:boolean;
callOnChange:boolean;
onCheck:boolean;
constructor(key:Key, factory:Function, dependencies:List, providedAsPromise:boolean, callOnDestroy:boolean) {
constructor(key:Key, factory:Function, dependencies:List, providedAsPromise:boolean, annotation:Directive) {
super(key, factory, dependencies, providedAsPromise);
this.callOnDestroy = callOnDestroy;
this.callOnDestroy = isPresent(annotation) && annotation.hasLifecycleHook(onDestroy);
this.callOnChange = isPresent(annotation) && annotation.hasLifecycleHook(onChange);
//this.onCheck = isPresent(annotation) && annotation.hasLifecycleHook(onCheck);
}
static createFromBinding(b:Binding, annotation:Directive):Binding {
var deps = ListWrapper.map(b.dependencies, DirectiveDependency.createFrom);
var callOnDestroy = isPresent(annotation) && isPresent(annotation.lifecycle) ?
ListWrapper.contains(annotation.lifecycle, onDestroy) :
false;
return new DirectiveBinding(b.key, b.factory, deps, b.providedAsPromise, callOnDestroy);
return new DirectiveBinding(b.key, b.factory, deps, b.providedAsPromise, annotation);
}
static createFromType(type:Type, annotation:Directive):Binding {
@ -567,7 +567,7 @@ export class ElementInjector extends TreeNode {
return _undefined;
}
getAtIndex(index:int) {
getDirectiveAtIndex(index:int) {
if (index == 0) return this._obj0;
if (index == 1) return this._obj1;
if (index == 2) return this._obj2;
@ -581,6 +581,21 @@ export class ElementInjector extends TreeNode {
throw new OutOfBoundsAccess(index);
}
getDirectiveBindingAtIndex(index:int) {
var p = this._proto;
if (index == 0) return p._binding0;
if (index == 1) return p._binding1;
if (index == 2) return p._binding2;
if (index == 3) return p._binding3;
if (index == 4) return p._binding4;
if (index == 5) return p._binding5;
if (index == 6) return p._binding6;
if (index == 7) return p._binding7;
if (index == 8) return p._binding8;
if (index == 9) return p._binding9;
throw new OutOfBoundsAccess(index);
}
hasInstances() {
return this._constructionCounter > 0;
}

View File

@ -12,7 +12,6 @@ import {FIELD, IMPLEMENTS, int, isPresent, isBlank, BaseException} from 'angular
import {Injector} from 'angular2/di';
import {NgElement} from 'angular2/src/core/dom/element';
import {ViewPort} from './viewport';
import {OnChange} from './interfaces';
import {Content} from './shadow_dom_emulation/content_tag';
import {LightDom, DestinationLightDom} from './shadow_dom_emulation/light_dom';
import {ShadowDomStrategy} from './shadow_dom_strategy';
@ -211,7 +210,9 @@ export class View {
_notifyDirectiveAboutChanges(groupMemento, records:List) {
var dir = groupMemento.directive(this.elementInjectors);
if (dir instanceof OnChange) {
var binding = groupMemento.directiveBinding(this.elementInjectors);
if (binding.callOnChange) {
dir.onChange(this._collectChanges(records));
}
}
@ -552,7 +553,7 @@ export class DirectivePropertyMemento {
invoke(record:ChangeRecord, elementInjectors:List<ElementInjector>) {
var elementInjector:ElementInjector = elementInjectors[this._elementInjectorIndex];
var directive = elementInjector.getAtIndex(this._directiveIndex);
var directive = elementInjector.getDirectiveAtIndex(this._directiveIndex);
this._setter(directive, record.currentValue);
}
}
@ -581,7 +582,12 @@ class DirectivePropertyGroupMemento {
directive(elementInjectors:List<ElementInjector>) {
var elementInjector:ElementInjector = elementInjectors[this._elementInjectorIndex];
return elementInjector.getAtIndex(this._directiveIndex);
return elementInjector.getDirectiveAtIndex(this._directiveIndex);
}
directiveBinding(elementInjectors:List<ElementInjector>) {
var elementInjector:ElementInjector = elementInjectors[this._elementInjectorIndex];
return elementInjector.getDirectiveBindingAtIndex(this._directiveIndex);
}
}

View File

@ -1,4 +1,4 @@
import {Template} from 'angular2/src/core/annotations/annotations';
import {Template, onChange} from 'angular2/src/core/annotations/annotations';
import {OnChange} from 'angular2/src/core/compiler/interfaces';
import {ViewPort} from 'angular2/src/core/compiler/viewport';
import {View} from 'angular2/src/core/compiler/view';
@ -7,6 +7,7 @@ import {ListWrapper} from 'angular2/src/facade/collection';
@Template({
selector: '[foreach][in]',
lifecycle: [onChange],
bind: {
'in': 'iterable[]'
}

View File

@ -0,0 +1,23 @@
import {ddescribe, describe, it, iit, expect, beforeEach} from 'angular2/test_lib';
import {Directive, onChange} from 'angular2/src/core/annotations/annotations';
export function main() {
describe("Directive", () => {
describe("lifecycle", () => {
it("should be false when no lifecycle specified", () => {
var d = new Directive();
expect(d.hasLifecycleHook(onChange)).toBe(false);
});
it("should be false when the lifecycle does not contain the hook", () => {
var d = new Directive({lifecycle:[]});
expect(d.hasLifecycleHook(onChange)).toBe(false);
});
it("should be true otherwise", () => {
var d = new Directive({lifecycle:[onChange]});
expect(d.hasLifecycleHook(onChange)).toBe(true);
});
});
});
}

View File

@ -330,14 +330,25 @@ export function main() {
expect(inj.get(SimpleDirective)).toBeAnInstanceOf(SimpleDirective);
});
it("should allow for direct access using getAtIndex", function () {
it("should allow for direct access using getDirectiveAtIndex", function () {
var inj = injector([
DirectiveBinding.createFromBinding(bind(SimpleDirective).toClass(SimpleDirective), null)
]);
expect(inj.getAtIndex(0)).toBeAnInstanceOf(SimpleDirective);
expect(() => inj.getAtIndex(-1)).toThrowError(
expect(inj.getDirectiveAtIndex(0)).toBeAnInstanceOf(SimpleDirective);
expect(() => inj.getDirectiveAtIndex(-1)).toThrowError(
'Index -1 is out-of-bounds.');
expect(() => inj.getAtIndex(10)).toThrowError(
expect(() => inj.getDirectiveAtIndex(10)).toThrowError(
'Index 10 is out-of-bounds.');
});
it("should allow for direct access using getBindingAtIndex", function () {
var inj = injector([
DirectiveBinding.createFromBinding(bind(SimpleDirective).toClass(SimpleDirective), null)
]);
expect(inj.getDirectiveBindingAtIndex(0)).toBeAnInstanceOf(DirectiveBinding);
expect(() => inj.getDirectiveBindingAtIndex(-1)).toThrowError(
'Index -1 is out-of-bounds.');
expect(() => inj.getDirectiveBindingAtIndex(10)).toThrowError(
'Index 10 is out-of-bounds.');
});

View File

@ -1,10 +1,9 @@
import {describe, xit, it, expect, beforeEach, ddescribe, iit, el} from 'angular2/test_lib';
import {ProtoView, ElementPropertyMemento, DirectivePropertyMemento} from 'angular2/src/core/compiler/view';
import {ProtoElementInjector, ElementInjector} from 'angular2/src/core/compiler/element_injector';
import {ProtoElementInjector, ElementInjector, DirectiveBinding} from 'angular2/src/core/compiler/element_injector';
import {EmulatedShadowDomStrategy, NativeShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
import {Component, Decorator, Template} from 'angular2/src/core/annotations/annotations';
import {OnChange} from 'angular2/core';
import {Component, Decorator, Template, Directive, onChange} from 'angular2/src/core/annotations/annotations';
import {Lexer, Parser, DynamicProtoChangeDetector,
ChangeDetector} from 'angular2/change_detection';
import {TemplateConfig} from 'angular2/src/core/annotations/template_config';
@ -546,7 +545,9 @@ export function main() {
var pv = new ProtoView(el('<div class="ng-binding"></div>'),
new DynamicProtoChangeDetector(), null);
pv.bindElement(new ProtoElementInjector(null, 0, [DirectiveImplementingOnChange]));
pv.bindElement(new ProtoElementInjector(null, 0, [
DirectiveBinding.createFromType(DirectiveImplementingOnChange, new Directive({lifecycle: [onChange]}))
]));
pv.bindDirectiveProperty( 0, parser.parseBinding('a', null), 'a', reflector.setter('a'), false);
pv.bindDirectiveProperty( 0, parser.parseBinding('b', null), 'b', reflector.setter('b'), false);
createViewAndChangeDetector(pv);
@ -563,7 +564,9 @@ export function main() {
var pv = new ProtoView(el('<div class="ng-binding"></div>'),
new DynamicProtoChangeDetector(), null);
pv.bindElement(new ProtoElementInjector(null, 0, [DirectiveImplementingOnChange]));
pv.bindElement(new ProtoElementInjector(null, 0, [
DirectiveBinding.createFromType(DirectiveImplementingOnChange, new Directive({lifecycle: [onChange]}))
]));
pv.bindDirectiveProperty( 0, parser.parseBinding('a', null), 'a', reflector.setter('a'), false);
pv.bindDirectiveProperty( 0, parser.parseBinding('b', null), 'b', reflector.setter('b'), false);
createViewAndChangeDetector(pv);
@ -616,7 +619,7 @@ class SomeDirective {
}
}
class DirectiveImplementingOnChange extends OnChange {
class DirectiveImplementingOnChange {
a;
b;
c;