Skip to content

Commit d49bc43

Browse files
committed
feat(core): added afterContentInit, afterViewInit, and afterViewChecked hooks
Closes #3897
1 parent f93cd9c commit d49bc43

File tree

36 files changed

+926
-205
lines changed

36 files changed

+926
-205
lines changed

modules/angular2/metadata.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,10 @@ export {
3838
} from './src/core/metadata';
3939

4040
export {
41+
AfterContentInit,
4142
AfterContentChecked,
43+
AfterViewInit,
44+
AfterViewChecked,
4245
OnChanges,
4346
OnDestroy,
4447
OnInit,

modules/angular2/src/core/change_detection/abstract_change_detector.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,19 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
8282
this.mode === ChangeDetectionStrategy.Checked)
8383
return;
8484
var s = _scope_check(this.id, throwOnChange);
85+
8586
this.detectChangesInRecords(throwOnChange);
87+
8688
this._detectChangesInLightDomChildren(throwOnChange);
87-
if (throwOnChange === false) this.callAfterContentChecked();
89+
if (!throwOnChange) this.afterContentLifecycleCallbacks();
90+
8891
this._detectChangesInShadowDomChildren(throwOnChange);
92+
if (!throwOnChange) this.afterViewLifecycleCallbacks();
93+
8994
if (this.mode === ChangeDetectionStrategy.CheckOnce)
9095
this.mode = ChangeDetectionStrategy.Checked;
96+
97+
this.alreadyChecked = true;
9198
wtfLeave(s);
9299
}
93100

@@ -156,7 +163,19 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
156163

157164
hydrated(): boolean { return this.context !== null; }
158165

159-
callAfterContentChecked(): void { this.dispatcher.notifyAfterContentChecked(); }
166+
afterContentLifecycleCallbacks(): void {
167+
this.dispatcher.notifyAfterContentChecked();
168+
this.afterContentLifecycleCallbacksInternal();
169+
}
170+
171+
afterContentLifecycleCallbacksInternal(): void {}
172+
173+
afterViewLifecycleCallbacks(): void {
174+
this.dispatcher.notifyAfterViewChecked();
175+
this.afterViewLifecycleCallbacksInternal();
176+
}
177+
178+
afterViewLifecycleCallbacksInternal(): void {}
160179

