refactor(app): add cordova-platform to ion-app, add back-button support, get api working

This commit is contained in:
Dan Bucholtz
2018-02-05 22:20:48 -06:00
parent 2f8a027e2f
commit 2bde55421d
29 changed files with 423 additions and 94 deletions

View File

@ -725,6 +725,36 @@ declare global {
}
import {
CordovaPlatform as IonCordovaPlatform
} from './components/cordova-platform/cordova-platform';
declare global {
interface HTMLIonCordovaPlatformElement extends IonCordovaPlatform, HTMLElement {
}
var HTMLIonCordovaPlatformElement: {
prototype: HTMLIonCordovaPlatformElement;
new (): HTMLIonCordovaPlatformElement;
};
interface HTMLElementTagNameMap {
"ion-cordova-platform": HTMLIonCordovaPlatformElement;
}
interface ElementTagNameMap {
"ion-cordova-platform": HTMLIonCordovaPlatformElement;
}
namespace JSX {
interface IntrinsicElements {
"ion-cordova-platform": JSXElements.IonCordovaPlatformAttributes;
}
}
namespace JSXElements {
export interface IonCordovaPlatformAttributes extends HTMLAttributes {
}
}
}
import {
Datetime as IonDatetime
} from './components/datetime/datetime';

View File

@ -1,5 +1,5 @@
import { Component, Listen, Method } from '@stencil/core';
import { ActionSheetEvent, ActionSheetOptions } from '../../index';
import { ActionSheetEvent, ActionSheetOptions, OverlayController } from '../../index';
let ids = 0;
const actionSheets = new Map<number, HTMLIonActionSheetElement>();
@ -7,7 +7,7 @@ const actionSheets = new Map<number, HTMLIonActionSheetElement>();
@Component({
tag: 'ion-action-sheet-controller'
})
export class ActionSheetController {
export class ActionSheetController implements OverlayController {
@Method()
create(opts?: ActionSheetOptions): Promise<HTMLIonActionSheetElement> {
@ -38,6 +38,10 @@ export class ActionSheetController {
return actionSheet.dismiss(data, role);
}
@Method()
getTop() {
return actionSheets.get(getHighestId());
}
@Listen('body:ionActionSheetWillPresent')
protected actionSheetWillPresent(ev: ActionSheetEvent) {

View File

@ -36,6 +36,9 @@
#### dismiss()
#### getTop()
----------------------------------------------

View File

@ -1,5 +1,5 @@
import { Component, Listen, Method } from '@stencil/core';
import { AlertEvent, AlertOptions } from '../../index';
import { AlertEvent, AlertOptions, OverlayController } from '../../index';
let ids = 0;
const alerts = new Map<number, HTMLIonAlertElement>();
@ -7,7 +7,7 @@ const alerts = new Map<number, HTMLIonAlertElement>();
@Component({
tag: 'ion-alert-controller'
})
export class AlertController {
export class AlertController implements OverlayController {
@Method()
create(opts?: AlertOptions): Promise<HTMLIonAlertElement> {
@ -38,6 +38,10 @@ export class AlertController {
return alert.dismiss(data, role);
}
@Method()
getTop() {
return alerts.get(getHighestId());
}
@Listen('body:ionAlertWillPresent')
protected alertWillPresent(ev: AlertEvent) {

View File

@ -50,6 +50,9 @@ building the form within a modal instead.
#### dismiss()
#### getTop()
----------------------------------------------

View File

@ -1,11 +1,12 @@
import { Component, Element, Listen, Method, Prop, State } from '@stencil/core';
import { Config, NavContainer, NavEvent } from '../../index';
import { isReady } from '../../utils/helpers';
import { Component, Element, Event, EventEmitter, Listen, Method, Prop, State } from '@stencil/core';
import { Config, NavEvent, OverlayController, PublicNav, PublicViewController } from '../../index';
import { getOrAppendElement } from '../../utils/helpers';
import { isCordova } from '../../global/platform-utils';
const rootNavs = new Map<number, HTMLIonNavElement>();
const ACTIVE_SCROLLING_TIME = 100;
@Component({
tag: 'ion-app',
styleUrls: {
@ -18,10 +19,10 @@ const ACTIVE_SCROLLING_TIME = 100;
})
export class App {
private didScroll = false;
private scrollTime = 0;
@Element() element: HTMLElement;
@Event() exitApp: EventEmitter<ExitAppEventDetail>;
@State() modeCode: string;
@State() hoverCSS = false;
@ -44,16 +45,17 @@ export class App {
* Returns an array of top level Navs
*/
@Method()
getRootNavs(): NavContainer[] {
/*const navs: NavContainer[] = [];
rootNavs.forEach((rootNav: NavContainer) => {
getRootNavs(): PublicNav[] {
const navs: PublicNav[] = [];
rootNavs.forEach((rootNav: PublicNav) => {
navs.push(rootNav);
});
return navs;
*/
return [];
}
/**
* Returns whether the application is enabled or not
*/
@Method()
isEnabled(): boolean {
return true;
@ -79,45 +81,66 @@ export class App {
@Method()
setScrolling() {
this.scrollTime = Date.now() + ACTIVE_SCROLLING_TIME;
this.didScroll = true;
}
@Method()
getActiveNavs(_rootNavId?: number): NavContainer[] {
/*const portal = portals.get(PORTAL_MODAL);
if (portal && portal.views && portal.views.length) {
return findTopNavs(portal);
}
*/
// TODO - figure out if a modal is open, don't use portal
/*if (!rootNavs.size) {
return [];
}
if (rootNavId) {
return findTopNavs(rootNavs.get(rootNavId));
}
if (rootNavs.size === 1) {
return findTopNavs(rootNavs.values().next().value);
}
// fallback to just using all root navs
let activeNavs: NavContainer[] = [];
rootNavs.forEach(nav => {
activeNavs = activeNavs.concat(findTopNavs(nav));
});
return activeNavs;
*/
return [];
getTopNavs(rootNavId = -1): PublicNav[] {
return getTopNavsImpl(rootNavId);
}
@Method() getNavByIdOrName(_nameOrId: number | string): any {
/*const navs = Array.from(rootNavs.values());
@Method()
getNavByIdOrName(nameOrId: number | string): PublicNav {
const navs = Array.from(rootNavs.values());
for (const navContainer of navs) {
const match = getNavByIdOrNameImpl(navContainer, nameOrId);
if (match) {
return match;
}
}
*/
return null;
}
@Method()
hardwareBackButtonPressed() {
// check if menu exists and is open
return checkIfMenuIsOpen().then((done: boolean) => {
if (!done) {
// we need to check if there is an action-sheet, alert, loading, picker, popover or toast open
// if so, just return and don't do anything
// Why? I have no idea, but that is the existing behavior in Ionic 3
return checkIfNotModalOverlayIsOpen();
}
return done;
}).then((done: boolean) => {
if (!done) {
// if there's a modal open, close that instead
return closeModalIfOpen();
}
return done;
}).then((done: boolean) => {
// okay cool, it's time to pop a nav if possible
if (!done) {
return popEligibleView();
}
return done;
}).then((done: boolean) => {
if (!done) {
// okay, we didn't find a nav that we can pop, so we should just exit the app
// since each platform exits differently, just delegate it to the platform to
// figure out how to exit
return this.exitApp.emit();
}
return Promise.resolve();
});
}
@Method()
appResume(): void {
return null;
}
@Method()
appPaused(): void {
return null;
}
@ -133,17 +156,37 @@ export class App {
render() {
const isDevice = true;
return [
isCordova() && <ion-cordova-platform/>,
isDevice && <ion-tap-click />,
isDevice && <ion-status-tap />,
// <ion-router-controller></ion-router-controller>
<slot></slot>
];
}
}
export function getTopNavsImpl(rootNavId = -1) {
if (!rootNavs.size) {
return [];
}
export function findTopNavs(nav: NavContainer): NavContainer[] {
let containers: NavContainer[] = [];
if (rootNavId !== -1) {
return findTopNavs(rootNavs.get(rootNavId));
}
if (rootNavs.size === 1) {
return findTopNavs(rootNavs.values().next().value);
}
// fallback to just using all root navs
let activeNavs: PublicNav[] = [];
rootNavs.forEach(nav => {
activeNavs = activeNavs.concat(findTopNavs(nav));
});
return activeNavs;
}
export function findTopNavs(nav: PublicNav): PublicNav[] {
let containers: PublicNav[] = [];
const childNavs = nav.getChildNavs();
if (!childNavs || !childNavs.length) {
containers.push(nav);
@ -156,11 +199,11 @@ export function findTopNavs(nav: NavContainer): NavContainer[] {
return containers;
}
export function getNavByIdOrNameImpl(nav: NavContainer, id: number | string): NavContainer {
if (nav.id === id || nav.name === id) {
export function getNavByIdOrNameImpl(nav: PublicNav, id: number | string): PublicNav {
if (nav.navId === id || nav.name === id) {
return nav;
}
for (const child of nav.getAllChildNavs()) {
for (const child of nav.getChildNavs()) {
const tmp = getNavByIdOrNameImpl(child, id);
if (tmp) {
return tmp;
@ -169,12 +212,95 @@ export function getNavByIdOrNameImpl(nav: NavContainer, id: number | string): Na
return null;
}
export function handleBackButtonClick(): Promise<any> {
// if there is a menu controller dom element, hydrate it, otherwise move on
// TODO ensure ion-menu-controller is the name
const menuControllerElement = document.querySelector('ion-menu-controller'); // TODO - use menu controller types
const promise = menuControllerElement ? isReady(menuControllerElement) : Promise.resolve();
return promise.then(() => {
// TODO check if the menu is open, close it if so
export function getHydratedController(tagName: string): Promise<HTMLElement> {
const controller = getOrAppendElement(tagName);
return (controller as any).componentOnReady();
}
export function checkIfMenuIsOpen(): Promise<boolean> {
return getHydratedController('ion-menu-controller').then((menuController: HTMLIonMenuControllerElement) => {
if (menuController.isOpen()) {
return menuController.close().then(() => {
return true;
});
}
return false;
});
}
export function checkIfNotModalOverlayIsOpen(): Promise<boolean> {
const promises: Promise<any>[] = [];
promises.push(checkIfOverlayExists('ion-action-sheet-controller'));
promises.push(checkIfOverlayExists('ion-alert-controller'));
promises.push(checkIfOverlayExists('ion-loading-controller'));
promises.push(checkIfOverlayExists('ion-picker-controller'));
promises.push(checkIfOverlayExists('ion-popover-controller'));
promises.push(checkIfOverlayExists('ion-toast-controller'));
return Promise.all(promises).then((results: boolean[]) => {
return results.every((value: boolean) => !!value);
});
}
export function checkIfOverlayExists(tagName: string): Promise<boolean> {
const overlayControllerElement = document.querySelector(tagName) as any as OverlayController;
if (!overlayControllerElement) {
return Promise.resolve(false);
}
return (overlayControllerElement as any).componentOnReady().then(() => {
return !!(overlayControllerElement.getTop());
});
}
export function closeModalIfOpen(): Promise<boolean> {
return getHydratedController('ion-modal-controller').then((modalController: HTMLIonModalControllerElement) => {
if (modalController.getTop()) {
return modalController.dismiss().then(() => {
return true;
});
}
return false;
});
}
export function popEligibleView(): Promise<boolean> {
let navToPop: PublicNav = null;
let mostRecentVC: PublicViewController = null;
rootNavs.forEach(nav => {
const topNavs = getTopNavsImpl(nav.navId);
const poppableNavs = topNavs.map(topNav => getPoppableNav(topNav)).filter(nav => !!nav).filter(nav => !!nav.last());
poppableNavs.forEach(poppable => {
const topViewController = poppable.last();
if (!mostRecentVC || topViewController.timestamp >= mostRecentVC.timestamp) {
mostRecentVC = topViewController;
navToPop = poppable;
}
});
});
if (navToPop) {
return navToPop.pop().then(() => {
return true;
});
}
return Promise.resolve(false);
}
export function getPoppableNav(nav: PublicNav): PublicNav {
if (!nav) {
return null;
}
// to be a poppable nav, a nav must a top view, plus a view that we can pop back to
if (nav.getViews.length > 1) {
return nav;
}
return getPoppableNav(nav.parent);
}
export interface ExitAppEvent extends CustomEvent {
target: HTMLIonAppElement;
detail: ExitAppEventDetail;
}
export interface ExitAppEventDetail {
}

View File

@ -5,9 +5,17 @@
<!-- Auto Generated Below -->
## Events
#### exitApp
## Methods
#### getActiveNavs()
#### appPaused()
#### appResume()
#### getNavByIdOrName()
@ -18,8 +26,16 @@
Returns an array of top level Navs
#### getTopNavs()
#### hardwareBackButtonPressed()
#### isEnabled()
Returns whether the application is enabled or not
#### isScrolling()

View File

@ -0,0 +1,84 @@
import { Component, Listen, Method} from '@stencil/core';
let isReady = false;
let readyQueue: Function[] = [];
@Component({
tag: 'ion-cordova-platform',
styleUrls: {
ios: 'cordova-platform.ios.scss',
md: 'cordova-platform.md.scss'
},
host: {
theme: 'cordova-platform'
}
})
export class CordovaPlatform {
@Method()
ready() {
return readyImpl();
}
@Listen('document:deviceready')
deviceReadyHandler() {
isReady = true;
processReadyQueue();
}
@Listen('body:exitApp')
exitCordovaApp() {
// this is lifted directly from Ionic 3
((window.navigator as any).app as any).exitApp();
}
componentDidLoad() {
readyImpl().then(() => {
// okay cool, we've received the ready event, we need to listen for the other events now
document.addEventListener('backbutton', handleBackButton);
document.addEventListener('resume', handleResume);
document.addEventListener('pause', handlePause);
});
}
}
export function handleBackButton() {
return getHydratedApp().then((app: HTMLIonAppElement) => {
return app.hardwareBackButtonPressed();
});
}
export function handleResume() {
return getHydratedApp().then((app: HTMLIonAppElement) => {
return app.appResume();
});
}
export function handlePause() {
return getHydratedApp().then((app: HTMLIonAppElement) => {
return app.appPaused();
});
}
export function getHydratedApp() {
const app = document.querySelector('ion-app');
return (app as any).componentOnReady();
}
export function readyImpl(): Promise<any> {
if (isReady) {
return Promise.resolve();
}
return new Promise((resolve) => {
readyQueue.push(resolve);
});
}
export function processReadyQueue() {
for (const resolve of readyQueue) {
resolve();
}
readyQueue = [];
}

View File

@ -0,0 +1,16 @@
# ion-cordova-platform
<!-- Auto Generated Below -->
## Methods
#### ready()
----------------------------------------------
*Built by [StencilJS](https://stenciljs.com/)*

View File

@ -1,5 +1,5 @@
import { Component, Listen, Method } from '@stencil/core';
import { LoadingEvent, LoadingOptions } from '../../index';
import { LoadingEvent, LoadingOptions, OverlayController } from '../../index';
let ids = 0;
const loadings = new Map<number, HTMLIonLoadingElement>();
@ -7,7 +7,7 @@ const loadings = new Map<number, HTMLIonLoadingElement>();
@Component({
tag: 'ion-loading-controller'
})
export class LoadingController {
export class LoadingController implements OverlayController {
@Method()
create(opts?: LoadingOptions): Promise<HTMLIonLoadingElement> {
@ -35,6 +35,10 @@ export class LoadingController {
return loading.dismiss(data, role);
}
@Method()
getTop() {
return loadings.get(getHighestId());
}
@Listen('body:ionLoadingWillPresent')
protected loadingWillPresent(ev: LoadingEvent) {

View File

@ -44,6 +44,9 @@ in the `usage` section below.
#### dismiss()
#### getTop()
----------------------------------------------

View File

@ -1,5 +1,5 @@
import { Component, Listen, Method } from '@stencil/core';
import { ModalEvent, ModalOptions } from '../../index';
import { ModalEvent, ModalOptions, OverlayController } from '../../index';
let ids = 0;
const modals = new Map<number, HTMLIonModalElement>();
@ -7,7 +7,7 @@ const modals = new Map<number, HTMLIonModalElement>();
@Component({
tag: 'ion-modal-controller'
})
export class ModalController {
export class ModalController implements OverlayController {
@Method()
create(opts?: ModalOptions): Promise<HTMLIonModalElement> {
@ -38,6 +38,10 @@ export class ModalController {
return modal.dismiss(data, role);
}
@Method()
getTop() {
return modals.get(getHighestId());
}
@Listen('body:ionModalWillPresent')
protected modalWillPresent(ev: ModalEvent) {
@ -50,7 +54,6 @@ export class ModalController {
modals.delete(ev.target.modalId);
}
@Listen('body:keyup.escape')
protected escapeKeyUp() {
removeLastModal();

View File

@ -13,6 +13,9 @@
#### dismiss()
#### getTop()
----------------------------------------------

View File

@ -4,11 +4,11 @@ import {
Animation,
AnimationOptions,
FrameworkDelegate,
FrameworkMountingData,
Nav,
NavOptions,
PublicViewController,
ViewController,
FrameworkMountingData
} from '../../index';
export interface PublicNav {
@ -27,20 +27,15 @@ export interface PublicNav {
getPrevious(view?: PublicViewController): PublicViewController;
canGoBack(): boolean;
canSwipeBack(): boolean;
getFirstView(): PublicViewController;
first(): PublicViewController;
last(): PublicViewController;
getChildNavs(): PublicNav[];
getViews(): PublicViewController[];
element?: HTMLElement;
}
export interface NavContainer {
id?: number;
navId?: number;
name?: string;
parent?: Nav;
getChildNavs?(): NavContainer[];
getAllChildNavs?(): NavContainer[];
getType?(): string;
getSecondaryIdentifier?(): string;
element?: HTMLElement;
parent?: PublicNav;
}
export interface NavOptions {
@ -110,4 +105,5 @@ export interface PublicViewController {
component?: any;
instance?: any;
element?: HTMLElement;
timestamp?: number;
}

View File

@ -159,7 +159,11 @@ export function canSwipeBack(_nav: Nav) {
}
export function getFirstView(nav: Nav): ViewController {
return nav.views && nav.views.length > 0 ? nav.views[0] : null;
return nav.views && nav.views.length ? nav.views[0] : null;
}
export function getLastView(nav: Nav): ViewController {
return nav.views && nav.views.length ? nav.views[nav.views.length - 1] : null;
}
export function getActiveChildNavs(nav: Nav): Nav[] {

View File

@ -6,7 +6,6 @@ import {
ComponentDataPair,
Config,
FrameworkDelegate,
NavContainer,
NavOptions,
NavResult,
NavState,
@ -29,6 +28,7 @@ import {
getActiveImpl,
getFirstView,
getHydratedTransition,
getLastView,
getNextNavId,
getNextTransitionId,
getParentTransitionId,
@ -62,7 +62,7 @@ const urlMap = new Map<string, TransitionInstruction>();
tag: 'ion-nav',
styleUrl: 'nav.scss'
})
export class Nav implements PublicNav, NavContainer {
export class Nav implements PublicNav {
@Element() element: HTMLElement;
@Event() navInit: EventEmitter<NavEventDetail>;
@ -97,7 +97,6 @@ export class Nav implements PublicNav, NavContainer {
componentWillLoad() {
this.routes = Array.from(this.element.querySelectorAll('ion-route'))
.map(child => child.getRoute());
//this.useRouter = false; // this.config.getBoolean('useRouter', false);
}
componentDidLoad() {
@ -193,10 +192,15 @@ export class Nav implements PublicNav, NavContainer {
}
@Method()
getFirstView(): PublicViewController {
first(): PublicViewController {
return getFirstView(this);
}
@Method()
last(): PublicViewController {
return getLastView(this);
}
@Method()
getState(): NavState {
assert(this.useRouter, 'routing is disabled');
@ -340,7 +344,7 @@ export function canGoBackImpl(nav: Nav) {
}
export function navInitializedImpl(potentialParent: Nav, event: NavEvent) {
if (potentialParent.element !== event.target) {
if ((potentialParent.element as any as HTMLIonNavElement) !== event.target) {
// set the parent on the child nav that dispatched the event
event.target.setParent(potentialParent);
if (!potentialParent.childNavs) {

View File

@ -78,15 +78,15 @@ boolean
#### clearTransitionInfoForUrl()
#### first()
#### getActive()
#### getChildNavs()
#### getFirstView()
#### getId()
@ -114,6 +114,9 @@ boolean
#### isTransitioning()
#### last()
#### pop()

View File

@ -14,6 +14,7 @@ export class ViewController implements PublicViewController {
overlay: boolean;
zIndex: number;
dismissProxy: any;
timestamp: number;
onDidDismiss: (data: any, role: string) => void;
@ -159,6 +160,7 @@ export function didLoadImpl(viewController: ViewController) {
}
export function initializeNewViewController(viewController: ViewController, data: any) {
viewController.timestamp = Date.now();
viewController.state = STATE_NEW;
viewController.data = data || {};
}

View File

@ -1,5 +1,5 @@
import { Component, Listen, Method } from '@stencil/core';
import { PickerEvent, PickerOptions } from '../../index';
import { PickerEvent, PickerOptions, OverlayController } from '../../index';
let ids = 0;
const pickers = new Map<number, HTMLIonPickerElement>();
@ -7,7 +7,7 @@ const pickers = new Map<number, HTMLIonPickerElement>();
@Component({
tag: 'ion-picker-controller'
})
export class PickerController {
export class PickerController implements OverlayController {
@Method()
create(opts?: PickerOptions): Promise<HTMLIonPickerElement> {
@ -38,6 +38,10 @@ export class PickerController {
return picker.dismiss(data, role);
}
@Method()
getTop() {
return pickers.get(getHighestId());
}
@Listen('body:ionPickerWillPresent')
protected pickerWillPresent(ev: PickerEvent) {

View File

@ -13,6 +13,9 @@
#### dismiss()
#### getTop()
----------------------------------------------

View File

@ -1,5 +1,5 @@
import { Component, Listen, Method } from '@stencil/core';
import { PopoverEvent, PopoverOptions } from '../../index';
import { OverlayController, PopoverEvent, PopoverOptions } from '../../index';
let ids = 0;
const popovers = new Map<number, HTMLIonPopoverElement>();
@ -7,7 +7,7 @@ const popovers = new Map<number, HTMLIonPopoverElement>();
@Component({
tag: 'ion-popover-controller'
})
export class PopoverController {
export class PopoverController implements OverlayController {
@Method()
create(opts?: PopoverOptions): Promise<HTMLIonPopoverElement> {
@ -38,6 +38,10 @@ export class PopoverController {
return popover.dismiss(data, role);
}
@Method()
getTop() {
return popovers.get(getHighestId());
}
@Listen('body:ionPopoverWillPresent')
protected popoverWillPresent(ev: PopoverEvent) {

View File

@ -32,6 +32,9 @@ view. See the [usage](#usage) section for an example of passing this event.
#### dismiss()
#### getTop()
----------------------------------------------

View File

@ -13,6 +13,9 @@
#### dismiss()
#### getTop()
----------------------------------------------

View File

@ -1,5 +1,5 @@
import { Component, Listen, Method } from '@stencil/core';
import { ToastEvent, ToastOptions } from '../../index';
import { OverlayController, ToastEvent, ToastOptions } from '../../index';
let ids = 0;
const toasts = new Map<number, HTMLIonToastElement>();
@ -7,7 +7,7 @@ const toasts = new Map<number, HTMLIonToastElement>();
@Component({
tag: 'ion-toast-controller'
})
export class ToastController {
export class ToastController implements OverlayController {
@Method()
create(opts?: ToastOptions): Promise<HTMLIonToastElement> {
@ -38,6 +38,10 @@ export class ToastController {
return toast.dismiss(data, role);
}
@Method()
getTop() {
return toasts.get(getHighestId());
}
@Listen('body:ionToastWillPresent')
protected toastWillPresent(ev: ToastEvent) {

View File

@ -145,10 +145,6 @@ export interface OverlayDismissEventDetail {
role?: string;
}
export interface OverlayController {
create(): HTMLElement;
}
export interface FrameworkDelegate {
attachViewToDom(elementOrContainerToMountTo: any, elementOrComponentToMount: any, propsOrDataObj?: any, classesToAdd?: string[], escapeHatch?: any): Promise<FrameworkMountingData>;
removeViewFromDom(elementOrContainerToUnmountFrom: any, elementOrComponentToUnmount: any, escapeHatch?: any): Promise<void>;
@ -160,7 +156,6 @@ export interface FrameworkMountingData {
data: any;
}
declare global {
namespace JSXElements {
@ -186,3 +181,7 @@ declare global {
}
}
}
export interface OverlayController {
getTop(): HTMLElement;
}

View File

@ -302,7 +302,7 @@ export function debounce(func: Function, wait = 0) {
export function getNavAsChildIfExists(element: HTMLElement): HTMLIonNavElement|null {
for (let i = 0; i < element.children.length; i++) {
if (element.children[i].tagName.toLowerCase() === 'ion-nav') {
return element.children[i] as HTMLIonNavElement;
return element.children[i] as any as HTMLIonNavElement;
}
}
return null;

View File

@ -42,6 +42,7 @@ exports.config = {
{ components: ['ion-toggle'] },
{ components: ['ion-toast', 'ion-toast-controller'] },
{ components: ['ion-tap-click', 'ion-status-tap'] },
{ components: ['ion-cordova-platform'] },
],
collections: [
'ionicons'