mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-18 19:21:34 +08:00
fix(router-outlet): attach entering view before first change detection (#18821)
This commit is contained in:
@ -205,7 +205,6 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
|
||||
// Calling `markForCheck` to make sure we will run the change detection when the
|
||||
// `RouterOutlet` is inside a `ChangeDetectionStrategy.OnPush` component.
|
||||
enteringView = this.stackCtrl.createView(this.activated, activatedRoute);
|
||||
enteringView.ref.changeDetectorRef.detectChanges();
|
||||
|
||||
// Store references to the proxy by component
|
||||
this.proxyMap.set(cmpRef.instance, activatedRouteProxy);
|
||||
|
@ -31,7 +31,7 @@ export class StackController {
|
||||
createView(ref: ComponentRef<any>, activatedRoute: ActivatedRoute): RouteView {
|
||||
const url = getUrl(this.router, activatedRoute);
|
||||
const element = (ref && ref.location && ref.location.nativeElement) as HTMLElement;
|
||||
const unlistenEvents = bindLifecycleEvents(ref.instance, element);
|
||||
const unlistenEvents = bindLifecycleEvents(this.zone, ref.instance, element);
|
||||
return {
|
||||
id: this.nextId++,
|
||||
stackId: computeStackId(this.tabsPrefix, url),
|
||||
@ -90,16 +90,28 @@ export class StackController {
|
||||
}
|
||||
}
|
||||
|
||||
const reused = this.views.includes(enteringView);
|
||||
const views = this.insertView(enteringView, direction);
|
||||
return this.wait(() => {
|
||||
return this.transition(enteringView, leavingView, animation, this.canGoBack(1), false)
|
||||
.then(() => cleanupAsync(enteringView, views, viewsSnapshot, this.location))
|
||||
.then(() => ({
|
||||
enteringView,
|
||||
direction,
|
||||
animation,
|
||||
tabSwitch
|
||||
}));
|
||||
|
||||
// Trigger change detection before transition starts
|
||||
// This will call ngOnInit() the first time too, just after the view
|
||||
// was attached to the dom, but BEFORE the transition starts
|
||||
if (!reused) {
|
||||
enteringView.ref.changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
// Wait until previous transitions finish
|
||||
return this.zone.runOutsideAngular(() => {
|
||||
return this.wait(() => {
|
||||
return this.transition(enteringView, leavingView, animation, this.canGoBack(1), false)
|
||||
.then(() => cleanupAsync(enteringView, views, viewsSnapshot, this.location))
|
||||
.then(() => ({
|
||||
enteringView,
|
||||
direction,
|
||||
animation,
|
||||
tabSwitch
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -199,11 +211,11 @@ export class StackController {
|
||||
if (enteringView) {
|
||||
enteringView.ref.changeDetectorRef.reattach();
|
||||
}
|
||||
// TODO: disconnect leaving page from change detection to
|
||||
// disconnect leaving page from change detection to
|
||||
// reduce jank during the page transition
|
||||
// if (leavingView) {
|
||||
// leavingView.ref.changeDetectorRef.detach();
|
||||
// }
|
||||
if (leavingView) {
|
||||
leavingView.ref.changeDetectorRef.detach();
|
||||
}
|
||||
const enteringEl = enteringView ? enteringView.element : undefined;
|
||||
const leavingEl = leavingView ? leavingView.element : undefined;
|
||||
const containerEl = this.containerEl;
|
||||
@ -213,13 +225,13 @@ export class StackController {
|
||||
containerEl.appendChild(enteringEl);
|
||||
}
|
||||
|
||||
return this.zone.runOutsideAngular(() => containerEl.commit(enteringEl, leavingEl, {
|
||||
return containerEl.commit(enteringEl, leavingEl, {
|
||||
deepWait: true,
|
||||
duration: direction === undefined ? 0 : undefined,
|
||||
direction,
|
||||
showGoBack,
|
||||
progressAnimation
|
||||
}));
|
||||
});
|
||||
}
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
@ -34,10 +34,10 @@ export class AngularFrameworkDelegate implements FrameworkDelegate {
|
||||
) {}
|
||||
|
||||
attachViewToDom(container: any, component: any, params?: any, cssClasses?: string[]): Promise<any> {
|
||||
return new Promise(resolve => {
|
||||
this.zone.run(() => {
|
||||
return this.zone.run(() => {
|
||||
return new Promise(resolve => {
|
||||
const el = attachView(
|
||||
this.resolver, this.injector, this.location, this.appRef,
|
||||
this.zone, this.resolver, this.injector, this.location, this.appRef,
|
||||
this.elRefMap, this.elEventsMap,
|
||||
container, component, params, cssClasses
|
||||
);
|
||||
@ -47,8 +47,8 @@ export class AngularFrameworkDelegate implements FrameworkDelegate {
|
||||
}
|
||||
|
||||
removeViewFromDom(_container: any, component: any): Promise<void> {
|
||||
return new Promise(resolve => {
|
||||
this.zone.run(() => {
|
||||
return this.zone.run(() => {
|
||||
return new Promise(resolve => {
|
||||
const componentRef = this.elRefMap.get(component);
|
||||
if (componentRef) {
|
||||
componentRef.destroy();
|
||||
@ -66,6 +66,7 @@ export class AngularFrameworkDelegate implements FrameworkDelegate {
|
||||
}
|
||||
|
||||
export function attachView(
|
||||
zone: NgZone,
|
||||
resolver: ComponentFactoryResolver,
|
||||
injector: Injector,
|
||||
location: ViewContainerRef | undefined,
|
||||
@ -93,7 +94,7 @@ export function attachView(
|
||||
hostElement.classList.add(clazz);
|
||||
}
|
||||
}
|
||||
const unbindEvents = bindLifecycleEvents(instance, hostElement);
|
||||
const unbindEvents = bindLifecycleEvents(zone, instance, hostElement);
|
||||
container.appendChild(hostElement);
|
||||
|
||||
if (!location) {
|
||||
@ -113,15 +114,17 @@ const LIFECYCLES = [
|
||||
LIFECYCLE_WILL_UNLOAD
|
||||
];
|
||||
|
||||
export function bindLifecycleEvents(instance: any, element: HTMLElement) {
|
||||
const unregisters = LIFECYCLES
|
||||
.filter(eventName => typeof instance[eventName] === 'function')
|
||||
.map(eventName => {
|
||||
const handler = (ev: any) => instance[eventName](ev.detail);
|
||||
element.addEventListener(eventName, handler);
|
||||
return () => element.removeEventListener(eventName, handler);
|
||||
});
|
||||
return () => unregisters.forEach(fn => fn());
|
||||
export function bindLifecycleEvents(zone: NgZone, instance: any, element: HTMLElement) {
|
||||
return zone.run(() => {
|
||||
const unregisters = LIFECYCLES
|
||||
.filter(eventName => typeof instance[eventName] === 'function')
|
||||
.map(eventName => {
|
||||
const handler = (ev: any) => instance[eventName](ev.detail);
|
||||
element.addEventListener(eventName, handler);
|
||||
return () => element.removeEventListener(eventName, handler);
|
||||
});
|
||||
return () => unregisters.forEach(fn => fn());
|
||||
});
|
||||
}
|
||||
|
||||
const NavParamsToken = new InjectionToken<any>('NavParamsToken');
|
||||
|
Reference in New Issue
Block a user