fix(core): don’t stop change detection because of errors

- prevents unsubscribing from the zone on error
- prevents unsubscribing from directive `EventEmitter`s on error
- prevents detaching views in dev mode if there on error
- ensures that `ngOnInit` is only called 1x (also in prod mode)

Fixes #9531
Fixes #2413
Fixes #15925
This commit is contained in:
Tobias Bosch
2017-04-28 11:50:45 -07:00
committed by Matias Niemelä
parent ac220fc2bb
commit e263e19a2a
16 changed files with 218 additions and 68 deletions

View File

@ -131,20 +131,33 @@ export function main() {
describe('ApplicationRef', () => {
beforeEach(() => { TestBed.configureTestingModule({imports: [createModule()]}); });
it('should throw when reentering tick', inject([ApplicationRef], (ref: ApplicationRef_) => {
const view = jasmine.createSpyObj('view', ['detach', 'attachToAppRef']);
const viewRef = jasmine.createSpyObj(
'viewRef', ['detectChanges', 'detachFromAppRef', 'attachToAppRef']);
viewRef.internalView = view;
view.ref = viewRef;
try {
ref.attachView(viewRef);
viewRef.detectChanges.and.callFake(() => ref.tick());
expect(() => ref.tick()).toThrowError('ApplicationRef.tick is called recursively');
} finally {
ref.detachView(viewRef);
}
}));
it('should throw when reentering tick', () => {
@Component({template: '{{reenter()}}'})
class ReenteringComponent {
reenterCount = 1;
reenterErr: any;
constructor(private appRef: ApplicationRef) {}
reenter() {
if (this.reenterCount--) {
try {
this.appRef.tick();
} catch (e) {
this.reenterErr = e;
}
}
}
}
const fixture = TestBed.configureTestingModule({declarations: [ReenteringComponent]})
.createComponent(ReenteringComponent);
const appRef = TestBed.get(ApplicationRef) as ApplicationRef;
appRef.attachView(fixture.componentRef.hostView);
appRef.tick();
expect(fixture.componentInstance.reenterErr.message)
.toBe('ApplicationRef.tick is called recursively');
});
describe('APP_BOOTSTRAP_LISTENER', () => {
let capturedCompRefs: ComponentRef<any>[];