161180
_detectChangesInLightDomChildren(throwOnChange: boolean): void {
162181
var c = this.lightDomChildren;

modules/angular2/src/core/change_detection/change_detection_jit_generator.ts

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -63,23 +63,23 @@ export class ChangeDetectorJITGenerator {
6363
var ${CHANGES_LOCAL} = null;
6464
6565
${this.records.map((r) => this._genRecord(r)).join("\n")}
66-
67-
${this._names.getAlreadyCheckedName()} = true;
6866
}
6967
7068
${this._maybeGenHandleEventInternal()}
7169
7270
${this._genCheckNoChanges()}
7371
74-
${this._maybeGenCallAfterContentChecked()}
72+
${this._maybeGenAfterContentLifecycleCallbacks()}
73+
74+
${this._maybeGenAfterViewLifecycleCallbacks()}
7575
7676
${this._maybeGenHydrateDirectives()}
7777
7878
${this._maybeGenDehydrateDirectives()}
7979
80-
${this._genPropertyBindingTargets()};
80+
${this._genPropertyBindingTargets()}
8181
82-
${this._genDirectiveIndices()};
82+
${this._genDirectiveIndices()}
8383
8484
return function(dispatcher) {
8585
return new ${this._typeName}(dispatcher);
@@ -172,23 +172,26 @@ export class ChangeDetectorJITGenerator {
172172
}`;
173173
}
174174

175-
_maybeGenCallAfterContentChecked(): string {
176-
var notifications = [];
177-
var dirs = this.directiveRecords;
178-
179-
// NOTE(kegluneq): Order is important!
180-
for (var i = dirs.length - 1; i >= 0; --i) {
181-
var dir = dirs[i];
182-
if (dir.callAfterContentChecked) {
183-
notifications.push(
184-
`${this._names.getDirectiveName(dir.directiveIndex)}.afterContentChecked();`);
185-
}
175+
_maybeGenAfterContentLifecycleCallbacks(): string {
176+
var notifications = this._logic.genContentLifecycleCallbacks(this.directiveRecords);
177+
if (notifications.length > 0) {
178+
var directiveNotifications = notifications.join("\n");
179+
return `
180+
${this._typeName}.prototype.afterContentLifecycleCallbacksInternal = function() {
181+
${directiveNotifications}
182+
}
183+
`;
184+
} else {
185+
return '';
186186
}
187+
}
188+
189+
_maybeGenAfterViewLifecycleCallbacks(): string {
190+
var notifications = this._logic.genViewLifecycleCallbacks(this.directiveRecords);
187191
if (notifications.length > 0) {
188192
var directiveNotifications = notifications.join("\n");
189193
return `
190-
${this._typeName}.prototype.callAfterContentChecked = function() {
191-
${ABSTRACT_CHANGE_DETECTOR}.prototype.callAfterContentChecked.call(this);
194+
${this._typeName}.prototype.afterViewLifecycleCallbacksInternal = function() {
192195
${directiveNotifications}
193196
}
194197
`;

modules/angular2/src/core/change_detection/codegen_logic_util.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,4 +183,38 @@ export class CodegenLogicUtil {
183183
}
184184
return res.join("\n");
185185
}
186+
187+
genContentLifecycleCallbacks(directiveRecords: DirectiveRecord[]): string[] {
188+
var res = [];
189+
// NOTE(kegluneq): Order is important!
190+
for (var i = directiveRecords.length - 1; i >= 0; --i) {
191+
var dir = directiveRecords[i];
192+
if (dir.callAfterContentInit) {
193+
res.push(
194+
`if(! ${this._names.getAlreadyCheckedName()}) ${this._names.getDirectiveName(dir.directiveIndex)}.afterContentInit();`);
195+
}
196+
197+
if (dir.callAfterContentChecked) {
198+
res.push(`${this._names.getDirectiveName(dir.directiveIndex)}.afterContentChecked();`);
199+
}
200+
}
201+
return res;
202+
}
203+
204+
genViewLifecycleCallbacks(directiveRecords: DirectiveRecord[]): string[] {
205+
var res = [];
206+
// NOTE(kegluneq): Order is important!
207+
for (var i = directiveRecords.length - 1; i >= 0; --i) {
208+
var dir = directiveRecords[i];
209+
if (dir.callAfterViewInit) {
210+
res.push(
211+
`if(! ${this._names.getAlreadyCheckedName()}) ${this._names.getDirectiveName(dir.directiveIndex)}.afterViewInit();`);
212+
}
213+
214+
if (dir.callAfterViewChecked) {
215+
res.push(`${this._names.getDirectiveName(dir.directiveIndex)}.afterViewChecked();`);
216+
}
217+
}
218+
return res;
219+
}
186220
}

modules/angular2/src/core/change_detection/directive_record.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,33 @@ export class DirectiveIndex {
99

1010
export class DirectiveRecord {
1111
directiveIndex: DirectiveIndex;
12+
callAfterContentInit: boolean;
1213
callAfterContentChecked: boolean;
14+
callAfterViewInit: boolean;
15+
callAfterViewChecked: boolean;
1316
callOnChanges: boolean;
1417
callDoCheck: boolean;
1518
callOnInit: boolean;
1619
changeDetection: ChangeDetectionStrategy;
1720

18-
constructor({directiveIndex, callAfterContentChecked, callOnChanges, callDoCheck, callOnInit,
19-
changeDetection}: {
21+
constructor({directiveIndex, callAfterContentInit, callAfterContentChecked, callAfterViewInit,
22+
callAfterViewChecked, callOnChanges, callDoCheck, callOnInit, changeDetection}: {
2023
directiveIndex?: DirectiveIndex,
24+
callAfterContentInit?: boolean,
2125
callAfterContentChecked?: boolean,
26+
callAfterViewInit?: boolean,
27+
callAfterViewChecked?: boolean,
2228
callOnChanges?: boolean,
2329
callDoCheck?: boolean,
2430
callOnInit?: boolean,
2531
changeDetection?: ChangeDetectionStrategy
2632
} = {}) {
2733
this.directiveIndex = directiveIndex;
34+
this.callAfterContentInit = normalizeBool(callAfterContentInit);
2835
this.callAfterContentChecked = normalizeBool(callAfterContentChecked);
2936
this.callOnChanges = normalizeBool(callOnChanges);
37+
this.callAfterViewInit = normalizeBool(callAfterViewInit);
38+
this.callAfterViewChecked = normalizeBool(callAfterViewChecked);
3039
this.callDoCheck = normalizeBool(callDoCheck);
3140
this.callOnInit = normalizeBool(callOnInit);
3241
this.changeDetection = changeDetection;

modules/angular2/src/core/change_detection/dynamic_change_detector.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -159,26 +159,40 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
159159
isChanged = false;
160160
}
161161
}
162-
163-
this.alreadyChecked = true;
164162
}
165163

166164
_firstInBinding(r: ProtoRecord): boolean {
167165
var prev = ChangeDetectionUtil.protoByIndex(this.records, r.selfIndex - 1);
168166
return isBlank(prev) || prev.bindingRecord !== r.bindingRecord;
169167
}
170168

171-
callAfterContentChecked() {
172-
super.callAfterContentChecked();
169+
afterContentLifecycleCallbacksInternal() {
173170
var dirs = this.directiveRecords;
174171
for (var i = dirs.length - 1; i >= 0; --i) {
175172
var dir = dirs[i];
173+
if (dir.callAfterContentInit && !this.alreadyChecked) {
174+
this._getDirectiveFor(dir.directiveIndex).afterContentInit();
175+
}
176+
176177
if (dir.callAfterContentChecked) {
177178
this._getDirectiveFor(dir.directiveIndex).afterContentChecked();
178179
}
179180
}
180181
}
181182

183+
afterViewLifecycleCallbacksInternal() {
184+
var dirs = this.directiveRecords;
185+
for (var i = dirs.length - 1; i >= 0; --i) {
186+
var dir = dirs[i];
187+
if (dir.callAfterViewInit && !this.alreadyChecked) {
188+
this._getDirectiveFor(dir.directiveIndex).afterViewInit();
189+
}
190+
if (dir.callAfterViewChecked) {
191+
this._getDirectiveFor(dir.directiveIndex).afterViewChecked();
192+
}
193+
}
194+
}
195+
182196
_updateDirectiveOrElement(change, bindingRecord) {
183197
if (isBlank(bindingRecord.directiveRecord)) {
184198
super.notifyDispatcher(change.currentValue);

modules/angular2/src/core/change_detection/interfaces.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export interface ChangeDispatcher {
5252
notifyOnBinding(bindingTarget: BindingTarget, value: any): void;
5353
logBindingUpdate(bindingTarget: BindingTarget, value: any): void;
5454
notifyAfterContentChecked(): void;
55+
notifyAfterViewChecked(): void;
5556
}
5657

5758
export interface ChangeDetector {

modules/angular2/src/core/compiler/directive_lifecycle_reflector.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,14 @@ bool hasLifecycleHook(LifecycleEvent e, type, DirectiveMetadata annotation) {
1717
interface = OnChanges;
1818
} else if (e == LifecycleEvent.OnDestroy) {
1919
interface = OnDestroy;
20+
} else if (e == LifecycleEvent.AfterContentInit) {
21+
interface = AfterContentInit;
2022
} else if (e == LifecycleEvent.AfterContentChecked) {
2123
interface = AfterContentChecked;
24+
} else if (e == LifecycleEvent.AfterViewInit) {
25+
interface = AfterViewInit;
26+
} else if (e == LifecycleEvent.AfterViewChecked) {
27+
interface = AfterViewChecked;
2228
} else if (e == LifecycleEvent.DoCheck) {
2329
interface = DoCheck;
2430
} else if (e == LifecycleEvent.OnInit) {

modules/angular2/src/core/compiler/directive_lifecycle_reflector.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,14 @@ export function hasLifecycleHook(e: LifecycleEvent, type, annotation: DirectiveM
88
if (!(type instanceof Type)) return false;
99
var proto = (<any>type).prototype;
1010
switch (e) {
11+
case LifecycleEvent.AfterContentInit:
12+
return !!proto.afterContentInit;
1113
case LifecycleEvent.AfterContentChecked:
1214
return !!proto.afterContentChecked;
15+
case LifecycleEvent.AfterViewInit:
16+
return !!proto.afterViewInit;
17+
case LifecycleEvent.AfterViewChecked:
18+
return !!proto.afterViewChecked;
1319
case LifecycleEvent.OnChanges:
1420
return !!proto.onChanges;
1521
case LifecycleEvent.DoCheck:

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

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -216,38 +216,41 @@ export class DirectiveBinding extends ResolvedBinding {
216216

217217
get changeDetection() { return this.metadata.changeDetection; }
218218

219-
static createFromBinding(binding: Binding, ann: DirectiveMetadata): DirectiveBinding {
220-
if (isBlank(ann)) {
221-
ann = new DirectiveMetadata();
219+
static createFromBinding(binding: Binding, meta: DirectiveMetadata): DirectiveBinding {
220+
if (isBlank(meta)) {
221+
meta = new DirectiveMetadata();
222222
}
223223

224224
var rb = binding.resolve();
225225
var deps = ListWrapper.map(rb.dependencies, DirectiveDependency.createFrom);
226-
var resolvedBindings = isPresent(ann.bindings) ? Injector.resolve(ann.bindings) : [];
227-
var resolvedViewBindings = ann instanceof ComponentMetadata && isPresent(ann.viewBindings) ?
228-
Injector.resolve(ann.viewBindings) :
226+
var resolvedBindings = isPresent(meta.bindings) ? Injector.resolve(meta.bindings) : [];
227+
var resolvedViewBindings = meta instanceof ComponentMetadata && isPresent(meta.viewBindings) ?
228+
Injector.resolve(meta.viewBindings) :
229229
[];
230230
var metadata = RenderDirectiveMetadata.create({
231231
id: stringify(rb.key.token),
232-
type: ann instanceof ComponentMetadata ? RenderDirectiveMetadata.COMPONENT_TYPE :
233-
RenderDirectiveMetadata.DIRECTIVE_TYPE,
234-
selector: ann.selector,
235-
compileChildren: ann.compileChildren,
236-
events: ann.events,
237-
host: isPresent(ann.host) ? MapWrapper.createFromStringMap(ann.host) : null,
238-
properties: ann.properties,
232+
type: meta instanceof ComponentMetadata ? RenderDirectiveMetadata.COMPONENT_TYPE :
233+
RenderDirectiveMetadata.DIRECTIVE_TYPE,
234+
selector: meta.selector,
235+
compileChildren: meta.compileChildren,
236+
events: meta.events,
237+
host: isPresent(meta.host) ? MapWrapper.createFromStringMap(meta.host) : null,
238+
properties: meta.properties,
239239
readAttributes: DirectiveBinding._readAttributes(deps),
240240

241-
callOnDestroy: hasLifecycleHook(LifecycleEvent.OnDestroy, rb.key.token, ann),
242-
callOnChanges: hasLifecycleHook(LifecycleEvent.OnChanges, rb.key.token, ann),
243-
callDoCheck: hasLifecycleHook(LifecycleEvent.DoCheck, rb.key.token, ann),
244-
callOnInit: hasLifecycleHook(LifecycleEvent.OnInit, rb.key.token, ann),
241+
callOnDestroy: hasLifecycleHook(LifecycleEvent.OnDestroy, rb.key.token, meta),
242+
callOnChanges: hasLifecycleHook(LifecycleEvent.OnChanges, rb.key.token, meta),
243+
callDoCheck: hasLifecycleHook(LifecycleEvent.DoCheck, rb.key.token, meta),
244+
callOnInit: hasLifecycleHook(LifecycleEvent.OnInit, rb.key.token, meta),
245+
callAfterContentInit: hasLifecycleHook(LifecycleEvent.AfterContentInit, rb.key.token, meta),
245246
callAfterContentChecked:
246-
hasLifecycleHook(LifecycleEvent.AfterContentChecked, rb.key.token, ann),
247+
hasLifecycleHook(LifecycleEvent.AfterContentChecked, rb.key.token, meta),
248+
callAfterViewInit: hasLifecycleHook(LifecycleEvent.AfterViewInit, rb.key.token, meta),
249+
callAfterViewChecked: hasLifecycleHook(LifecycleEvent.AfterViewChecked, rb.key.token, meta),
247250

248-
changeDetection: ann instanceof ComponentMetadata ? ann.changeDetection : null,
251+
changeDetection: meta instanceof ComponentMetadata ? meta.changeDetection : null,
249252

250-
exportAs: ann.exportAs
253+
exportAs: meta.exportAs
251254
});
252255
return new DirectiveBinding(rb.key, rb.factory, deps, resolvedBindings, resolvedViewBindings,
253256
metadata);

0 commit comments

Comments
 (0)