mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-20 12:29:55 +08:00
refactor(external-router): remove the external router controller, move the reconciliation methods to the nav itself, and move the external activation status information to ion-app
This commit is contained in:
@ -28,7 +28,7 @@ import { OutletInjector } from './outlet-injector';
|
||||
import { RouteEventHandler } from './route-event-handler';
|
||||
|
||||
import { AngularComponentMounter, AngularEscapeHatch } from '..';
|
||||
import { ensureExternalRounterController } from '../util/util';
|
||||
import { getIonApp } from '../util/util';
|
||||
|
||||
let id = 0;
|
||||
|
||||
@ -139,9 +139,14 @@ export class RouterOutlet implements OnDestroy, OnInit, RouterDelegate {
|
||||
export function activateRoute(navElement: HTMLIonNavElement,
|
||||
component: Type<any>, data: any = {}, cfr: ComponentFactoryResolver, injector: Injector, isTopLevel: boolean): Promise<void> {
|
||||
|
||||
return ensureExternalRounterController().then((externalRouterController) => {
|
||||
return getIonApp().then((ionApp) => {
|
||||
if (!ionApp) {
|
||||
return Promise.reject(new Error(`<ion-app> element is required for angular router integration`));
|
||||
}
|
||||
const escapeHatch = getEscapeHatch(cfr, injector);
|
||||
return externalRouterController.reconcileNav(navElement, component, data, escapeHatch, isTopLevel);
|
||||
return navElement.componentOnReady().then(() => {
|
||||
return navElement.reconcileFromExternalRouter(component, data, escapeHatch, isTopLevel);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ import {
|
||||
Router
|
||||
} from '@angular/router';
|
||||
|
||||
import { ensureExternalRounterController } from '../util/util';
|
||||
import { getIonApp } from '../util/util';
|
||||
|
||||
@Injectable()
|
||||
export class RouteEventHandler {
|
||||
@ -14,16 +14,16 @@ export class RouteEventHandler {
|
||||
|
||||
router.events.subscribe((event: Event) => {
|
||||
if (event instanceof NavigationEnd) {
|
||||
ensureExternalRounterController().then((element) => {
|
||||
element.updateExternalNavOccuring(false);
|
||||
getIonApp().then((appElement) => {
|
||||
appElement.updateExternalNavOccuring(false);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
externalNavStart() {
|
||||
return ensureExternalRounterController().then((element) => {
|
||||
element.updateExternalNavOccuring(true);
|
||||
return getIonApp().then((appElement) => {
|
||||
appElement.updateExternalNavOccuring(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -26,12 +26,7 @@ export function isString(something: any) {
|
||||
return typeof something === 'string' ? true : false;
|
||||
}
|
||||
|
||||
export function ensureExternalRounterController(): Promise<HTMLIonExternalRouterControllerElement> {
|
||||
const element = document.querySelector('ion-external-router-controller');
|
||||
if (element) {
|
||||
return (element as any).componentOnReady();
|
||||
}
|
||||
const toCreate = document.createElement('ion-external-router-controller');
|
||||
document.body.appendChild(toCreate);
|
||||
return (toCreate as any).componentOnReady();
|
||||
export function getIonApp(): Promise<HTMLIonAppElement> {
|
||||
const element = ensureElementInBody('ion-app') as HTMLIonAppElement;
|
||||
return element.componentOnReady();
|
||||
}
|
30
packages/core/src/components.d.ts
vendored
30
packages/core/src/components.d.ts
vendored
@ -875,36 +875,6 @@ declare global {
|
||||
}
|
||||
|
||||
|
||||
import {
|
||||
ExternalRouterController as IonExternalRouterController
|
||||
} from './components/external-router-controller/external-router-controller';
|
||||
|
||||
declare global {
|
||||
interface HTMLIonExternalRouterControllerElement extends IonExternalRouterController, HTMLStencilElement {
|
||||
}
|
||||
var HTMLIonExternalRouterControllerElement: {
|
||||
prototype: HTMLIonExternalRouterControllerElement;
|
||||
new (): HTMLIonExternalRouterControllerElement;
|
||||
};
|
||||
interface HTMLElementTagNameMap {
|
||||
"ion-external-router-controller": HTMLIonExternalRouterControllerElement;
|
||||
}
|
||||
interface ElementTagNameMap {
|
||||
"ion-external-router-controller": HTMLIonExternalRouterControllerElement;
|
||||
}
|
||||
namespace JSX {
|
||||
interface IntrinsicElements {
|
||||
"ion-external-router-controller": JSXElements.IonExternalRouterControllerAttributes;
|
||||
}
|
||||
}
|
||||
namespace JSXElements {
|
||||
export interface IonExternalRouterControllerAttributes extends HTMLAttributes {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
import {
|
||||
FabButton as IonFabButton
|
||||
} from './components/fab-button/fab-button';
|
||||
|
@ -30,6 +30,49 @@ export class App {
|
||||
|
||||
@Prop({ context: 'config' }) config: Config;
|
||||
|
||||
externalNavPromise: void | Promise<any> = null;
|
||||
externalNavOccuring = false;
|
||||
|
||||
/**
|
||||
* Returns the promise set by an external navigation system
|
||||
* This API is not meant for public usage and could
|
||||
* change at any time
|
||||
*/
|
||||
@Method()
|
||||
getExternalNavPromise(): void | Promise<any> {
|
||||
return this.externalNavPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the Promise set by an external navigation system
|
||||
* This API is not meant for public usage and could
|
||||
* change at any time
|
||||
*/
|
||||
@Method()
|
||||
setExternalNavPromise(value: null | Promise<any>): void {
|
||||
this.externalNavPromise = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an external navigation event is occuring
|
||||
* This API is not meant for public usage and could
|
||||
* change at any time
|
||||
*/
|
||||
@Method()
|
||||
getExternalNavOccuring(): boolean {
|
||||
return this.externalNavOccuring;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates whether an external navigation event is occuring
|
||||
* This API is not meant for public usage and could
|
||||
* change at any time
|
||||
*/
|
||||
@Method()
|
||||
updateExternalNavOccuring(status: boolean) {
|
||||
this.externalNavOccuring = status;
|
||||
}
|
||||
|
||||
componentWillLoad() {
|
||||
this.modeCode = this.config.get('mode');
|
||||
this.useRouter = this.config.getBoolean('useRouter', false);
|
||||
|
@ -12,6 +12,20 @@
|
||||
|
||||
## Methods
|
||||
|
||||
#### getExternalNavOccuring()
|
||||
|
||||
Returns whether an external navigation event is occuring
|
||||
This API is not meant for public usage and could
|
||||
change at any time
|
||||
|
||||
|
||||
#### getExternalNavPromise()
|
||||
|
||||
Returns the promise set by an external navigation system
|
||||
This API is not meant for public usage and could
|
||||
change at any time
|
||||
|
||||
|
||||
#### getNavByIdOrName()
|
||||
|
||||
|
||||
@ -33,9 +47,23 @@ Returns whether the application is enabled or not
|
||||
Boolean if the app is actively scrolling or not.
|
||||
|
||||
|
||||
#### setExternalNavPromise()
|
||||
|
||||
Updates the Promise set by an external navigation system
|
||||
This API is not meant for public usage and could
|
||||
change at any time
|
||||
|
||||
|
||||
#### setScrolling()
|
||||
|
||||
|
||||
#### updateExternalNavOccuring()
|
||||
|
||||
Updates whether an external navigation event is occuring
|
||||
This API is not meant for public usage and could
|
||||
change at any time
|
||||
|
||||
|
||||
|
||||
----------------------------------------------
|
||||
|
||||
|
@ -1,119 +0,0 @@
|
||||
import { Component, Method } from '@stencil/core';
|
||||
import { EscapeHatch, NavResult } from '../../index';
|
||||
|
||||
@Component({
|
||||
tag: 'ion-external-router-controller'
|
||||
})
|
||||
export class ExternalRouterController {
|
||||
|
||||
externalNavPromise: void | Promise<any> = null;
|
||||
externalNavOccuring = false;
|
||||
|
||||
@Method()
|
||||
getExternalNavPromise(): void | Promise<any> {
|
||||
return this.externalNavPromise;
|
||||
}
|
||||
|
||||
@Method()
|
||||
clearExternalNavPromise(): void {
|
||||
this.externalNavPromise = null;
|
||||
}
|
||||
|
||||
@Method()
|
||||
getExternalNavOccuring(): boolean {
|
||||
return this.externalNavOccuring;
|
||||
}
|
||||
|
||||
@Method()
|
||||
updateExternalNavOccuring(status: boolean) {
|
||||
this.externalNavOccuring = status;
|
||||
}
|
||||
|
||||
@Method()
|
||||
reconcileNav(nav: HTMLIonNavElement, component: any, data: any = {}, escapeHatch: EscapeHatch, isTopLevel: boolean) {
|
||||
return nav.componentOnReady().then(() => {
|
||||
// check if the nav has an `<ion-tab>` as a parent
|
||||
if (isParentTab(nav)) {
|
||||
// check if the tab is selected
|
||||
return updateTab(this, nav, component, data, escapeHatch, isTopLevel);
|
||||
} else {
|
||||
return updateNav(nav, component, data, escapeHatch, isTopLevel);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function isParentTab(navElement: HTMLIonNavElement) {
|
||||
return navElement.parentElement.tagName.toLowerCase() === 'ion-tab';
|
||||
}
|
||||
|
||||
function updateTab(externalRouterController: ExternalRouterController, navElement: HTMLIonNavElement, component: any, data: any, escapeHatch: EscapeHatch, isTopLevel: boolean) {
|
||||
|
||||
const tab = navElement.parentElement as HTMLIonTabElement;
|
||||
|
||||
// yeah yeah, I know this is kind of ugly but oh well, I know the internal structure of <ion-tabs>
|
||||
const tabs = tab.parentElement.parentElement as HTMLIonTabsElement;
|
||||
|
||||
return isTabSelected(tabs, tab).then((isSelected: boolean) => {
|
||||
if (!isSelected) {
|
||||
const promise = updateNav(navElement, component, data, escapeHatch, isTopLevel);
|
||||
externalRouterController.externalNavPromise = promise;
|
||||
// okay, the tab is not selected, so we need to do a "switch" transition
|
||||
// basically, we should update the nav, and then swap the tabs
|
||||
return promise.then(() => {
|
||||
return tabs.select(tab);
|
||||
});
|
||||
}
|
||||
|
||||
// okay cool, the tab is already selected, so we want to see a transition
|
||||
return updateNav(navElement, component, data, escapeHatch, isTopLevel);
|
||||
});
|
||||
}
|
||||
|
||||
function isTabSelected(tabsElement: HTMLIonTabsElement, tabElement: HTMLIonTabElement ): Promise<boolean> {
|
||||
const promises: Promise<any>[] = [];
|
||||
promises.push(tabsElement.componentOnReady());
|
||||
promises.push(tabElement.componentOnReady());
|
||||
return Promise.all(promises).then(() => {
|
||||
return tabsElement.getSelected() === tabElement;
|
||||
});
|
||||
}
|
||||
|
||||
function updateNav(navElement: HTMLIonNavElement,
|
||||
component: any, data: any, escapeHatch: EscapeHatch, isTopLevel: boolean): Promise<NavResult> {
|
||||
|
||||
|
||||
// check if the component is the top view
|
||||
const activeViews = navElement.getViews();
|
||||
if (activeViews.length === 0) {
|
||||
// there isn't a view in the stack, so push one
|
||||
return navElement.setRoot(component, data, {}, escapeHatch);
|
||||
}
|
||||
|
||||
const currentView = activeViews[activeViews.length - 1];
|
||||
if (currentView.component === component) {
|
||||
// the top view is already the component being activated, so there is no change needed
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
// check if the component is the previous view, if so, pop back to it
|
||||
if (activeViews.length > 1) {
|
||||
// there's at least two views in the stack
|
||||
const previousView = activeViews[activeViews.length - 2];
|
||||
if (previousView.component === component) {
|
||||
// cool, we match the previous view, so pop it
|
||||
return navElement.pop(null, escapeHatch);
|
||||
}
|
||||
}
|
||||
|
||||
// check if the component is already in the stack of views, in which case we pop back to it
|
||||
for (const view of activeViews) {
|
||||
if (view.component === component) {
|
||||
// cool, we found the match, pop back to that bad boy
|
||||
return navElement.popTo(view, null, escapeHatch);
|
||||
}
|
||||
}
|
||||
|
||||
// it's the top level nav, and it's not one of those other behaviors, so do a push so the user gets a chill animation
|
||||
return navElement.push(component, data, { animate: isTopLevel }, escapeHatch);
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
# ion-external-router-controller
|
||||
|
||||
|
||||
|
||||
<!-- Auto Generated Below -->
|
||||
|
||||
|
||||
## Methods
|
||||
|
||||
#### clearExternalNavPromise()
|
||||
|
||||
|
||||
#### getExternalNavOccuring()
|
||||
|
||||
|
||||
#### getExternalNavPromise()
|
||||
|
||||
|
||||
#### reconcileNav()
|
||||
|
||||
|
||||
#### updateExternalNavOccuring()
|
||||
|
||||
|
||||
|
||||
----------------------------------------------
|
||||
|
||||
*Built with [StencilJS](https://stenciljs.com/)*
|
@ -51,6 +51,7 @@ import {
|
||||
focusOutActiveElement,
|
||||
isDef,
|
||||
isNumber,
|
||||
isParentTab,
|
||||
normalizeUrl,
|
||||
} from '../../utils/helpers';
|
||||
|
||||
@ -258,6 +259,11 @@ export class Nav implements PublicNav, NavOutlet {
|
||||
return allTransitionsCompleteImpl(this);
|
||||
}
|
||||
|
||||
@Method()
|
||||
reconcileFromExternalRouter(component: any, data: any = {}, escapeHatch: EscapeHatch, isTopLevel: boolean) {
|
||||
return reconcileFromExternalRouterImpl(this, component, data, escapeHatch, isTopLevel);
|
||||
}
|
||||
|
||||
canSwipeBack(): boolean {
|
||||
return (this.swipeBackEnabled &&
|
||||
// this.childNavs.length === 0 &&
|
||||
@ -1300,6 +1306,92 @@ export function getDefaultEscapeHatch(): EscapeHatch {
|
||||
};
|
||||
}
|
||||
|
||||
export function reconcileFromExternalRouterImpl(nav: Nav, component: any, data: any = {}, escapeHatch: EscapeHatch, isTopLevel: boolean) {
|
||||
// check if the nav has an `<ion-tab>` as a parent
|
||||
if (isParentTab(nav.element as any)) {
|
||||
// check if the tab is selected
|
||||
return updateTab(nav, component, data, escapeHatch, isTopLevel);
|
||||
} else {
|
||||
return updateNav(nav, component, data, escapeHatch, isTopLevel);
|
||||
}
|
||||
}
|
||||
|
||||
export function updateTab(nav: Nav, component: any, data: any, escapeHatch: EscapeHatch, isTopLevel: boolean) {
|
||||
|
||||
const tab = nav.element.parentElement as HTMLIonTabElement;
|
||||
|
||||
// yeah yeah, I know this is kind of ugly but oh well, I know the internal structure of <ion-tabs>
|
||||
const tabs = tab.parentElement.parentElement as HTMLIonTabsElement;
|
||||
|
||||
return isTabSelected(tabs, tab).then((isSelected: boolean) => {
|
||||
if (!isSelected) {
|
||||
const promise = updateNav(nav, component, data, escapeHatch, isTopLevel);
|
||||
const app = document.querySelector('ion-app');
|
||||
return app.componentOnReady().then(() => {
|
||||
app.setExternalNavPromise(promise);
|
||||
}).then(() => {
|
||||
// okay, the tab is not selected, so we need to do a "switch" transition
|
||||
// basically, we should update the nav, and then swap the tabs
|
||||
return promise.then(() => {
|
||||
return tabs.select(tab);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// okay cool, the tab is already selected, so we want to see a transition
|
||||
return updateNav(nav, component, data, escapeHatch, isTopLevel);
|
||||
});
|
||||
}
|
||||
|
||||
export function isTabSelected(tabsElement: HTMLIonTabsElement, tabElement: HTMLIonTabElement ): Promise<boolean> {
|
||||
const promises: Promise<any>[] = [];
|
||||
promises.push(tabsElement.componentOnReady());
|
||||
promises.push(tabElement.componentOnReady());
|
||||
return Promise.all(promises).then(() => {
|
||||
return tabsElement.getSelected() === tabElement;
|
||||
});
|
||||
}
|
||||
|
||||
export function updateNav(nav: Nav,
|
||||
component: any, data: any, escapeHatch: EscapeHatch, isTopLevel: boolean): Promise<NavResult> {
|
||||
|
||||
|
||||
// check if the component is the top view
|
||||
const activeViews = nav.getViews();
|
||||
if (activeViews.length === 0) {
|
||||
// there isn't a view in the stack, so push one
|
||||
return nav.setRoot(component, data, {}, escapeHatch);
|
||||
}
|
||||
|
||||
const currentView = activeViews[activeViews.length - 1];
|
||||
if (currentView.component === component) {
|
||||
// the top view is already the component being activated, so there is no change needed
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
// check if the component is the previous view, if so, pop back to it
|
||||
if (activeViews.length > 1) {
|
||||
// there's at least two views in the stack
|
||||
const previousView = activeViews[activeViews.length - 2];
|
||||
if (previousView.component === component) {
|
||||
// cool, we match the previous view, so pop it
|
||||
return nav.pop(null, escapeHatch);
|
||||
}
|
||||
}
|
||||
|
||||
// check if the component is already in the stack of views, in which case we pop back to it
|
||||
for (const view of activeViews) {
|
||||
if (view.component === component) {
|
||||
// cool, we found the match, pop back to that bad boy
|
||||
return nav.popTo(view, null, escapeHatch);
|
||||
}
|
||||
}
|
||||
|
||||
// it's the top level nav, and it's not one of those other behaviors, so do a push so the user gets a chill animation
|
||||
return nav.push(component, data, { animate: isTopLevel }, escapeHatch);
|
||||
}
|
||||
|
||||
|
||||
export interface IsRedirectRequired {
|
||||
required: boolean;
|
||||
url?: string;
|
||||
|
@ -143,6 +143,9 @@ boolean
|
||||
#### push()
|
||||
|
||||
|
||||
#### reconcileFromExternalRouter()
|
||||
|
||||
|
||||
#### removeIndex()
|
||||
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { Component, Element, Event, EventEmitter, Method, Prop, State, Watch } from '@stencil/core';
|
||||
import { ensureExternalRounterController, getNavAsChildIfExists } from '../../utils/helpers';
|
||||
|
||||
import { FrameworkDelegate } from '../..';
|
||||
import { getIonApp, getNavAsChildIfExists } from '../../utils/helpers';
|
||||
|
||||
|
||||
@Component({
|
||||
tag: 'ion-tab',
|
||||
@ -81,7 +83,7 @@ export class Tab {
|
||||
@Method()
|
||||
prepareActive(): Promise<any> {
|
||||
if (this.loaded) {
|
||||
return this.configChildgNav();
|
||||
return this.configChildNav();
|
||||
}
|
||||
this.loaded = true;
|
||||
|
||||
@ -94,7 +96,7 @@ export class Tab {
|
||||
} else {
|
||||
promise = Promise.resolve();
|
||||
}
|
||||
return promise.then(() => this.configChildgNav());
|
||||
return promise.then(() => this.configChildNav());
|
||||
}
|
||||
|
||||
@Method()
|
||||
@ -108,15 +110,15 @@ export class Tab {
|
||||
return null;
|
||||
}
|
||||
|
||||
private configChildgNav(): Promise<any|void> {
|
||||
private configChildNav(): Promise<any|void> {
|
||||
const nav = getNavAsChildIfExists(this.el);
|
||||
if (nav) {
|
||||
// the tab's nav has been initialized externally
|
||||
return ensureExternalRounterController().then<void|any>((externalRouterController) => {
|
||||
const externalNavPromise = externalRouterController.getExternalNavPromise();
|
||||
return getIonApp().then((ionApp) => {
|
||||
const externalNavPromise = ionApp ? ionApp.getExternalNavPromise() : null;
|
||||
if (externalNavPromise) {
|
||||
return externalNavPromise.then(() => {
|
||||
externalRouterController.clearExternalNavPromise();
|
||||
return (externalNavPromise as any).then(() => {
|
||||
ionApp.setExternalNavPromise(null);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Component, Element, Event, EventEmitter, Listen, Method, Prop, State } from '@stencil/core';
|
||||
import { Config, NavEventDetail, NavOutlet } from '../../index';
|
||||
|
||||
import { asyncRaf, ensureExternalRounterController } from '../../utils/helpers';
|
||||
import { asyncRaf, getIonApp } from '../../utils/helpers';
|
||||
|
||||
|
||||
@Component({
|
||||
@ -75,10 +75,13 @@ export class Tabs implements NavOutlet {
|
||||
|
||||
const promises: Promise<any>[] = [];
|
||||
promises.push(this.initTabs());
|
||||
promises.push(ensureExternalRounterController());
|
||||
return Promise.all(promises).then(([_, externalRouterController]) => {
|
||||
return (externalRouterController as HTMLIonExternalRouterControllerElement).getExternalNavOccuring();
|
||||
}).then((externalNavOccuring) => {
|
||||
promises.push(getIonApp());
|
||||
return Promise.all(promises).then(([_, ionApp]) => {
|
||||
if (ionApp) {
|
||||
return (ionApp as HTMLIonAppElement).getExternalNavOccuring();
|
||||
}
|
||||
return false;
|
||||
}).then((externalNavOccuring: boolean) => {
|
||||
if (!externalNavOccuring) {
|
||||
return this.initSelect();
|
||||
}
|
||||
@ -116,11 +119,8 @@ export class Tabs implements NavOutlet {
|
||||
tab.selected = false;
|
||||
}
|
||||
}
|
||||
selectedTab.selected = true;
|
||||
|
||||
const leavingTab = this.selectedTab;
|
||||
this.selectedTab = selectedTab;
|
||||
|
||||
|
||||
return selectedTab.prepareActive()
|
||||
.then(() => selectedTab.active = true)
|
||||
@ -129,6 +129,8 @@ export class Tabs implements NavOutlet {
|
||||
if (leavingTab) {
|
||||
leavingTab.active = false;
|
||||
}
|
||||
selectedTab.selected = true;
|
||||
this.selectedTab = selectedTab;
|
||||
this.ionChange.emit(selectedTab);
|
||||
this.ionNavChanged.emit({isPop: false});
|
||||
});
|
||||
@ -216,6 +218,7 @@ export class Tabs implements NavOutlet {
|
||||
this.selectedTab = selectedTab;
|
||||
if (selectedTab) {
|
||||
selectedTab.selected = true;
|
||||
selectedTab.active = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -321,12 +321,14 @@ export function normalizeUrl(url: string) {
|
||||
return url;
|
||||
}
|
||||
|
||||
export function ensureExternalRounterController(): Promise<HTMLIonExternalRouterControllerElement> {
|
||||
const element = document.querySelector('ion-external-router-controller');
|
||||
if (element) {
|
||||
return (element as any).componentOnReady();
|
||||
export function isParentTab(element: HTMLElement) {
|
||||
return element.parentElement.tagName.toLowerCase() === 'ion-tab';
|
||||
}
|
||||
const toCreate = document.createElement('ion-external-router-controller');
|
||||
document.body.appendChild(toCreate);
|
||||
return (toCreate as any).componentOnReady();
|
||||
|
||||
export function getIonApp(): Promise<HTMLIonAppElement> {
|
||||
const appElement = document.querySelector('ion-app');
|
||||
if (!appElement) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
return appElement.componentOnReady();
|
||||
}
|
||||
|
Reference in New Issue
Block a user