MOBILE-3833 collapsible: Wait tabs to be ready

main
Pau Ferrer Ocaña 2022-03-31 11:02:52 +02:00
parent 23275c7903
commit cdeb4af6da
4 changed files with 52 additions and 17 deletions

View File

@ -23,6 +23,7 @@ import {
AfterViewInit, AfterViewInit,
ViewChild, ViewChild,
SimpleChange, SimpleChange,
ElementRef,
} from '@angular/core'; } from '@angular/core';
import { IonSlides } from '@ionic/angular'; import { IonSlides } from '@ionic/angular';
import { BackButtonEvent } from '@ionic/core'; import { BackButtonEvent } from '@ionic/core';
@ -35,6 +36,9 @@ import { CoreEventObserver } from '@singletons/events';
import { CoreDom } from '@singletons/dom'; import { CoreDom } from '@singletons/dom';
import { CoreUtils } from '@services/utils/utils'; import { CoreUtils } from '@services/utils/utils';
import { CoreError } from './errors/error'; import { CoreError } from './errors/error';
import { CorePromisedValue } from './promised-value';
import { AsyncComponent } from './async-component';
import { CoreComponentsRegistry } from '@singletons/components-registry';
/** /**
* Class to abstract some common code for tabs. * Class to abstract some common code for tabs.
@ -42,7 +46,7 @@ import { CoreError } from './errors/error';
@Component({ @Component({
template: '', template: '',
}) })
export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, AfterViewInit, OnChanges, OnDestroy { export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, AfterViewInit, OnChanges, OnDestroy, AsyncComponent {
// Minimum tab's width. // Minimum tab's width.
protected static readonly MIN_TAB_WIDTH = 107; protected static readonly MIN_TAB_WIDTH = 107;
@ -85,13 +89,16 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
// Swiper 6 documentation: https://swiper6.vercel.app/ // Swiper 6 documentation: https://swiper6.vercel.app/
protected isInTransition = false; // Wether Slides is in transition. protected isInTransition = false; // Wether Slides is in transition.
protected subscriptions: Subscription[] = []; protected subscriptions: Subscription[] = [];
protected onReadyPromise = new CorePromisedValue<void>();
tabAction: CoreTabsRoleTab<T>; tabAction: CoreTabsRoleTab<T>;
constructor() { constructor(element: ElementRef) {
this.backButtonFunction = this.backButtonClicked.bind(this); this.backButtonFunction = this.backButtonClicked.bind(this);
this.tabAction = new CoreTabsRoleTab(this); this.tabAction = new CoreTabsRoleTab(this);
CoreComponentsRegistry.register(element.nativeElement, this);
} }
/** /**
@ -525,6 +532,7 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
if (suceeded !== false) { if (suceeded !== false) {
this.tabSelected(tabToSelect, index); this.tabSelected(tabToSelect, index);
} }
this.onReadyPromise.resolve();
} }
/** /**
@ -554,7 +562,14 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
} }
/** /**
* Component destroyed. * @inheritdoc
*/
async ready(): Promise<void> {
return await this.onReadyPromise;
}
/**
* @inheritdoc
*/ */
ngOnDestroy(): void { ngOnDestroy(): void {
this.isDestroyed = true; this.isDestroyed = true;

View File

@ -20,7 +20,6 @@ import {
OnDestroy, OnDestroy,
AfterViewInit, AfterViewInit,
ViewChild, ViewChild,
ElementRef,
SimpleChange, SimpleChange,
} from '@angular/core'; } from '@angular/core';
import { IonRouterOutlet, IonTabs, ViewDidEnter, ViewDidLeave } from '@ionic/angular'; import { IonRouterOutlet, IonTabs, ViewDidEnter, ViewDidLeave } from '@ionic/angular';
@ -69,12 +68,6 @@ export class CoreTabsOutletComponent extends CoreTabsBaseComponent<CoreTabsOutle
protected lastActiveComponent?: Partial<ViewDidLeave>; protected lastActiveComponent?: Partial<ViewDidLeave>;
protected existsInNavigationStack = false; protected existsInNavigationStack = false;
constructor(element: ElementRef) {
super();
CoreComponentsRegistry.register(element.nativeElement, this);
}
/** /**
* Init tab info. * Init tab info.
* *
@ -223,7 +216,7 @@ export class CoreTabsOutletComponent extends CoreTabsBaseComponent<CoreTabsOutle
} }
/** /**
* Component destroyed. * @inheritdoc
*/ */
ngOnDestroy(): void { ngOnDestroy(): void {
super.ngOnDestroy(); super.ngOnDestroy();

View File

@ -16,6 +16,7 @@ import { Directive, ElementRef, Input, OnChanges, OnDestroy, OnInit, SimpleChang
import { CorePromisedValue } from '@classes/promised-value'; import { CorePromisedValue } from '@classes/promised-value';
import { CoreLoadingComponent } from '@components/loading/loading'; import { CoreLoadingComponent } from '@components/loading/loading';
import { CoreTabsOutletComponent } from '@components/tabs-outlet/tabs-outlet'; import { CoreTabsOutletComponent } from '@components/tabs-outlet/tabs-outlet';
import { CoreTabsComponent } from '@components/tabs/tabs';
import { CoreSettingsHelper } from '@features/settings/services/settings-helper'; import { CoreSettingsHelper } from '@features/settings/services/settings-helper';
import { ScrollDetail } from '@ionic/core'; import { ScrollDetail } from '@ionic/core';
import { CoreUtils } from '@services/utils/utils'; import { CoreUtils } from '@services/utils/utils';
@ -230,8 +231,12 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest
* Search the page content, initialize it, and wait until it's ready for the transition to trigger on scroll. * Search the page content, initialize it, and wait until it's ready for the transition to trigger on scroll.
*/ */
protected async initializeContent(): Promise<void> { protected async initializeContent(): Promise<void> {
if (!this.page) {
return;
}
// Initialize from tabs. // Initialize from tabs.
const tabs = CoreComponentsRegistry.resolve(this.page?.querySelector('core-tabs-outlet'), CoreTabsOutletComponent); const tabs = CoreComponentsRegistry.resolve(this.page.querySelector('core-tabs-outlet'), CoreTabsOutletComponent);
if (tabs) { if (tabs) {
const outlet = tabs.getOutlet(); const outlet = tabs.getOutlet();
@ -249,7 +254,7 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest
} }
// Initialize from page content. // Initialize from page content.
const content = this.page?.querySelector('ion-content:not(.disable-scroll-y)'); const content = this.page.querySelector('ion-content:not(.disable-scroll-y)');
if (!content) { if (!content) {
throw new Error('[collapsible-header] Couldn\'t get content'); throw new Error('[collapsible-header] Couldn\'t get content');
@ -357,7 +362,19 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest
return; return;
} }
// Wait loadings to finish.
await CoreComponentsRegistry.waitComponentsReady(this.page, 'core-loading', CoreLoadingComponent); await CoreComponentsRegistry.waitComponentsReady(this.page, 'core-loading', CoreLoadingComponent);
// Wait tabs to be ready.
await CoreComponentsRegistry.waitComponentsReady(this.page, 'core-tabs', CoreTabsComponent);
await CoreComponentsRegistry.waitComponentsReady(this.page, 'core-tabs-outlet', CoreTabsOutletComponent);
// Wait loadings to finish, inside tabs (if any).
await CoreComponentsRegistry.waitComponentsReady(
this.page,
'core-tab core-loading, ion-router-outlet core-loading',
CoreLoadingComponent,
);
} }
/** /**

View File

@ -15,6 +15,7 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { AsyncComponent } from '@classes/async-component'; import { AsyncComponent } from '@classes/async-component';
import { CoreUtils } from '@services/utils/utils'; import { CoreUtils } from '@services/utils/utils';
import { CoreLogger } from './logger';
/** /**
* Registry to keep track of component instances. * Registry to keep track of component instances.
@ -22,6 +23,7 @@ import { CoreUtils } from '@services/utils/utils';
export class CoreComponentsRegistry { export class CoreComponentsRegistry {
private static instances: WeakMap<Element, unknown> = new WeakMap(); private static instances: WeakMap<Element, unknown> = new WeakMap();
protected static logger = CoreLogger.getInstance('CoreComponentsRegistry');
/** /**
* Register a component instance. * Register a component instance.
@ -78,6 +80,8 @@ export class CoreComponentsRegistry {
): Promise<void> { ): Promise<void> {
const instance = this.resolve(element, componentClass); const instance = this.resolve(element, componentClass);
if (!instance) { if (!instance) {
this.logger.error('No instance registered for element ' + componentClass, element);
return; return;
} }
@ -97,15 +101,21 @@ export class CoreComponentsRegistry {
selector: string, selector: string,
componentClass?: ComponentConstructor<T>, componentClass?: ComponentConstructor<T>,
): Promise<void> { ): Promise<void> {
let elements: Element[] = [];
if (element.matches(selector)) { if (element.matches(selector)) {
// Element to wait is myself. // Element to wait is myself.
await CoreComponentsRegistry.waitComponentReady<T>(element, componentClass); elements = [element];
} else { } else {
await Promise.all(Array elements = Array.from(element.querySelectorAll(selector));
.from(element.querySelectorAll(selector))
.map(element => CoreComponentsRegistry.waitComponentReady<T>(element, componentClass)));
} }
if (!elements.length) {
return;
}
await Promise.all(elements.map(element => CoreComponentsRegistry.waitComponentReady<T>(element, componentClass)));
// Wait for next tick to ensure components are completely rendered. // Wait for next tick to ensure components are completely rendered.
await CoreUtils.nextTick(); await CoreUtils.nextTick();
} }