fix(ivy): init hooks being re-run if an exception is throw (#28024)

Fixes Ivy running the init hooks if an exception is thrown in one of them.

These changes fix FW-830.

PR Close #28024
This commit is contained in:
Kristiyan Kostadinov 2019-01-09 21:28:38 +01:00 committed by Andrew Kushnir
parent 3bafc002ae
commit a6ba789599
2 changed files with 30 additions and 29 deletions

View File

@ -319,11 +319,14 @@ export function leaveView(newView: LView): void {
if (isCreationMode(lView)) { if (isCreationMode(lView)) {
lView[FLAGS] &= ~LViewFlags.CreationMode; lView[FLAGS] &= ~LViewFlags.CreationMode;
} else { } else {
executeHooks(lView, tView.viewHooks, tView.viewCheckHooks, checkNoChangesMode); try {
// Views are clean and in update mode after being checked, so these bits are cleared executeHooks(lView, tView.viewHooks, tView.viewCheckHooks, checkNoChangesMode);
lView[FLAGS] &= ~(LViewFlags.Dirty | LViewFlags.FirstLViewPass); } finally {
lView[FLAGS] |= LViewFlags.RunInit; // Views are clean and in update mode after being checked, so these bits are cleared
lView[BINDING_INDEX] = tView.bindingStartIndex; lView[FLAGS] &= ~(LViewFlags.Dirty | LViewFlags.FirstLViewPass);
lView[FLAGS] |= LViewFlags.RunInit;
lView[BINDING_INDEX] = tView.bindingStartIndex;
}
} }
enterView(newView, null); enterView(newView, null);
} }

View File

@ -1022,33 +1022,31 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
expect(directiveLog.filter(['ngAfterViewInit'])).toEqual([]); expect(directiveLog.filter(['ngAfterViewInit'])).toEqual([]);
})); }));
fixmeIvy( it('should not call ngAfterViewInit again if it throws', fakeAsync(() => {
'FW-830: Exception thrown in ngAfterViewInit triggers ngAfterViewInit re-execution') const ctx =
.it('should not call ngAfterViewInit again if it throws', fakeAsync(() => { createCompFixture('<div testDirective="dir" throwOn="ngAfterViewInit"></div>');
const ctx = createCompFixture(
'<div testDirective="dir" throwOn="ngAfterViewInit"></div>');
let errored = false; let errored = false;
// First pass fails, but ngAfterViewInit should be called. // First pass fails, but ngAfterViewInit should be called.
try { try {
ctx.detectChanges(false); ctx.detectChanges(false);
} catch (e) { } catch (e) {
errored = true; errored = true;
} }
expect(errored).toBe(true); expect(errored).toBe(true);
expect(directiveLog.filter(['ngAfterViewInit'])).toEqual(['dir.ngAfterViewInit']); expect(directiveLog.filter(['ngAfterViewInit'])).toEqual(['dir.ngAfterViewInit']);
directiveLog.clear(); directiveLog.clear();
// Second change detection also fails, but this time ngAfterViewInit should not be // Second change detection also fails, but this time ngAfterViewInit should not be
// called. // called.
try { try {
ctx.detectChanges(false); ctx.detectChanges(false);
} catch (e) { } catch (e) {
throw new Error('Second detectChanges() should not have run detection.'); throw new Error('Second detectChanges() should not have run detection.');
} }
expect(directiveLog.filter(['ngAfterViewInit'])).toEqual([]); expect(directiveLog.filter(['ngAfterViewInit'])).toEqual([]);
})); }));
}); });
describe('ngAfterViewChecked', () => { describe('ngAfterViewChecked', () => {