chore(angular): generate standalone component wrappers (#27970)

Issue number: N/A

---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the current behavior?
<!-- Please describe the current behavior that you are modifying. -->

Ionic Framework currently only generates the lazy/hydrated Angular
component wrappers for the web components.

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

- Generates Angular standalone component wrappers for the web
components, using the CE build.
  - Adds manual component wrapper for `ion-icon` with the CE build.
- Migrates the `ion-back-button` and `ion-nav` to be manual component
wrappers
- Continues to generate the lazy/hydrated Angular component wrappers.
- Refactors "NavDelegate" etc. language to `IonNav` to avoid exporting
as and simplify navigating the code.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!-- If this introduces a breaking change, please describe the impact
and migration path for existing applications below. -->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

Related output targets PR:
https://github.com/ionic-team/stencil-ds-output-targets/pull/367
This commit is contained in:
Sean Perkins
2023-08-18 11:02:16 -04:00
committed by GitHub
parent ca53682684
commit e57759f4b6
23 changed files with 3408 additions and 160 deletions

14
core/package-lock.json generated
View File

@@ -25,7 +25,7 @@
"@playwright/test": "^1.36.1",
"@rollup/plugin-node-resolve": "^8.4.0",
"@rollup/plugin-virtual": "^2.0.3",
"@stencil/angular-output-target": "^0.7.1",
"@stencil/angular-output-target": "^0.8.1",
"@stencil/react-output-target": "^0.5.3",
"@stencil/sass": "^3.0.4",
"@stencil/vue-output-target": "^0.8.6",
@@ -1625,9 +1625,9 @@
}
},
"node_modules/@stencil/angular-output-target": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/@stencil/angular-output-target/-/angular-output-target-0.7.1.tgz",
"integrity": "sha512-lxJbCAbyAQVAKGgEpNTjSF7GZZszbrJnNdNVgzuD1hLRFJyElA6kUSL0GQrZMbiPG5lC/cYdbQwpyWHX4xN8mw==",
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@stencil/angular-output-target/-/angular-output-target-0.8.1.tgz",
"integrity": "sha512-nO04lC7JxUT0bi2sTvBslz6aut1dVOvYRckf29a0l08XnRsDzIfT5e6f+c5HL9vCl4UFRq8fTQ+Og/G7Hlwtng==",
"dev": true,
"peerDependencies": {
"@stencil/core": ">=2.0.0 || >=3 || >= 4.0.0-beta.0 || >= 4.0.0"
@@ -11513,9 +11513,9 @@
}
},
"@stencil/angular-output-target": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/@stencil/angular-output-target/-/angular-output-target-0.7.1.tgz",
"integrity": "sha512-lxJbCAbyAQVAKGgEpNTjSF7GZZszbrJnNdNVgzuD1hLRFJyElA6kUSL0GQrZMbiPG5lC/cYdbQwpyWHX4xN8mw==",
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@stencil/angular-output-target/-/angular-output-target-0.8.1.tgz",
"integrity": "sha512-nO04lC7JxUT0bi2sTvBslz6aut1dVOvYRckf29a0l08XnRsDzIfT5e6f+c5HL9vCl4UFRq8fTQ+Og/G7Hlwtng==",
"dev": true,
"requires": {}
},

View File

@@ -47,7 +47,7 @@
"@playwright/test": "^1.36.1",
"@rollup/plugin-node-resolve": "^8.4.0",
"@rollup/plugin-virtual": "^2.0.3",
"@stencil/angular-output-target": "^0.7.1",
"@stencil/angular-output-target": "^0.8.1",
"@stencil/react-output-target": "^0.5.3",
"@stencil/sass": "^3.0.4",
"@stencil/vue-output-target": "^0.8.6",

View File

