refactor(exception_handler): unified all exception handling
BREAKING CHANGE Previously it was possible to pass a custom error reporter to bootstrap, which was used only during the construction of Injector. This had limited utility, so this capability has been removed.
This commit is contained in:
parent
fdf226ab69
commit
70792c744d
|
@ -15,7 +15,7 @@ export 'application_common.dart' show ApplicationRef;
|
||||||
///
|
///
|
||||||
/// See [commonBootstrap] for detailed documentation.
|
/// See [commonBootstrap] for detailed documentation.
|
||||||
Future<ApplicationRef> bootstrap(Type appComponentType,
|
Future<ApplicationRef> bootstrap(Type appComponentType,
|
||||||
[List componentInjectableBindings, Function errorReporter]) {
|
[List componentInjectableBindings]) {
|
||||||
reflector.reflectionCapabilities = new ReflectionCapabilities();
|
reflector.reflectionCapabilities = new ReflectionCapabilities();
|
||||||
return commonBootstrap(appComponentType, componentInjectableBindings, errorReporter);
|
return commonBootstrap(appComponentType, componentInjectableBindings);
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,8 +97,7 @@ function _injectorBindings(appComponentType): List<Type | Binding | List<any>> {
|
||||||
|
|
||||||
bind(appComponentType)
|
bind(appComponentType)
|
||||||
.toFactory((p: Promise<any>) => p.then(ref => ref.instance), [appComponentRefPromiseToken]),
|
.toFactory((p: Promise<any>) => p.then(ref => ref.instance), [appComponentRefPromiseToken]),
|
||||||
bind(LifeCycle)
|
bind(LifeCycle).toFactory((exceptionHandler) => new LifeCycle(null, assertionsEnabled()),
|
||||||
.toFactory((exceptionHandler) => new LifeCycle(exceptionHandler, null, assertionsEnabled()),
|
|
||||||
[ExceptionHandler]),
|
[ExceptionHandler]),
|
||||||
bind(EventManager)
|
bind(EventManager)
|
||||||
.toFactory(
|
.toFactory(
|
||||||
|
@ -141,22 +140,13 @@ function _injectorBindings(appComponentType): List<Type | Binding | List<any>> {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
function _createNgZone(givenReporter: Function): NgZone {
|
function _createNgZone(): NgZone {
|
||||||
var defaultErrorReporter = (exception, stackTrace) => {
|
// bootstrapErrorReporter is needed because we cannot use custom exception handler
|
||||||
var longStackTrace = ListWrapper.join(stackTrace, "\n\n-----async gap-----\n");
|
// configured via DI until the root Injector has been created.
|
||||||
DOM.logError(`${exception}\n\n${longStackTrace}`);
|
var handler = new ExceptionHandler();
|
||||||
|
var bootstrapErrorReporter = (exception, stackTrace) => handler.call(exception, stackTrace);
|
||||||
if (exception instanceof BaseException && isPresent(exception.context)) {
|
|
||||||
print("Error Context:");
|
|
||||||
print(exception.context);
|
|
||||||
}
|
|
||||||
throw exception;
|
|
||||||
};
|
|
||||||
|
|
||||||
var reporter = isPresent(givenReporter) ? givenReporter : defaultErrorReporter;
|
|
||||||
|
|
||||||
var zone = new NgZone({enableLongStackTrace: assertionsEnabled()});
|
var zone = new NgZone({enableLongStackTrace: assertionsEnabled()});
|
||||||
zone.overrideOnErrorHandler(reporter);
|
zone.overrideOnErrorHandler(bootstrapErrorReporter);
|
||||||
return zone;
|
return zone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,17 +277,20 @@ function _createNgZone(givenReporter: Function): NgZone {
|
||||||
* Returns a `Promise` of {@link ApplicationRef}.
|
* Returns a `Promise` of {@link ApplicationRef}.
|
||||||
*/
|
*/
|
||||||
export function commonBootstrap(
|
export function commonBootstrap(
|
||||||
appComponentType: Type, componentInjectableBindings: List<Type | Binding | List<any>> = null,
|
appComponentType: Type, componentInjectableBindings: List<Type | Binding | List<any>> = null):
|
||||||
errorReporter: Function = null): Promise<ApplicationRef> {
|
Promise<ApplicationRef> {
|
||||||
BrowserDomAdapter.makeCurrent();
|
BrowserDomAdapter.makeCurrent();
|
||||||
var bootstrapProcess = PromiseWrapper.completer();
|
var bootstrapProcess = PromiseWrapper.completer();
|
||||||
|
|
||||||
var zone = _createNgZone(errorReporter);
|
var zone = _createNgZone();
|
||||||
zone.run(() => {
|
zone.run(() => {
|
||||||
// TODO(rado): prepopulate template cache, so applications with only
|
// TODO(rado): prepopulate template cache, so applications with only
|
||||||
// index.html and main.js are possible.
|
// index.html and main.js are possible.
|
||||||
|
|
||||||
var appInjector = _createAppInjector(appComponentType, componentInjectableBindings, zone);
|
var appInjector = _createAppInjector(appComponentType, componentInjectableBindings, zone);
|
||||||
|
var exceptionHandler = appInjector.get(ExceptionHandler);
|
||||||
|
zone.overrideOnErrorHandler((e, s) => exceptionHandler.call(e, s));
|
||||||
|
|
||||||
var compRefToken: Promise<any> =
|
var compRefToken: Promise<any> =
|
||||||
PromiseWrapper.wrap(() => appInjector.get(appComponentRefPromiseToken));
|
PromiseWrapper.wrap(() => appInjector.get(appComponentRefPromiseToken));
|
||||||
var tick = (componentRef) => {
|
var tick = (componentRef) => {
|
||||||
|
|
|
@ -8,7 +8,6 @@ import 'application_common.dart';
|
||||||
/// See [commonBootstrap] for detailed documentation.
|
/// See [commonBootstrap] for detailed documentation.
|
||||||
Future<ApplicationRef> bootstrapStatic(
|
Future<ApplicationRef> bootstrapStatic(
|
||||||
Type appComponentType,
|
Type appComponentType,
|
||||||
[List componentInjectableBindings,
|
[List componentInjectableBindings]) {
|
||||||
Function errorReporter]) {
|
return commonBootstrap(appComponentType, componentInjectableBindings);
|
||||||
return commonBootstrap(appComponentType, componentInjectableBindings, errorReporter);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
export class ExceptionHandler {
|
export class ExceptionHandler {
|
||||||
logError: Function = DOM.logError;
|
logError: Function = DOM.logError;
|
||||||
|
|
||||||
call(exception: Object, stackTrace: string | string[] = null, reason: string = null) {
|
call(exception: Object, stackTrace: any = null, reason: string = null) {
|
||||||
var longStackTrace = isListLikeIterable(stackTrace) ?
|
var longStackTrace = isListLikeIterable(stackTrace) ?
|
||||||
(<any>stackTrace).join("\n\n-----async gap-----\n") :
|
(<any>stackTrace).join("\n\n-----async gap-----\n") :
|
||||||
stackTrace;
|
stackTrace;
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import {Injectable} from 'angular2/di';
|
import {Injectable} from 'angular2/di';
|
||||||
import {ChangeDetector} from 'angular2/change_detection';
|
import {ChangeDetector} from 'angular2/change_detection';
|
||||||
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
||||||
import {ExceptionHandler} from 'angular2/src/core/exception_handler';
|
|
||||||
import {isPresent, BaseException} from 'angular2/src/facade/lang';
|
import {isPresent, BaseException} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -32,17 +31,11 @@ import {isPresent, BaseException} from 'angular2/src/facade/lang';
|
||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class LifeCycle {
|
export class LifeCycle {
|
||||||
_errorHandler;
|
|
||||||
_changeDetector: ChangeDetector;
|
_changeDetector: ChangeDetector;
|
||||||
_enforceNoNewChanges: boolean;
|
_enforceNoNewChanges: boolean;
|
||||||
_runningTick: boolean = false;
|
_runningTick: boolean = false;
|
||||||
|
|
||||||
constructor(exceptionHandler: ExceptionHandler, changeDetector: ChangeDetector = null,
|
constructor(changeDetector: ChangeDetector = null, enforceNoNewChanges: boolean = false) {
|
||||||
enforceNoNewChanges: boolean = false) {
|
|
||||||
this._errorHandler = (exception, stackTrace) => {
|
|
||||||
exceptionHandler.call(exception, stackTrace);
|
|
||||||
throw exception;
|
|
||||||
};
|
|
||||||
this._changeDetector =
|
this._changeDetector =
|
||||||
changeDetector; // may be null when instantiated from application bootstrap
|
changeDetector; // may be null when instantiated from application bootstrap
|
||||||
this._enforceNoNewChanges = enforceNoNewChanges;
|
this._enforceNoNewChanges = enforceNoNewChanges;
|
||||||
|
@ -55,8 +48,6 @@ export class LifeCycle {
|
||||||
if (isPresent(changeDetector)) {
|
if (isPresent(changeDetector)) {
|
||||||
this._changeDetector = changeDetector;
|
this._changeDetector = changeDetector;
|
||||||
}
|
}
|
||||||
|
|
||||||
zone.overrideOnErrorHandler(this._errorHandler);
|
|
||||||
zone.overrideOnTurnDone(() => this.tick());
|
zone.overrideOnTurnDone(() => this.tick());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -83,8 +83,7 @@ export function main() {
|
||||||
|
|
||||||
it('should throw if bootstrapped Directive is not a Component',
|
it('should throw if bootstrapped Directive is not a Component',
|
||||||
inject([AsyncTestCompleter], (async) => {
|
inject([AsyncTestCompleter], (async) => {
|
||||||
var refPromise =
|
var refPromise = bootstrap(HelloRootDirectiveIsNotCmp, testBindings);
|
||||||
bootstrap(HelloRootDirectiveIsNotCmp, testBindings, (e, t) => { throw e; });
|
|
||||||
|
|
||||||
PromiseWrapper.then(refPromise, null, (reason) => {
|
PromiseWrapper.then(refPromise, null, (reason) => {
|
||||||
expect(reason.message)
|
expect(reason.message)
|
||||||
|
@ -96,7 +95,7 @@ export function main() {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should throw if no element is found', inject([AsyncTestCompleter], (async) => {
|
it('should throw if no element is found', inject([AsyncTestCompleter], (async) => {
|
||||||
var refPromise = bootstrap(HelloRootCmp, [], (e, t) => { throw e; });
|
var refPromise = bootstrap(HelloRootCmp, []);
|
||||||
PromiseWrapper.then(refPromise, null, (reason) => {
|
PromiseWrapper.then(refPromise, null, (reason) => {
|
||||||
expect(reason.message).toContain('The selector "hello-app" did not match any elements');
|
expect(reason.message).toContain('The selector "hello-app" did not match any elements');
|
||||||
async.done();
|
async.done();
|
||||||
|
|
|
@ -21,7 +21,7 @@ export function main() {
|
||||||
describe("LifeCycle", () => {
|
describe("LifeCycle", () => {
|
||||||
it("should throw when reentering tick", () => {
|
it("should throw when reentering tick", () => {
|
||||||
var cd = <any>new SpyChangeDetector();
|
var cd = <any>new SpyChangeDetector();
|
||||||
var lc = new LifeCycle(null, cd, false);
|
var lc = new LifeCycle(cd, false);
|
||||||
|
|
||||||
cd.spy("detectChanges").andCallFake(() => lc.tick());
|
cd.spy("detectChanges").andCallFake(() => lc.tick());
|
||||||
expect(() => lc.tick()).toThrowError("LifeCycle.tick is called recursively");
|
expect(() => lc.tick()).toThrowError("LifeCycle.tick is called recursively");
|
||||||
|
|
Loading…
Reference in New Issue