From cdeb4af6daee1f96b51a6b2fb1374fa25127d82c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Thu, 31 Mar 2022 11:02:52 +0200 Subject: [PATCH] MOBILE-3833 collapsible: Wait tabs to be ready --- src/core/classes/tabs.ts | 21 ++++++++++++++++--- .../components/tabs-outlet/tabs-outlet.ts | 9 +------- src/core/directives/collapsible-header.ts | 21 +++++++++++++++++-- src/core/singletons/components-registry.ts | 18 ++++++++++++---- 4 files changed, 52 insertions(+), 17 deletions(-) diff --git a/src/core/classes/tabs.ts b/src/core/classes/tabs.ts index bccb13a6a..61f006286 100644 --- a/src/core/classes/tabs.ts +++ b/src/core/classes/tabs.ts @@ -23,6 +23,7 @@ import { AfterViewInit, ViewChild, SimpleChange, + ElementRef, } from '@angular/core'; import { IonSlides } from '@ionic/angular'; import { BackButtonEvent } from '@ionic/core'; @@ -35,6 +36,9 @@ import { CoreEventObserver } from '@singletons/events'; import { CoreDom } from '@singletons/dom'; import { CoreUtils } from '@services/utils/utils'; 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. @@ -42,7 +46,7 @@ import { CoreError } from './errors/error'; @Component({ template: '', }) -export class CoreTabsBaseComponent implements OnInit, AfterViewInit, OnChanges, OnDestroy { +export class CoreTabsBaseComponent implements OnInit, AfterViewInit, OnChanges, OnDestroy, AsyncComponent { // Minimum tab's width. protected static readonly MIN_TAB_WIDTH = 107; @@ -85,13 +89,16 @@ export class CoreTabsBaseComponent implements OnInit, Aft // Swiper 6 documentation: https://swiper6.vercel.app/ protected isInTransition = false; // Wether Slides is in transition. protected subscriptions: Subscription[] = []; + protected onReadyPromise = new CorePromisedValue(); tabAction: CoreTabsRoleTab; - constructor() { + constructor(element: ElementRef) { this.backButtonFunction = this.backButtonClicked.bind(this); this.tabAction = new CoreTabsRoleTab(this); + + CoreComponentsRegistry.register(element.nativeElement, this); } /** @@ -525,6 +532,7 @@ export class CoreTabsBaseComponent implements OnInit, Aft if (suceeded !== false) { this.tabSelected(tabToSelect, index); } + this.onReadyPromise.resolve(); } /** @@ -554,7 +562,14 @@ export class CoreTabsBaseComponent implements OnInit, Aft } /** - * Component destroyed. + * @inheritdoc + */ + async ready(): Promise { + return await this.onReadyPromise; + } + + /** + * @inheritdoc */ ngOnDestroy(): void { this.isDestroyed = true; diff --git a/src/core/components/tabs-outlet/tabs-outlet.ts b/src/core/components/tabs-outlet/tabs-outlet.ts index 50bf4c761..279ddde43 100644 --- a/src/core/components/tabs-outlet/tabs-outlet.ts +++ b/src/core/components/tabs-outlet/tabs-outlet.ts @@ -20,7 +20,6 @@ import { OnDestroy, AfterViewInit, ViewChild, - ElementRef, SimpleChange, } from '@angular/core'; import { IonRouterOutlet, IonTabs, ViewDidEnter, ViewDidLeave } from '@ionic/angular'; @@ -69,12 +68,6 @@ export class CoreTabsOutletComponent extends CoreTabsBaseComponent; protected existsInNavigationStack = false; - constructor(element: ElementRef) { - super(); - - CoreComponentsRegistry.register(element.nativeElement, this); - } - /** * Init tab info. * @@ -223,7 +216,7 @@ export class CoreTabsOutletComponent extends CoreTabsBaseComponent { + if (!this.page) { + return; + } + // 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) { const outlet = tabs.getOutlet(); @@ -249,7 +254,7 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest } // 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) { throw new Error('[collapsible-header] Couldn\'t get content'); @@ -357,7 +362,19 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest return; } + // Wait loadings to finish. 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, + ); } /** diff --git a/src/core/singletons/components-registry.ts b/src/core/singletons/components-registry.ts index 32b08e3dd..0a5e18cea 100644 --- a/src/core/singletons/components-registry.ts +++ b/src/core/singletons/components-registry.ts @@ -15,6 +15,7 @@ import { Component } from '@angular/core'; import { AsyncComponent } from '@classes/async-component'; import { CoreUtils } from '@services/utils/utils'; +import { CoreLogger } from './logger'; /** * Registry to keep track of component instances. @@ -22,6 +23,7 @@ import { CoreUtils } from '@services/utils/utils'; export class CoreComponentsRegistry { private static instances: WeakMap = new WeakMap(); + protected static logger = CoreLogger.getInstance('CoreComponentsRegistry'); /** * Register a component instance. @@ -78,6 +80,8 @@ export class CoreComponentsRegistry { ): Promise { const instance = this.resolve(element, componentClass); if (!instance) { + this.logger.error('No instance registered for element ' + componentClass, element); + return; } @@ -97,15 +101,21 @@ export class CoreComponentsRegistry { selector: string, componentClass?: ComponentConstructor, ): Promise { + let elements: Element[] = []; + if (element.matches(selector)) { // Element to wait is myself. - await CoreComponentsRegistry.waitComponentReady(element, componentClass); + elements = [element]; } else { - await Promise.all(Array - .from(element.querySelectorAll(selector)) - .map(element => CoreComponentsRegistry.waitComponentReady(element, componentClass))); + elements = Array.from(element.querySelectorAll(selector)); } + if (!elements.length) { + return; + } + + await Promise.all(elements.map(element => CoreComponentsRegistry.waitComponentReady(element, componentClass))); + // Wait for next tick to ensure components are completely rendered. await CoreUtils.nextTick(); }