MOBILE-4479 core: Make wait directives functions recursive

To be able to wait for elements inside the loading elements
main
Dani Palou 2023-12-04 09:00:25 +01:00
parent 7c643dc4a4
commit 1c967b7813
2 changed files with 82 additions and 20 deletions

View File

@ -428,19 +428,15 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest
return; return;
} }
// Wait loadings to finish. // Make sure elements have been added to the DOM.
await CoreDirectivesRegistry.waitDirectivesReady(this.page, 'core-loading', CoreLoadingComponent); await CoreUtils.nextTick();
// Wait tabs to be ready. // Wait all loadings and tabs to finish loading.
await CoreDirectivesRegistry.waitDirectivesReady(this.page, 'core-tabs', CoreTabsComponent); await CoreDirectivesRegistry.waitMultipleDirectivesReady(this.page, [
await CoreDirectivesRegistry.waitDirectivesReady(this.page, 'core-tabs-outlet', CoreTabsOutletComponent); { selector: 'core-loading', class: CoreLoadingComponent },
{ selector: 'core-tabs', class: CoreTabsComponent },
// Wait loadings to finish, inside tabs (if any). { selector: 'core-tabs-outlet', class: CoreTabsOutletComponent },
await CoreDirectivesRegistry.waitDirectivesReady( ]);
this.page,
'core-tab core-loading, ion-router-outlet core-loading',
CoreLoadingComponent,
);
} }
/** /**

View File

@ -114,15 +114,16 @@ export class CoreDirectivesRegistry {
selector?: string, selector?: string,
directiveClass?: DirectiveConstructor<T>, directiveClass?: DirectiveConstructor<T>,
): Promise<void> { ): Promise<void> {
let elements: Element[] = []; const findElements = (): Element[] => {
if (!selector || element.matches(selector)) {
if (!selector || element.matches(selector)) { // Element to wait is myself.
// Element to wait is myself. return [element];
elements = [element]; } else {
} else { return Array.from(element.querySelectorAll(selector));
elements = Array.from(element.querySelectorAll(selector)); }
} };
const elements = findElements();
if (!elements.length) { if (!elements.length) {
return; return;
} }
@ -135,6 +136,63 @@ export class CoreDirectivesRegistry {
// Wait for next tick to ensure directives are completely rendered. // Wait for next tick to ensure directives are completely rendered.
await CoreUtils.nextTick(); await CoreUtils.nextTick();
// Check if there are new elements now that the found elements are ready (there could be nested elements).
if (elements.length !== findElements().length) {
await this.waitDirectivesReady(element, selector, directiveClass);
}
}
/**
* Get all directive instances (with multiple types) and wait for them to be ready.
*
* @param element Root element.
* @param directives Directives to wait.
* @returns Promise resolved when done.
*/
static async waitMultipleDirectivesReady(
element: Element,
directives: DirectiveData<AsyncDirective>[],
): Promise<void> {
const findElements = (selector?: string): Element[] => {
if (!selector || element.matches(selector)) {
// Element to wait is myself.
return [element];
} else {
return Array.from(element.querySelectorAll(selector));
}
};
let allElements: Element[] = [];
await Promise.all(directives.map(async directive => {
const elements = findElements(directive.selector);
if (!elements.length) {
return;
}
allElements = allElements.concat(elements);
await Promise.all(elements.map(async element => {
const instances = this.resolveAll<AsyncDirective>(element, directive.class);
await Promise.all(instances.map(instance => instance.ready()));
}));
}));
// Wait for next tick to ensure directives are completely rendered.
await CoreUtils.nextTick();
// Check if there are new elements now that the found elements are ready (there could be nested elements).
const elementsAfterReady = directives.reduce((elements, directive) => {
elements = elements.concat(findElements(directive.selector));
return elements;
}, <Element[]> []);
if (allElements.length !== elementsAfterReady.length) {
await this.waitMultipleDirectivesReady(element, directives);
}
} }
} }
@ -144,3 +202,11 @@ export class CoreDirectivesRegistry {
*/ */
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
export type DirectiveConstructor<T = Directive> = { new(...args: any[]): T }; export type DirectiveConstructor<T = Directive> = { new(...args: any[]): T };
/**
* Data to identify a directive when waiting for ready.
*/
type DirectiveData<T extends AsyncDirective> = {
selector?: string; // If defined, CSS Selector to wait for.
class?: DirectiveConstructor<T>; // Directive class.
};