fix(router): RouterOutlet loads component twice in a race condition
Closes #7497 Closes #7545
This commit is contained in:
parent
d61aaac400
commit
2f581ffc88
@ -59,7 +59,7 @@ export abstract class ComponentRef {
|
|||||||
*
|
*
|
||||||
* TODO(i): rename to destroy to be consistent with AppViewManager and ViewContainerRef
|
* TODO(i): rename to destroy to be consistent with AppViewManager and ViewContainerRef
|
||||||
*/
|
*/
|
||||||
abstract dispose();
|
abstract dispose(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ComponentRef_ extends ComponentRef {
|
export class ComponentRef_ extends ComponentRef {
|
||||||
@ -84,7 +84,7 @@ export class ComponentRef_ extends ComponentRef {
|
|||||||
*/
|
*/
|
||||||
get hostComponentType(): Type { return this.componentType; }
|
get hostComponentType(): Type { return this.componentType; }
|
||||||
|
|
||||||
dispose() { this._dispose(); }
|
dispose(): void { this._dispose(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,7 +34,7 @@ let _resolveToTrue = PromiseWrapper.resolve(true);
|
|||||||
@Directive({selector: 'router-outlet'})
|
@Directive({selector: 'router-outlet'})
|
||||||
export class RouterOutlet implements OnDestroy {
|
export class RouterOutlet implements OnDestroy {
|
||||||
name: string = null;
|
name: string = null;
|
||||||
private _componentRef: ComponentRef = null;
|
private _componentRef: Promise<ComponentRef> = null;
|
||||||
private _currentInstruction: ComponentInstruction = null;
|
private _currentInstruction: ComponentInstruction = null;
|
||||||
|
|
||||||
constructor(private _elementRef: ElementRef, private _loader: DynamicComponentLoader,
|
constructor(private _elementRef: ElementRef, private _loader: DynamicComponentLoader,
|
||||||
@ -62,12 +62,15 @@ export class RouterOutlet implements OnDestroy {
|
|||||||
provide(RouteParams, {useValue: new RouteParams(nextInstruction.params)}),
|
provide(RouteParams, {useValue: new RouteParams(nextInstruction.params)}),
|
||||||
provide(routerMod.Router, {useValue: childRouter})
|
provide(routerMod.Router, {useValue: childRouter})
|
||||||
]);
|
]);
|
||||||
return this._loader.loadNextToLocation(componentType, this._elementRef, providers)
|
this._componentRef =
|
||||||
.then((componentRef) => {
|
this._loader.loadNextToLocation(componentType, this._elementRef, providers);
|
||||||
this._componentRef = componentRef;
|
return this._componentRef.then((componentRef) => {
|
||||||
if (hasLifecycleHook(hookMod.routerOnActivate, componentType)) {
|
if (hasLifecycleHook(hookMod.routerOnActivate, componentType)) {
|
||||||
return (<OnActivate>this._componentRef.instance)
|
return this._componentRef.then(
|
||||||
.routerOnActivate(nextInstruction, previousInstruction);
|
(ref: ComponentRef) =>
|
||||||
|
(<OnActivate>ref.instance).routerOnActivate(nextInstruction, previousInstruction));
|
||||||
|
} else {
|
||||||
|
return componentRef;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -86,13 +89,15 @@ export class RouterOutlet implements OnDestroy {
|
|||||||
// a new one.
|
// a new one.
|
||||||
if (isBlank(this._componentRef)) {
|
if (isBlank(this._componentRef)) {
|
||||||
return this.activate(nextInstruction);
|
return this.activate(nextInstruction);
|
||||||
}
|
} else {
|
||||||
return PromiseWrapper.resolve(
|
return PromiseWrapper.resolve(
|
||||||
hasLifecycleHook(hookMod.routerOnReuse, this._currentInstruction.componentType) ?
|
hasLifecycleHook(hookMod.routerOnReuse, this._currentInstruction.componentType) ?
|
||||||
(<OnReuse>this._componentRef.instance)
|
this._componentRef.then(
|
||||||
.routerOnReuse(nextInstruction, previousInstruction) :
|
(ref: ComponentRef) =>
|
||||||
|
(<OnReuse>ref.instance).routerOnReuse(nextInstruction, previousInstruction)) :
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by the {@link Router} when an outlet disposes of a component's contents.
|
* Called by the {@link Router} when an outlet disposes of a component's contents.
|
||||||
@ -102,14 +107,16 @@ export class RouterOutlet implements OnDestroy {
|
|||||||
var next = _resolveToTrue;
|
var next = _resolveToTrue;
|
||||||
if (isPresent(this._componentRef) && isPresent(this._currentInstruction) &&
|
if (isPresent(this._componentRef) && isPresent(this._currentInstruction) &&
|
||||||
hasLifecycleHook(hookMod.routerOnDeactivate, this._currentInstruction.componentType)) {
|
hasLifecycleHook(hookMod.routerOnDeactivate, this._currentInstruction.componentType)) {
|
||||||
next = <Promise<boolean>>PromiseWrapper.resolve(
|
next = this._componentRef.then(
|
||||||
(<OnDeactivate>this._componentRef.instance)
|
(ref: ComponentRef) =>
|
||||||
|
(<OnDeactivate>ref.instance)
|
||||||
.routerOnDeactivate(nextInstruction, this._currentInstruction));
|
.routerOnDeactivate(nextInstruction, this._currentInstruction));
|
||||||
}
|
}
|
||||||
return next.then((_) => {
|
return next.then((_) => {
|
||||||
if (isPresent(this._componentRef)) {
|
if (isPresent(this._componentRef)) {
|
||||||
this._componentRef.dispose();
|
var onDispose = this._componentRef.then((ref: ComponentRef) => ref.dispose());
|
||||||
this._componentRef = null;
|
this._componentRef = null;
|
||||||
|
return onDispose;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -127,12 +134,14 @@ export class RouterOutlet implements OnDestroy {
|
|||||||
return _resolveToTrue;
|
return _resolveToTrue;
|
||||||
}
|
}
|
||||||
if (hasLifecycleHook(hookMod.routerCanDeactivate, this._currentInstruction.componentType)) {
|
if (hasLifecycleHook(hookMod.routerCanDeactivate, this._currentInstruction.componentType)) {
|
||||||
return <Promise<boolean>>PromiseWrapper.resolve(
|
return this._componentRef.then(
|
||||||
(<CanDeactivate>this._componentRef.instance)
|
(ref: ComponentRef) =>
|
||||||
|
(<CanDeactivate>ref.instance)
|
||||||
.routerCanDeactivate(nextInstruction, this._currentInstruction));
|
.routerCanDeactivate(nextInstruction, this._currentInstruction));
|
||||||
}
|
} else {
|
||||||
return _resolveToTrue;
|
return _resolveToTrue;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by the {@link Router} during recognition phase of a navigation.
|
* Called by the {@link Router} during recognition phase of a navigation.
|
||||||
@ -151,8 +160,9 @@ export class RouterOutlet implements OnDestroy {
|
|||||||
this._currentInstruction.componentType != nextInstruction.componentType) {
|
this._currentInstruction.componentType != nextInstruction.componentType) {
|
||||||
result = false;
|
result = false;
|
||||||
} else if (hasLifecycleHook(hookMod.routerCanReuse, this._currentInstruction.componentType)) {
|
} else if (hasLifecycleHook(hookMod.routerCanReuse, this._currentInstruction.componentType)) {
|
||||||
result = (<CanReuse>this._componentRef.instance)
|
result = this._componentRef.then(
|
||||||
.routerCanReuse(nextInstruction, this._currentInstruction);
|
(ref: ComponentRef) =>
|
||||||
|
(<CanReuse>ref.instance).routerCanReuse(nextInstruction, this._currentInstruction));
|
||||||
} else {
|
} else {
|
||||||
result = nextInstruction == this._currentInstruction ||
|
result = nextInstruction == this._currentInstruction ||
|
||||||
(isPresent(nextInstruction.params) && isPresent(this._currentInstruction.params) &&
|
(isPresent(nextInstruction.params) && isPresent(this._currentInstruction.params) &&
|
||||||
|
@ -106,7 +106,7 @@ const CORE = [
|
|||||||
'ComponentMetadata.viewProviders:any[]',
|
'ComponentMetadata.viewProviders:any[]',
|
||||||
'ComponentRef',
|
'ComponentRef',
|
||||||
'ComponentRef.componentType:Type',
|
'ComponentRef.componentType:Type',
|
||||||
'ComponentRef.dispose():any',
|
'ComponentRef.dispose():void',
|
||||||
'ComponentRef.hostComponent:any',
|
'ComponentRef.hostComponent:any',
|
||||||
'ComponentRef.hostView:HostViewRef',
|
'ComponentRef.hostView:HostViewRef',
|
||||||
'ComponentRef.injector:Injector',
|
'ComponentRef.injector:Injector',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user