@@ -7,6 +7,56 @@ import { vueOutputTarget } from '@stencil/vue-output-target';
// @ts-ignore
import { apiSpecGenerator } from './scripts/api-spec-generator';
const componentCorePackage = '@ionic/core';
const getAngularOutputTargets = () => {
const excludeComponents = [
// overlays that accept user components
'ion-modal',
'ion-popover',
// navigation
'ion-router',
'ion-route',
'ion-route-redirect',
'ion-router-link',
'ion-router-outlet',
'ion-nav',
'ion-back-button',
// tabs
'ion-tabs',
'ion-tab',
// auxiliar
'ion-picker-column',
]
return [
angularOutputTarget({
componentCorePackage,
directivesProxyFile: '../packages/angular/src/directives/proxies.ts',
directivesArrayFile: '../packages/angular/src/directives/proxies-list.ts',
excludeComponents,
outputType: 'component',
}),
angularOutputTarget({
componentCorePackage,
directivesProxyFile: '../packages/angular/standalone/src/directives/proxies.ts',
excludeComponents: [
...excludeComponents,
/**
* IonIcon is a special case because it does not come
* from the `@ionic/core` package, so generating proxies that
* are reliant on the CE build will reference the wrong
* import location.
*/
'ion-icon'
],
outputType: 'standalone',
})
];
}
export const config: Config = {
autoprefixCss: true,
sourceMap: false,
@@ -61,7 +111,7 @@ export const config: Config = {
],
outputTargets: [
reactOutputTarget({
componentCorePackage: '@ionic/core',
componentCorePackage,
includeImportCustomElements: true,
includePolyfills: false,
includeDefineCustomElements: false,
@@ -98,7 +148,7 @@ export const config: Config = {
]
}),
vueOutputTarget({
componentCorePackage: '@ionic/core',
componentCorePackage,
includeImportCustomElements: true,
includePolyfills: false,
includeDefineCustomElements: false,
@@ -182,30 +232,7 @@ export const config: Config = {
// type: 'stats',
// file: 'stats.json'
// },
angularOutputTarget({
componentCorePackage: '@ionic/core',
directivesProxyFile: '../packages/angular/src/directives/proxies.ts',
directivesArrayFile: '../packages/angular/src/directives/proxies-list.ts',
excludeComponents: [
// overlays that accept user components
'ion-modal',
'ion-popover',
// navigation
'ion-router',
'ion-route',
'ion-route-redirect',
'ion-router-link',
'ion-router-outlet',
// tabs
'ion-tabs',
'ion-tab',
// auxiliar
'ion-picker-column',
],
}),
...getAngularOutputTargets(),
],
buildEs5: 'prod',
testing: {

View File

@@ -1,4 +1,5 @@
import { Directive, HostListener, Input, Optional, ElementRef, NgZone } from '@angular/core';
import { HostListener, Input, Optional, ElementRef, NgZone, ChangeDetectorRef, Directive } from '@angular/core';
import type { Components } from '@ionic/core';
import type { AnimationBuilder } from '@ionic/core/components';
import { Config } from '../../providers/config';
@@ -9,21 +10,23 @@ import { IonRouterOutlet } from './router-outlet';
const BACK_BUTTON_INPUTS = ['color', 'defaultHref', 'disabled', 'icon', 'mode', 'routerAnimation', 'text', 'type'];
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export declare interface IonBackButton extends Components.IonBackButton {}
@ProxyCmp({
inputs: BACK_BUTTON_INPUTS,
})
@Directive({
selector: 'ion-back-button',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: BACK_BUTTON_INPUTS,
})
// eslint-disable-next-line @angular-eslint/directive-class-suffix
export class IonBackButtonDelegate {
export class IonBackButton {
@Input()
defaultHref: string | undefined | null;
defaultHref: string | undefined;
@Input()
routerAnimation?: AnimationBuilder;
routerAnimation: AnimationBuilder | undefined;
protected el: HTMLElement;
@@ -32,8 +35,10 @@ export class IonBackButtonDelegate {
private navCtrl: NavController,
private config: Config,
private r: ElementRef,
protected z: NgZone
protected z: NgZone,
c: ChangeDetectorRef
) {
c.detach();
this.el = this.r.nativeElement;
}

View File

@@ -1,4 +1,5 @@
import { ElementRef, Injector, Directive, EnvironmentInjector, NgZone } from '@angular/core';
import { ElementRef, Injector, EnvironmentInjector, NgZone, ChangeDetectorRef, Directive } from '@angular/core';
import type { Components } from '@ionic/core';
import { AngularDelegate } from '../../providers/angular-delegate';
import { ProxyCmp, proxyOutputs } from '../../utils/proxy';
@@ -21,25 +22,29 @@ const NAV_METHODS = [
'getPrevious',
];
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export declare interface IonNav extends Components.IonNav {}
@ProxyCmp({
inputs: NAV_INPUTS,
methods: NAV_METHODS,
})
@Directive({
selector: 'ion-nav',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: NAV_INPUTS,
})
// eslint-disable-next-line @angular-eslint/directive-class-suffix
export class NavDelegate {
export class IonNav {
protected el: HTMLElement;
constructor(
ref: ElementRef,
environmentInjector: EnvironmentInjector,
injector: Injector,
angularDelegate: AngularDelegate,
protected z: NgZone
protected z: NgZone,
c: ChangeDetectorRef
) {
c.detach();
this.el = ref.nativeElement;
ref.nativeElement.delegate = angularDelegate.create(environmentInjector, injector);
proxyOutputs(this, this.el, ['ionNavDidChange', 'ionNavWillChange']);

View File

@@ -27,12 +27,12 @@ export { IonModal } from './overlays/modal';
export { IonRouterOutlet, provideComponentInputBinding } from './directives/navigation/router-outlet';
export type { StackEvent } from './directives/navigation/stack-utils';
export { IonBackButtonDelegate } from './directives/navigation/back-button';
export { IonBackButton } from './directives/navigation/back-button';
export {
RouterLinkDelegateDirective,
RouterLinkWithHrefDelegateDirective,
} from './directives/navigation/router-link-delegate';
export { NavDelegate } from './directives/navigation/nav-delegate';
export { IonNav } from './directives/navigation/nav';
export { IonTabs } from './directives/navigation/tabs';
export { ProxyCmp } from './utils/proxy';

View File

@@ -1,20 +1,23 @@
import { Directive, Optional, ElementRef, NgZone } from '@angular/core';
import { IonBackButtonDelegate as IonBackButtonDelegateBase, NavController, Config } from '@ionic/angular/common';
import { Optional, ElementRef, NgZone, ChangeDetectorRef, Component, ChangeDetectionStrategy } from '@angular/core';
import { IonBackButton as IonBackButtonBase, NavController, Config } from '@ionic/angular/common';
import { IonRouterOutlet } from './ion-router-outlet';
@Directive({
@Component({
selector: 'ion-back-button',
template: '<ng-content></ng-content>',
changeDetection: ChangeDetectionStrategy.OnPush,
})
// eslint-disable-next-line @angular-eslint/directive-class-suffix
export class IonBackButtonDelegateDirective extends IonBackButtonDelegateBase {
export class IonBackButton extends IonBackButtonBase {
constructor(
@Optional() routerOutlet: IonRouterOutlet,
navCtrl: NavController,
config: Config,
r: ElementRef,
z: NgZone
z: NgZone,
c: ChangeDetectorRef
) {
super(routerOutlet, navCtrl, config, r, z);
super(routerOutlet, navCtrl, config, r, z, c);
}
}

View File

@@ -0,0 +1,29 @@
import {
ElementRef,
Injector,
EnvironmentInjector,
NgZone,
ChangeDetectorRef,
Component,
ChangeDetectionStrategy,
} from '@angular/core';
import { IonNav as IonNavBase, AngularDelegate } from '@ionic/angular/common';
@Component({
selector: 'ion-nav',
template: '<ng-content></ng-content>',
changeDetection: ChangeDetectionStrategy.OnPush,
})
// eslint-disable-next-line @angular-eslint/directive-class-suffix
export class IonNav extends IonNavBase {
constructor(
ref: ElementRef,
environmentInjector: EnvironmentInjector,
injector: Injector,
angularDelegate: AngularDelegate,
z: NgZone,
c: ChangeDetectorRef
) {
super(ref, environmentInjector, injector, angularDelegate, z, c);
}
}

View File

@@ -1,18 +0,0 @@
import { Directive, ElementRef, Injector, EnvironmentInjector, NgZone } from '@angular/core';
import { NavDelegate as NavDelegateBase, AngularDelegate } from '@ionic/angular/common';
@Directive({
selector: 'ion-nav',
})
// eslint-disable-next-line @angular-eslint/directive-class-suffix
export class NavDelegate extends NavDelegateBase {
constructor(
ref: ElementRef,
environmentInjector: EnvironmentInjector,
injector: Injector,
angularDelegate: AngularDelegate,
z: NgZone
) {
super(ref, environmentInjector, injector, angularDelegate, z);
}
}

View File

@@ -8,7 +8,6 @@ export const DIRECTIVES = [
d.IonAlert,
d.IonApp,
d.IonAvatar,
d.IonBackButton,
d.IonBackdrop,
d.IonBadge,
d.IonBreadcrumb,
@@ -50,7 +49,6 @@ export const DIRECTIVES = [
d.IonMenu,
d.IonMenuButton,
d.IonMenuToggle,
d.IonNav,
d.IonNavLink,
d.IonNote,
d.IonPicker,

View File

@@ -230,28 +230,6 @@ export class IonAvatar {
export declare interface IonAvatar extends Components.IonAvatar {}
@ProxyCmp({
inputs: ['color', 'defaultHref', 'disabled', 'icon', 'mode', 'routerAnimation', 'text', 'type']
})
@Component({
selector: 'ion-back-button',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: ['color', 'defaultHref', 'disabled', 'icon', 'mode', 'routerAnimation', 'text', 'type'],
})
export class IonBackButton {
protected el: HTMLElement;
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
c.detach();
this.el = r.nativeElement;
}
}
export declare interface IonBackButton extends Components.IonBackButton {}
@ProxyCmp({
inputs: ['stopPropagation', 'tappable', 'visible']
})
@@ -1395,39 +1373,6 @@ export class IonMenuToggle {
export declare interface IonMenuToggle extends Components.IonMenuToggle {}
@ProxyCmp({
inputs: ['animated', 'animation', 'root', 'rootParams', 'swipeGesture'],
methods: ['push', 'insert', 'insertPages', 'pop', 'popTo', 'popToRoot', 'removeIndex', 'setRoot', 'setPages', 'getActive', 'getByIndex', 'canGoBack', 'getPrevious']
})
@Component({
selector: 'ion-nav',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: ['animated', 'animation', 'root', 'rootParams', 'swipeGesture'],
})
export class IonNav {
protected el: HTMLElement;
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
c.detach();
this.el = r.nativeElement;
proxyOutputs(this, this.el, ['ionNavWillChange', 'ionNavDidChange']);
}
}
export declare interface IonNav extends Components.IonNav {
/**
* Event fired when the nav will change components
*/
ionNavWillChange: EventEmitter<CustomEvent<void>>;
/**
* Event fired when the nav has changed components
*/
ionNavDidChange: EventEmitter<CustomEvent<void>>;
}
@ProxyCmp({
inputs: ['component', 'componentProps', 'routerAnimation', 'routerDirection']
})

View File

@@ -5,8 +5,8 @@ export { RadioValueAccessorDirective as RadioValueAccessor } from './directives/
export { SelectValueAccessorDirective as SelectValueAccessor } from './directives/control-value-accessors/select-value-accessor';
export { TextValueAccessorDirective as TextValueAccessor } from './directives/control-value-accessors/text-value-accessor';
export { IonTabs } from './directives/navigation/ion-tabs';
export { IonBackButtonDelegateDirective as IonBackButtonDelegate } from './directives/navigation/ion-back-button';
export { NavDelegate } from './directives/navigation/nav-delegate';
export { IonBackButton } from './directives/navigation/ion-back-button';
export { IonNav } from './directives/navigation/ion-nav';
export { IonRouterOutlet } from './directives/navigation/ion-router-outlet';
export {
RouterLinkDelegateDirective as RouterLinkDelegate,

View File

@@ -17,10 +17,10 @@ import {
SelectValueAccessorDirective,
TextValueAccessorDirective,
} from './directives/control-value-accessors';
import { IonBackButtonDelegateDirective } from './directives/navigation/ion-back-button';
import { IonBackButton } from './directives/navigation/ion-back-button';
import { IonNav } from './directives/navigation/ion-nav';
import { IonRouterOutlet } from './directives/navigation/ion-router-outlet';
import { IonTabs } from './directives/navigation/ion-tabs';
import { NavDelegate } from './directives/navigation/nav-delegate';
import {
RouterLinkDelegateDirective,
RouterLinkWithHrefDelegateDirective,
@@ -47,8 +47,8 @@ const DECLARATIONS = [
// navigation
IonTabs,
IonRouterOutlet,
IonBackButtonDelegateDirective,
NavDelegate,
IonBackButton,
IonNav,
RouterLinkDelegateDirective,
RouterLinkWithHrefDelegateDirective,
];

View File

@@ -0,0 +1,57 @@
/* eslint-disable */
/* tslint:disable */
import { fromEvent } from 'rxjs';
export const proxyInputs = (Cmp: any, inputs: string[]) => {
const Prototype = Cmp.prototype;
inputs.forEach((item) => {
Object.defineProperty(Prototype, item, {
get() {
return this.el[item];
},
set(val: any) {
this.z.runOutsideAngular(() => (this.el[item] = val));
},
});
});
};
export const proxyMethods = (Cmp: any, methods: string[]) => {
const Prototype = Cmp.prototype;
methods.forEach((methodName) => {
Prototype[methodName] = function () {
const args = arguments;
return this.z.runOutsideAngular(() => this.el[methodName].apply(this.el, args));
};
});
};
export const proxyOutputs = (instance: any, el: any, events: string[]) => {
events.forEach((eventName) => (instance[eventName] = fromEvent(el, eventName)));
};
export const defineCustomElement = (tagName: string, customElement: any) => {
if (customElement !== undefined && typeof customElements !== 'undefined' && !customElements.get(tagName)) {
customElements.define(tagName, customElement);
}
};
// tslint:disable-next-line: only-arrow-functions
export function ProxyCmp(opts: { defineCustomElementFn?: () => void; inputs?: any; methods?: any }) {
const decorator = function (cls: any) {
const { defineCustomElementFn, inputs, methods } = opts;
if (defineCustomElementFn !== undefined) {
defineCustomElementFn();
}
if (inputs) {
proxyInputs(cls, inputs);
}
if (methods) {
proxyMethods(cls, methods);
}
return cls;
};
return decorator;
}

View File

@@ -0,0 +1,24 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, NgZone } from '@angular/core';
import { defineCustomElement as defineIonIcon } from 'ionicons/components/ion-icon.js';
import { ProxyCmp } from './angular-component-lib/utils';
@ProxyCmp({
defineCustomElementFn: defineIonIcon,
inputs: ['color', 'flipRtl', 'icon', 'ios', 'lazy', 'md', 'mode', 'name', 'sanitize', 'size', 'src'],
})
@Component({
selector: 'ion-icon',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: ['color', 'flipRtl', 'icon', 'ios', 'lazy', 'md', 'mode', 'name', 'sanitize', 'size', 'src'],
standalone: true,
})
export class IonIcon {
protected el: HTMLElement;
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
c.detach();
this.el = r.nativeElement;
}
}

View File

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,6 @@ export { IonModal } from './overlays/modal';
export { IonPopover } from './overlays/popover';
export { IonRouterOutlet } from './navigation/router-outlet';
export { IonRouterLink, IonRouterLinkWithHref } from './navigation/router-link-delegate';
export { IonNav } from './navigation/nav-delegate';
export { provideIonicAngular } from './providers/ionic-angular';
export {
ActionSheetController,
@@ -23,3 +22,6 @@ export {
NavParams,
IonicRouteStrategy,
} from '@ionic/angular/common';
export { IonNav } from './navigation/nav';
export { IonIcon } from './directives/icon';
export * from './directives/proxies';

View File

@@ -1,10 +1,5 @@
import { Component, Optional, ChangeDetectionStrategy, ElementRef, NgZone } from '@angular/core';
import {
IonBackButtonDelegate as IonBackButtonDelegateBase,
NavController,
Config,
ProxyCmp,
} from '@ionic/angular/common';
import { Component, Optional, ChangeDetectionStrategy, ElementRef, NgZone, ChangeDetectorRef } from '@angular/core';
import { IonBackButton as IonBackButtonBase, NavController, Config, ProxyCmp } from '@ionic/angular/common';
import { defineCustomElement } from '@ionic/core/components/ion-back-button.js';
import { IonRouterOutlet } from './router-outlet';
@@ -19,14 +14,15 @@ import { IonRouterOutlet } from './router-outlet';
standalone: true,
})
// eslint-disable-next-line @angular-eslint/directive-class-suffix
export class IonBackButton extends IonBackButtonDelegateBase {
export class IonBackButton extends IonBackButtonBase {
constructor(
@Optional() routerOutlet: IonRouterOutlet,
navCtrl: NavController,
config: Config,
r: ElementRef,
z: NgZone
z: NgZone,
c: ChangeDetectorRef
) {
super(routerOutlet, navCtrl, config, r, z);
super(routerOutlet, navCtrl, config, r, z, c);
}
}

View File

@@ -1,5 +1,5 @@
import { Component, ElementRef, Injector, EnvironmentInjector, NgZone } from '@angular/core';
import { NavDelegate as NavDelegateBase, ProxyCmp, AngularDelegate } from '@ionic/angular/common';
import { Component, ElementRef, Injector, EnvironmentInjector, NgZone, ChangeDetectorRef } from '@angular/core';
import { IonNav as IonNavBase, ProxyCmp, AngularDelegate } from '@ionic/angular/common';
import { defineCustomElement } from '@ionic/core/components/ion-nav.js';
@ProxyCmp({
@@ -10,14 +10,15 @@ import { defineCustomElement } from '@ionic/core/components/ion-nav.js';
template: '<ng-content></ng-content>',
standalone: true,
})
export class IonNav extends NavDelegateBase {
export class IonNav extends IonNavBase {
constructor(
ref: ElementRef,
environmentInjector: EnvironmentInjector,
injector: Injector,
angularDelegate: AngularDelegate,
z: NgZone
z: NgZone,
c: ChangeDetectorRef
) {
super(ref, environmentInjector, injector, angularDelegate, z);
super(ref, environmentInjector, injector, angularDelegate, z, c);
}
}

View File

@@ -1 +1,3 @@
<ion-router-outlet></ion-router-outlet>
<ion-app>
<ion-router-outlet></ion-router-outlet>
</ion-app>

View File

@@ -1,12 +1,12 @@
import { Component } from '@angular/core';
import { RouterModule } from '@angular/router';
import { IonRouterOutlet } from '@ionic/angular/standalone';
import { IonRouterOutlet, IonApp } from '@ionic/angular/standalone';
@Component({
selector: 'app-root-standalone',
templateUrl: './app.component.html',
standalone: true,
imports: [RouterModule, IonRouterOutlet]
imports: [RouterModule, IonRouterOutlet, IonApp]
})
export class AppComponent {
}

View File

@@ -6,14 +6,15 @@ export const routes: Routes = [
path: '',
component: AppComponent,
children: [
{ path: 'popover', loadComponent: () => import('../popover/popover.component').then(m => m.PopoverComponent) },
{ path: 'modal', loadComponent: () => import('../modal/modal.component').then(m => m.ModalComponent) },
{ path: 'router-outlet', loadComponent: () => import('../router-outlet/router-outlet.component').then(m => m.RouterOutletComponent) },
{ path: 'back-button', loadComponent: () => import('../back-button/back-button.component').then(m => m.BackButtonComponent) },
{ path: 'router-link', loadComponent: () => import('../router-link/router-link.component').then(m => m.RouterLinkComponent) },
{ path: 'nav', loadComponent: () => import('../nav/nav.component').then(m => m.NavComponent) },
{ path: 'providers', loadComponent: () => import('../providers/providers.component').then(m => m.ProvidersComponent) },
{ path: 'overlay-controllers', loadComponent: () => import('../overlay-controllers/overlay-controllers.component').then(m => m.OverlayControllersComponent) },
{ path: 'popover', loadComponent: () => import('../popover/popover.component').then(c => c.PopoverComponent) },
{ path: 'modal', loadComponent: () => import('../modal/modal.component').then(c => c.ModalComponent) },
{ path: 'router-outlet', loadComponent: () => import('../router-outlet/router-outlet.component').then(c => c.RouterOutletComponent) },
{ path: 'back-button', loadComponent: () => import('../back-button/back-button.component').then(c => c.BackButtonComponent) },
{ path: 'router-link', loadComponent: () => import('../router-link/router-link.component').then(c => c.RouterLinkComponent) },
{ path: 'nav', loadComponent: () => import('../nav/nav.component').then(c => c.NavComponent) },
{ path: 'providers', loadComponent: () => import('../providers/providers.component').then(c => c.ProvidersComponent) },
{ path: 'overlay-controllers', loadComponent: () => import('../overlay-controllers/overlay-controllers.component').then(c => c.OverlayControllersComponent) },
{ path: 'button', loadComponent: () => import('../button/button.component').then(c => c.ButtonComponent) }
]
},
];

View File

@@ -0,0 +1,12 @@
import { Component } from "@angular/core";
import { IonButton } from '@ionic/angular/standalone';
@Component({
selector: 'app-button',
template: `<ion-button>Test</ion-button>`,
standalone: true,
imports: [IonButton]
})
export class ButtonComponent {
}