X Tutup
Skip to content

Commit 723e8fd

Browse files
committed
feat(change_detection): added a directive lifecycle hook that is called after children are checked
1 parent 507f7ea commit 723e8fd

File tree

13 files changed

+283
-82
lines changed

13 files changed

+283
-82
lines changed

modules/angular2/src/change_detection/abstract_change_detector.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,13 @@ export class AbstractChangeDetector extends ChangeDetector {
4242

4343
this.detectChangesInRecords(throwOnChange);
4444
this._detectChangesInChildren(throwOnChange);
45+
this.notifyOnAllChangesDone();
4546

4647
if (this.mode === CHECK_ONCE) this.mode = CHECKED;
4748
}
4849

4950
detectChangesInRecords(throwOnChange:boolean){}
51+
notifyOnAllChangesDone(){}
5052

5153
_detectChangesInChildren(throwOnChange:boolean) {
5254
var children = this.children;

modules/angular2/src/change_detection/change_detection_jit_generator.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
library change_detectoin.change_detection_jit_generator;
22

33
class ChangeDetectorJITGenerator {
4-
ChangeDetectorJITGenerator(typeName, records) {
4+
ChangeDetectorJITGenerator(typeName, records, directiveMementos) {
55
}
66

77
generate() {

modules/angular2/src/change_detection/change_detection_jit_generator.es6

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ import {
6464
* }
6565
* }
6666
*
67+
* ChangeDetector0.prototype.notifyOnAllChangesDone = function() {}
6768
*
6869
* ChangeDetector0.prototype.hydrate = function(context, locals) {
6970
* this.context = context;
@@ -96,31 +97,35 @@ var UTIL = "ChangeDetectionUtil";
9697
var DISPATCHER_ACCESSOR = "this.dispatcher";
9798
var PIPE_REGISTRY_ACCESSOR = "this.pipeRegistry";
9899
var PROTOS_ACCESSOR = "this.protos";
100+
var MEMENTOS_ACCESSOR = "this.directiveMementos";
99101
var CONTEXT_ACCESSOR = "this.context";
100102
var CHANGE_LOCAL = "change";
101103
var CHANGES_LOCAL = "changes";
102104
var LOCALS_ACCESSOR = "this.locals";
103105
var TEMP_LOCAL = "temp";
104106

105-
function typeTemplate(type:string, cons:string, detectChanges:string, setContext:string):string {
107+
function typeTemplate(type:string, cons:string, detectChanges:string,
108+
notifyOnAllChangesDone:string, setContext:string):string {
106109
return `
107110
${cons}
108111
${detectChanges}
112+
${notifyOnAllChangesDone}
109113
${setContext};
110114
111115
return function(dispatcher, pipeRegistry) {
112-
return new ${type}(dispatcher, pipeRegistry, protos);
116+
return new ${type}(dispatcher, pipeRegistry, protos, directiveMementos);
113117
}
114118
`;
115119
}
116120

117121
function constructorTemplate(type:string, fieldsDefinitions:string):string {
118122
return `
119-
var ${type} = function ${type}(dispatcher, pipeRegistry, protos) {
123+
var ${type} = function ${type}(dispatcher, pipeRegistry, protos, directiveMementos) {
120124
${ABSTRACT_CHANGE_DETECTOR}.call(this);
121125
${DISPATCHER_ACCESSOR} = dispatcher;
122126
${PIPE_REGISTRY_ACCESSOR} = pipeRegistry;
123127
${PROTOS_ACCESSOR} = protos;
128+
${MEMENTOS_ACCESSOR} = directiveMementos;
124129
${fieldsDefinitions}
125130
}
126131
@@ -157,6 +162,18 @@ ${type}.prototype.detectChangesInRecords = function(throwOnChange) {
157162
`;
158163
}
159164

165+
function notifyOnAllChangesDoneTemplate(type:string, body:string):string {
166+
return `
167+
${type}.prototype.notifyOnAllChangesDone = function() {
168+
${body}
169+
}
170+
`;
171+
}
172+
173+
function onAllChangesDoneTemplate(index:number):string {
174+
return `${DISPATCHER_ACCESSOR}.onAllChangesDone(${MEMENTOS_ACCESSOR}[${index}]);`;
175+
}
176+
160177

161178
function bodyTemplate(localDefinitions:string, changeDefinitions:string, records:string):string {
162179
return `
@@ -247,14 +264,16 @@ function addSimpleChangeRecordTemplate(protoIndex:number, oldValue:string, newVa
247264
export class ChangeDetectorJITGenerator {
248265
typeName:string;
249266
records:List<ProtoRecord>;
267+
directiveMementos:List;
250268
localNames:List<String>;
251269
changeNames:List<String>;
252270
fieldNames:List<String>;
253271
pipeNames:List<String>;
254272

255-
constructor(typeName:string, records:List<ProtoRecord>) {
273+
constructor(typeName:string, records:List<ProtoRecord>, directiveMementos:List) {
256274
this.typeName = typeName;
257275
this.records = records;
276+
this.directiveMementos = directiveMementos;
258277

259278
this.localNames = this.getLocalNames(records);
260279
this.changeNames = this.getChangeNames(this.localNames);
@@ -284,8 +303,10 @@ export class ChangeDetectorJITGenerator {
284303
}
285304

286305
generate():Function {
287-
var text = typeTemplate(this.typeName, this.genConstructor(), this.genDetectChanges(), this.genHydrate());
288-
return new Function('AbstractChangeDetector', 'ChangeDetectionUtil', 'protos', text)(AbstractChangeDetector, ChangeDetectionUtil, this.records);
306+
var text = typeTemplate(this.typeName, this.genConstructor(), this.genDetectChanges(),
307+
this.genNotifyOnAllChangesDone(), this.genHydrate());
308+
return new Function('AbstractChangeDetector', 'ChangeDetectionUtil', 'protos', 'directiveMementos', text)
309+
(AbstractChangeDetector, ChangeDetectionUtil, this.records, this.directiveMementos);
289310
}
290311

291312
genConstructor():string {
@@ -319,6 +340,20 @@ export class ChangeDetectorJITGenerator {
319340
return detectChangesTemplate(this.typeName, body);
320341
}
321342

343+
genNotifyOnAllChangesDone():string {
344+
var notifications = [];
345+
var mementos = this.directiveMementos;
346+
347+
for (var i = mementos.length - 1; i >= 0; --i) {
348+
var memento = mementos[i];
349+
if (memento.notifyOnAllChangesDone) {
350+
notifications.push(onAllChangesDoneTemplate(i));
351+
}
352+
}
353+
354+
return notifyOnAllChangesDoneTemplate(this.typeName, notifications.join(";\n"));
355+
}
356+
322357
genBody():string {
323358
var rec = this.records.map((r) => this.genRecord(r)).join("\n");
324359
return bodyTemplate(this.genLocalDefinitions(), this.genChangeDefinitions(), rec);

modules/angular2/src/change_detection/dynamic_change_detector.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
3434
prevContexts:List;
3535

3636
protos:List<ProtoRecord>;
37+
directiveMementos:List;
3738

38-
constructor(dispatcher:any, pipeRegistry:PipeRegistry, protoRecords:List<ProtoRecord>) {
39+
constructor(dispatcher:any, pipeRegistry:PipeRegistry, protoRecords:List<ProtoRecord>, directiveMementos:List) {
3940
super();
4041
this.dispatcher = dispatcher;
4142
this.pipeRegistry = pipeRegistry;
@@ -52,6 +53,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
5253
this.locals = null;
5354

5455
this.protos = protoRecords;
56+
this.directiveMementos = directiveMementos;
5557
}
5658

5759
hydrate(context:any, locals:any) {
@@ -102,6 +104,16 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
102104
}
103105
}
104106

107+
notifyOnAllChangesDone() {
108+
var mementos = this.directiveMementos;
109+
for (var i = mementos.length - 1; i >= 0; --i) {
110+
var memento = mementos[i];
111+
if (memento.notifyOnAllChangesDone) {
112+
this.dispatcher.onAllChangesDone(memento);
113+
}
114+
}
115+
}
116+
105117
_check(proto:ProtoRecord) {
106118
try {
107119
if (proto.mode === RECORD_TYPE_PIPE || proto.mode === RECORD_TYPE_BINDING_PIPE) {

modules/angular2/src/change_detection/proto_change_detector.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ import {
4747

4848
export class ProtoChangeDetector {
4949
addAst(ast:AST, bindingMemento:any, directiveMemento:any = null){}
50-
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List):ChangeDetector{
50+
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List, directiveMemento:List):ChangeDetector{
5151
return null;
5252
}
5353
}
@@ -73,9 +73,9 @@ export class DynamicProtoChangeDetector extends ProtoChangeDetector {
7373
this._pipeRegistry = pipeRegistry;
7474
}
7575

76-
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List) {
76+
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List, directiveMementos:List) {
7777
this._createRecordsIfNecessary(bindingRecords, variableBindings);
78-
return new DynamicChangeDetector(dispatcher, this._pipeRegistry, this._records);
78+
return new DynamicChangeDetector(dispatcher, this._pipeRegistry, this._records, directiveMementos);
7979
}
8080

8181
_createRecordsIfNecessary(bindingRecords:List, variableBindings:List) {
@@ -100,12 +100,12 @@ export class JitProtoChangeDetector extends ProtoChangeDetector {
100100
this._factory = null;
101101
}
102102

103-
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List) {
104-
this._createFactoryIfNecessary(bindingRecords, variableBindings);
103+
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List, directiveMementos:List) {
104+
this._createFactoryIfNecessary(bindingRecords, variableBindings, directiveMementos);
105105
return this._factory(dispatcher, this._pipeRegistry);
106106
}
107107

108-
_createFactoryIfNecessary(bindingRecords:List, variableBindings:List) {
108+
_createFactoryIfNecessary(bindingRecords:List, variableBindings:List, directiveMementos:List) {
109109
if (isBlank(this._factory)) {
110110
var recordBuilder = new ProtoRecordBuilder();
111111
ListWrapper.forEach(bindingRecords, (r) => {
@@ -114,7 +114,7 @@ export class JitProtoChangeDetector extends ProtoChangeDetector {
114114
var c = _jitProtoChangeDetectorClassCounter++;
115115
var records = coalesce(recordBuilder.records);
116116
var typeName = `ChangeDetector${c}`;
117-
this._factory = new ChangeDetectorJITGenerator(typeName, records).generate();
117+
this._factory = new ChangeDetectorJITGenerator(typeName, records, directiveMementos).generate();
118118
}
119119
}
120120
}

modules/angular2/src/core/annotations/annotations.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -900,3 +900,23 @@ export const onDestroy = "onDestroy";
900900
* @publicModule angular2/annotations
901901
*/
902902
export const onChange = "onChange";
903+
904+
/**
905+
* Notify a directive when the bindings of all its children have been changed.
906+
*
907+
* ## Example:
908+
*
909+
* ```
910+
* @Decorator({
911+
* selector: '[class-set]',
912+
* })
913+
* class ClassSet {
914+
*
915+
* onAllChangesDone() {
916+
* }
917+
*
918+
* }
919+
* ```
920+
* @publicModule angular2/annotations
921+
*/
922+
export const onAllChangesDone = "onAllChangesDone";

modules/angular2/src/core/compiler/element_injector.js

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {EventEmitter, PropertySetter, Attribute} from 'angular2/src/core/annotat
77
import * as viewModule from 'angular2/src/core/compiler/view';
88
import {ViewContainer} from 'angular2/src/core/compiler/view_container';
99
import {NgElement} from 'angular2/src/core/dom/element';
10-
import {Directive, onChange, onDestroy} from 'angular2/src/core/annotations/annotations';
10+
import {Directive, onChange, onDestroy, onAllChangesDone} from 'angular2/src/core/annotations/annotations';
1111
import {BindingPropagationConfig} from 'angular2/change_detection';
1212
import * as pclModule from 'angular2/src/core/compiler/private_component_location';
1313
import {setterFactory} from './property_setter_factory';
@@ -131,11 +131,13 @@ export class DirectiveDependency extends Dependency {
131131
export class DirectiveBinding extends Binding {
132132
callOnDestroy:boolean;
133133
callOnChange:boolean;
134+
callOnAllChangesDone:boolean;
134135

135136
constructor(key:Key, factory:Function, dependencies:List, providedAsPromise:boolean, annotation:Directive) {
136137
super(key, factory, dependencies, providedAsPromise);
137138
this.callOnDestroy = isPresent(annotation) && annotation.hasLifecycleHook(onDestroy);
138139
this.callOnChange = isPresent(annotation) && annotation.hasLifecycleHook(onChange);
140+
this.callOnAllChangesDone = isPresent(annotation) && annotation.hasLifecycleHook(onAllChangesDone);
139141
}
140142

141143
static createFromBinding(b:Binding, annotation:Directive):Binding {
@@ -216,6 +218,8 @@ export class ProtoElementInjector {
216218
distanceToParent:number;
217219
attributes:Map;
218220

221+
numberOfDirectives:number;
222+
219223
/** Whether the element is exported as $implicit. */
220224
exportElement:boolean;
221225

@@ -244,6 +248,7 @@ export class ProtoElementInjector {
244248
this._binding8 = null; this._keyId8 = null;
245249
this._binding9 = null; this._keyId9 = null;
246250

251+
this.numberOfDirectives = bindings.length;
247252
var length = bindings.length;
248253

249254
if (length > 0) {this._binding0 = this._createBinding(bindings[0]); this._keyId0 = this._binding0.key.id;}
@@ -282,6 +287,20 @@ export class ProtoElementInjector {
282287
return isPresent(this._binding0);
283288
}
284289

290+
getDirectiveBindingAtIndex(index:int) {
291+
if (index == 0) return this._binding0;
292+
if (index == 1) return this._binding1;
293+
if (index == 2) return this._binding2;
294+
if (index == 3) return this._binding3;
295+
if (index == 4) return this._binding4;
296+
if (index == 5) return this._binding5;
297+
if (index == 6) return this._binding6;
298+
if (index == 7) return this._binding7;
299+
if (index == 8) return this._binding8;
300+
if (index == 9) return this._binding9;
301+
throw new OutOfBoundsAccess(index);
302+
}
303+
285304
hasEventEmitter(eventName: string) {
286305
var p = this;
287306
if (isPresent(p._binding0) && DirectiveBinding._hasEventEmitter(eventName, p._binding0)) return true;
@@ -648,18 +667,7 @@ export class ElementInjector extends TreeNode {
648667
}
649668

650669
getDirectiveBindingAtIndex(index:int) {
651-
var p = this._proto;
652-
if (index == 0) return p._binding0;
653-
if (index == 1) return p._binding1;
654-
if (index == 2) return p._binding2;
655-
if (index == 3) return p._binding3;
656-
if (index == 4) return p._binding4;
657-
if (index == 5) return p._binding5;
658-
if (index == 6) return p._binding6;
659-
if (index == 7) return p._binding7;
660-
if (index == 8) return p._binding8;
661-
if (index == 9) return p._binding9;
662-
throw new OutOfBoundsAccess(index);
670+
return this._proto.getDirectiveBindingAtIndex(index);
663671
}
664672

665673
hasInstances() {

0 commit comments

Comments
 (0)
X Tutup