refactor(angular): move to packages directory (#27719)

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. -->

The `angular` directory sits at the root of the project instead of in
`packages` with all the other JS Framework integrations. This does not
cause any functional issues with Ionic, but it is confusing since
integrations are not in a consistent place.

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

- Moves the `angular` directory to `packages/angular`

Note: Most files should remain unchanged. The only files I changed are
the files that had direct paths to the old `angular` directory:

1. Removes the `angular` path in `lerna.json`. This is now covered by
`packages/*`
2. Updated the angular file path in `.gitignore`
3. Updates the path to the angular package in `stencil.config.ts` for
the Angular Output Targets
4. Updates some of Angular's sync scripts to correctly get the core
stylesheets as well as the core package.
5. Updates the test app sync script to correctly sync core and
angular-server

~I'm not entirely sure why GitHub thinks
https://github.com/ionic-team/ionic-framework/pull/27719/files#diff-f5bba7e7c7c75426e2b9c89868310cb03890493b4efe0252adf8d12cc8398962
is a new file since it exists in `main` here:
1f06be4a31/angular/test/base/scripts/build-ionic.sh~
Fixed in
6e7fc49827

## 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. -->

Dev build: `7.1.2-dev.11688052109.13454f5c`
This commit is contained in:
Liam DeBeasi
2023-07-05 13:52:35 -04:00
committed by GitHub
parent e5ab6d8804
commit 32bc33ed28
268 changed files with 29 additions and 30 deletions

View File

@@ -0,0 +1,17 @@
<ion-content>
<ion-button id="dismiss" (click)="modal.dismiss()">Dismiss Modal</ion-button>
<ion-accordion-group [value]="'a'">
<ion-accordion value="a">
<ion-item slot="header">
<ion-label>A</ion-label>
</ion-item>
<div slot="content">A content</div>
</ion-accordion>
<ion-accordion value="b">
<ion-item slot="header">
<ion-label>B</ion-label>
</ion-item>
<div slot="content">B content</div>
</ion-accordion>
</ion-accordion-group>
</ion-content>

View File

@@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-accordion-modal',
templateUrl: './accordion-modal.component.html',
})
export class AccordionModalComponent {
modal!: HTMLIonModalElement;
constructor() {}
}

View File

@@ -0,0 +1,13 @@
<ion-header>
<ion-toolbar>
<ion-buttons>
<ion-back-button></ion-back-button>
</ion-buttons>
<ion-title>
Accordion test
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-button id="open-modal" (click)="open()">Open Modal</ion-button>
</ion-content>

View File

@@ -0,0 +1,22 @@
import { Component } from '@angular/core';
import { ModalController } from '@ionic/angular';
import { AccordionModalComponent } from './accordion-modal/accordion-modal.component';
@Component({
selector: 'app-accordion',
templateUrl: './accordion.component.html',
})
export class AccordionComponent {
constructor(
private modalCtrl: ModalController
) { }
async open() {
const modal = await this.modalCtrl.create({
component: AccordionModalComponent,
animated: false,
});
await modal.present();
}
}

View File

@@ -0,0 +1,13 @@
<ion-header>
<ion-toolbar>
<ion-buttons>
<ion-back-button></ion-back-button>
</ion-buttons>
<ion-title>
Alert test
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<p>Change Detections: <span id="counter">{{counter()}}</span></p>
</ion-content>

View File

@@ -0,0 +1,39 @@
import { Component, NgZone } from '@angular/core';
import { AlertController } from '@ionic/angular';
import { NavComponent } from '../nav/nav.component';
@Component({
selector: 'app-alert',
templateUrl: './alert.component.html',
})
export class AlertComponent {
changes = 0;
constructor(
private alertCtrl: AlertController
) { }
counter() {
this.changes++;
return Math.floor(this.changes / 2);
}
async openAlert() {
const alert = await this.alertCtrl.create({
header: 'Hello',
message: 'Some text',
buttons: [
{
role: 'cancel',
text: 'Cancel',
handler: () => {
console.log(NgZone.isInAngularZone());
NgZone.assertInAngularZone();
}
}
]
});
await alert.present();
}
}

View File

@@ -0,0 +1,88 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { InputsComponent } from './inputs/inputs.component';
import { ModalComponent } from './modal/modal.component';
import { RouterLinkComponent } from './router-link/router-link.component';
import { RouterLinkPageComponent } from './router-link-page/router-link-page.component';
import { RouterLinkPage2Component } from './router-link-page2/router-link-page2.component';
import { RouterLinkPage3Component } from './router-link-page3/router-link-page3.component';
import { HomePageComponent } from './home-page/home-page.component';
import { NestedOutletComponent } from './nested-outlet/nested-outlet.component';
import { NestedOutletPageComponent } from './nested-outlet-page/nested-outlet-page.component';
import { NestedOutletPage2Component } from './nested-outlet-page2/nested-outlet-page2.component';
import { ViewChildComponent } from './view-child/view-child.component';
import { ProvidersComponent } from './providers/providers.component';
import { FormComponent } from './form/form.component';
import { NavigationPage1Component } from './navigation-page1/navigation-page1.component';
import { NavigationPage2Component } from './navigation-page2/navigation-page2.component';
import { NavigationPage3Component } from './navigation-page3/navigation-page3.component';
import { AlertComponent } from './alert/alert.component';
import { AccordionComponent } from './accordion/accordion.component';
const routes: Routes = [
{ path: '', component: HomePageComponent },
{ path: 'version-test', loadChildren: () => import('./version-test').then(m => m.VersionTestModule) },
{ path: 'accordions', component: AccordionComponent },
{ path: 'alerts', component: AlertComponent },
{ path: 'inputs', component: InputsComponent },
{ path: 'textarea', loadChildren: () => import('./textarea/textarea.module').then(m => m.TextareaModule) },
{ path: 'searchbar', loadChildren: () => import('./searchbar/searchbar.module').then(m => m.SearchbarModule) },
{ path: 'form', component: FormComponent },
{ path: 'modals', component: ModalComponent },
{ path: 'modal-inline', loadChildren: () => import('./modal-inline').then(m => m.ModalInlineModule) },
{ path: 'view-child', component: ViewChildComponent },
{ path: 'keep-contents-mounted', loadChildren: () => import('./keep-contents-mounted').then(m => m.OverlayAutoMountModule) },
{ path: 'overlays-inline', loadChildren: () => import('./overlays-inline').then(m => m.OverlaysInlineModule) },
{ path: 'popover-inline', loadChildren: () => import('./popover-inline').then(m => m.PopoverInlineModule) },
{ path: 'providers', component: ProvidersComponent },
{ path: 'router-link', component: RouterLinkComponent },
{ path: 'router-link-page', component: RouterLinkPageComponent },
{ path: 'router-link-page2/:id', component: RouterLinkPage2Component },
{ path: 'router-link-page3', component: RouterLinkPage3Component },
{ path: 'standalone', loadComponent: () => import('./standalone/standalone.component').then(c => c.StandaloneComponent) },
{ path: 'tabs', redirectTo: '/tabs/account', pathMatch: 'full' },
{
path: 'navigation',
children: [
{ path: 'page1', component: NavigationPage1Component },
{ path: 'page2', component: NavigationPage2Component },
{ path: 'page3', component: NavigationPage3Component }
]
},
{
path: 'tabs',
loadChildren: () => import('./tabs/tabs.module').then(m => m.TabsPageModule)
},
{
path: 'tabs-global',
loadChildren: () => import('./tabs-global/tabs-global.module').then(m => m.TabsGlobalModule)
},
{
path: 'tabs-slots',
loadComponent: () => import('./tabs-slots.component').then(c => c.TabsSlotsComponent)
},
{
path: 'nested-outlet',
component: NestedOutletComponent,
children: [
{
path: 'page',
component: NestedOutletPageComponent
},
{
path: 'page2',
component: NestedOutletPage2Component
}
]
},
{
path: 'form-controls/range',
loadChildren: () => import('./form-controls/range/range.module').then(m => m.RangeModule)
}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }

View File

View File

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

View File

@@ -0,0 +1,9 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
}

View File

@@ -0,0 +1,69 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { RouteReuseStrategy } from '@angular/router';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import { FormsModule } from '@angular/forms';
import { InputsComponent } from './inputs/inputs.component';
import { ModalComponent } from './modal/modal.component';
import { ModalExampleComponent } from './modal-example/modal-example.component';
import { RouterLinkComponent } from './router-link/router-link.component';
import { RouterLinkPageComponent } from './router-link-page/router-link-page.component';
import { RouterLinkPage2Component } from './router-link-page2/router-link-page2.component';
import { RouterLinkPage3Component } from './router-link-page3/router-link-page3.component';
import { HomePageComponent } from './home-page/home-page.component';
import { NestedOutletComponent } from './nested-outlet/nested-outlet.component';
import { NestedOutletPageComponent } from './nested-outlet-page/nested-outlet-page.component';
import { NestedOutletPage2Component } from './nested-outlet-page2/nested-outlet-page2.component';
import { NavComponent } from './nav/nav.component';
import { ViewChildComponent } from './view-child/view-child.component';
import { ProvidersComponent } from './providers/providers.component';
import { FormComponent } from './form/form.component';
import { NavigationPage1Component } from './navigation-page1/navigation-page1.component';
import { NavigationPage2Component } from './navigation-page2/navigation-page2.component';
import { NavigationPage3Component } from './navigation-page3/navigation-page3.component';
import { AlertComponent } from './alert/alert.component';
import { AccordionComponent } from './accordion/accordion.component';
import { AccordionModalComponent } from './accordion/accordion-modal/accordion-modal.component';
@NgModule({
declarations: [
AppComponent,
InputsComponent,
ModalComponent,
ModalExampleComponent,
RouterLinkComponent,
RouterLinkPageComponent,
RouterLinkPage2Component,
RouterLinkPage3Component,
HomePageComponent,
NestedOutletComponent,
NestedOutletPageComponent,
NestedOutletPage2Component,
NavComponent,
ViewChildComponent,
ProvidersComponent,
FormComponent,
NavigationPage1Component,
NavigationPage2Component,
NavigationPage3Component,
AlertComponent,
AccordionComponent,
AccordionModalComponent
],
imports: [
BrowserModule.withServerTransition({ appId: 'serverApp' }),
AppRoutingModule,
FormsModule,
ReactiveFormsModule,
IonicModule.forRoot({ keyboardHeight: 12345 }),
],
providers: [
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
],
bootstrap: [AppComponent]
})
export class AppModule { }

View File

@@ -0,0 +1,15 @@
import { NgModule } from '@angular/core';
import { ServerModule } from '@angular/platform-server';
import { IonicServerModule } from '@ionic/angular-server';
import { AppModule } from './app.module';
import { AppComponent } from './app.component';
@NgModule({
imports: [
AppModule,
ServerModule,
IonicServerModule
],
bootstrap: [AppComponent],
})
export class AppServerModule {}

View File

@@ -0,0 +1,13 @@
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { RangeComponent } from './range.component';
@NgModule({
imports: [
RouterModule.forChild([
{ path: '', component: RangeComponent }
])
]
})
export class RangeRoutingModule { }

View File

@@ -0,0 +1,17 @@
<ion-header>
<ion-toolbar>
<ion-title>Range</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<form [formGroup]="form">
<ion-list>
<ion-item>
<ion-range formControlName="range" min="0" max="20">
<div slot="label">Range</div>
</ion-range>
</ion-item>
</ion-list>
<ion-button type="submit">Submit</ion-button>
</form>
</ion-content>

View File

@@ -0,0 +1,18 @@
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'app-range',
templateUrl: './range.component.html'
})
export class RangeComponent {
form: FormGroup;
constructor(private fb: FormBuilder) {
this.form = this.fb.group({
range: [5, Validators.min(10)]
});
}
}

View File

@@ -0,0 +1,19 @@
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { IonicModule } from '@ionic/angular';
import { RangeRoutingModule } from './range-routing.module';
import { RangeComponent } from './range.component';
@NgModule({
imports: [
FormsModule,
ReactiveFormsModule,
IonicModule,
RangeRoutingModule
],
declarations: [
RangeComponent
]
})
export class RangeModule { }

View File

@@ -0,0 +1,78 @@
<ion-header>
<ion-toolbar>
<ion-title>
Forms test
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
<ion-list>
<ion-item>
<ion-label>DateTime</ion-label>
<ion-datetime formControlName="datetime" min="1994-03-14" max="2017-12-09" display-format="MM/DD/YYYY">
</ion-datetime>
</ion-item>
<ion-item>
<ion-select label="Select" formControlName="select">
<ion-select-option value="">No Game Console</ion-select-option>
<ion-select-option value="nes">NES</ion-select-option>
<ion-select-option value="n64" selected>Nintendo64</ion-select-option>
<ion-select-option value="ps">PlayStation</ion-select-option>
<ion-select-option value="genesis">Sega Genesis</ion-select-option>
<ion-select-option value="saturn">Sega Saturn</ion-select-option>
<ion-select-option value="snes">SNES</ion-select-option>
</ion-select>
</ion-item>
<ion-item>
<ion-toggle formControlName="toggle">
Toggle
</ion-toggle>
</ion-item>
<ion-item>
<ion-input label="Input (required)" formControlName="input" class="required" id="touched-input-test"></ion-input>
</ion-item>
<ion-button id="input-touched" (click)="setTouched()">Set Input Touched</ion-button>
<ion-item>
<ion-input label="Input" formControlName="input2"></ion-input>
</ion-item>
<ion-item>
<ion-checkbox formControlName="checkbox">
Checkbox
</ion-checkbox>
</ion-item>
</ion-list>
<p>
Form Status: <span id="status">{{ profileForm.status }}</span>
</p>
<p>
Form value: <span id="data">{{ profileForm.value | json }}</span>
</p>
<p>
Form Submit: <span id="submit">{{submitted}}</span>
</p>
<ion-button id="mark-all-touched-button" (click)="markAllAsTouched()">Mark all as touched</ion-button>
<ion-button id="submit-button" type="submit" [disabled]="!profileForm.valid">Submit</ion-button>
</form>
<ion-list>
<ion-item>
<ion-toggle [formControl]="outsideToggle">
Outside form
</ion-toggle>
<ion-note slot="end">{{outsideToggle.value}}</ion-note>
</ion-item>
</ion-list>
<p>
<ion-button (click)="setValues()" id="set-values">Set values</ion-button>
</p>
</ion-content>

View File

@@ -0,0 +1,53 @@
import { Component } from '@angular/core';
import { UntypedFormGroup, UntypedFormBuilder, Validators, UntypedFormControl } from '@angular/forms';
@Component({
selector: 'app-form',
templateUrl: './form.component.html',
})
export class FormComponent {
submitted = 'false';
profileForm: UntypedFormGroup;
outsideToggle = new UntypedFormControl(true);
constructor(fb: UntypedFormBuilder) {
this.profileForm = fb.group({
datetime: ['2010-08-20', Validators.required],
select: [undefined, Validators.required],
toggle: [false],
input: ['', Validators.required],
input2: ['Default Value'],
checkbox: [false]
}, {
updateOn: typeof (window as any) !== 'undefined' && window.location.hash === '#blur' ? 'blur' : 'change'
});
}
setTouched() {
const formControl = this.profileForm.get('input');
if (formControl) {
formControl.markAsTouched();
}
}
onSubmit() {
this.submitted = 'true';
}
setValues() {
this.profileForm.patchValue({
datetime: '2010-08-20',
select: 'nes',
toggle: true,
input: 'Some value',
input2: 'Another values',
checkbox: true
});
}
markAllAsTouched() {
this.profileForm.markAllAsTouched();
}
}

View File

@@ -0,0 +1,61 @@
<ion-header>
<ion-toolbar>
<ion-title>
Test App - Angular {{ angularVersion.major }}
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
<ion-item routerLink="/alerts" [routerAnimation]="routerAnimation">
<ion-label>
Alerts test
</ion-label>
</ion-item>
<ion-item routerLink="/inputs">
<ion-label>
Inputs test
</ion-label>
</ion-item>
<ion-item routerLink="/form">
<ion-label>
Form test
</ion-label>
</ion-item>
<ion-item routerLink="/modals">
<ion-label>
Modals test
</ion-label>
</ion-item>
<ion-item routerLink="/router-link">
<ion-label>
Router link test
</ion-label>
</ion-item>
<ion-item routerLink="/tabs">
<ion-label>
Tabs test
</ion-label>
</ion-item>
<ion-item routerLink="/nested-outlet/page">
<ion-label>
Nested ion-router-outlet
</ion-label>
</ion-item>
<ion-item routerLink="/view-child">
<ion-label>
ViewChild()
</ion-label>
</ion-item>
<ion-item routerLink="/providers">
<ion-label>
Providers
</ion-label>
</ion-item>
<ion-item routerLink="/accordions">
<ion-label>
Accordions Test
</ion-label>
</ion-item>
</ion-list>
</ion-content>

View File

@@ -0,0 +1,28 @@
import { Component, VERSION } from '@angular/core';
import { AnimationBuilder, AnimationController } from '@ionic/angular';
@Component({
selector: 'app-home-page',
templateUrl: './home-page.component.html',
})
export class HomePageComponent {
angularVersion = VERSION;
routerAnimation: AnimationBuilder = (_, opts) => {
const { direction, enteringEl, leavingEl } = opts;
const animation = this.animationCtrl.create().duration(500).easing('ease-out');
const enteringAnimation = this.animationCtrl.create().addElement(enteringEl).beforeRemoveClass(['ion-page-invisible']);
const leavingAnimation = this.animationCtrl.create().addElement(leavingEl).beforeRemoveClass(['ion-page-invisible']);
if (direction === 'back') {
enteringAnimation.fromTo('transform', 'translateX(-100%)', 'translateX(0%)');
leavingAnimation.fromTo('transform', 'translateX(0%)', 'translateX(100%)');
} else {
enteringAnimation.fromTo('transform', 'translateX(100%)', 'translateX(0%)');
leavingAnimation.fromTo('transform', 'translateX(0%)', 'translateX(-100%)');
}
return animation.addAnimation([enteringAnimation, leavingAnimation]);
};
constructor(private animationCtrl: AnimationController) {}
}

View File

@@ -0,0 +1,93 @@
<ion-header>
<ion-toolbar>
<ion-title>
Inputs test
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<p>Change Detections: <span id="counter">{{counter()}}</span></p>
<ion-list>
<ion-item>
<ion-label>DateTime</ion-label>
<ion-datetime [(ngModel)]="datetime" min="1994-03-14" max="2017-12-09" display-format="MM/DD/YYYY"></ion-datetime>
<ion-note slot="end" id="datetime-note">{{datetime}}</ion-note>
</ion-item>
<ion-item color="dark">
<ion-label>DateTime Mirror</ion-label>
<ion-datetime [(ngModel)]="datetime" min="1994-03-14" max="2017-12-09" display-format="MM/DD/YYYY"></ion-datetime>
<ion-note slot="end">{{datetime}}</ion-note>
</ion-item>
<ion-item>
<ion-select label="Select" [(ngModel)]="select" id="game-console">
<ion-select-option value="">No Game Console</ion-select-option>
<ion-select-option value="nes">NES</ion-select-option>
<ion-select-option value="n64" selected>Nintendo64</ion-select-option>
<ion-select-option value="ps">PlayStation</ion-select-option>
<ion-select-option value="genesis">Sega Genesis</ion-select-option>
<ion-select-option value="saturn">Sega Saturn</ion-select-option>
<ion-select-option value="snes">SNES</ion-select-option>
</ion-select>
<ion-note slot="end" id="select-note">{{select}}</ion-note>
</ion-item>
<ion-item color="dark">
<ion-select label="Select Mirror" [(ngModel)]="select">
<ion-select-option value="">No Game Console</ion-select-option>
<ion-select-option value="nes">NES</ion-select-option>
<ion-select-option value="n64" selected>Nintendo64</ion-select-option>
<ion-select-option value="ps">PlayStation</ion-select-option>
<ion-select-option value="genesis">Sega Genesis</ion-select-option>
<ion-select-option value="saturn">Sega Saturn</ion-select-option>
<ion-select-option value="snes">SNES</ion-select-option>
</ion-select>
<ion-note slot="end">{{select}}</ion-note>
</ion-item>
<ion-item>
<ion-toggle [(ngModel)]="toggle" slot="end">
Toggle
</ion-toggle>
<ion-note slot="end" id="toggle-note">{{toggle}}</ion-note>
</ion-item>
<ion-item color="dark">
<ion-toggle [(ngModel)]="toggle" slot="end">
Toggle Mirror
</ion-toggle>
<ion-note slot="end">{{toggle}}</ion-note>
</ion-item>
<ion-item>
<ion-input label="Input" [(ngModel)]="input"></ion-input>
<ion-note slot="end" id="input-note">{{input}}</ion-note>
</ion-item>
<ion-item color="dark">
<ion-input label="Input Mirror" [(ngModel)]="input"></ion-input>
<ion-note slot="end">{{input}}</ion-note>
</ion-item>
<ion-item>
<ion-checkbox [(ngModel)]="checkbox" slot="start" id="first-checkbox">
Checkbox
</ion-checkbox>
<ion-note slot="end" id="checkbox-note">{{checkbox}}</ion-note>
</ion-item>
<ion-item color="dark">
<ion-checkbox [(ngModel)]="checkbox" slot="start">
Checkbox Mirror
</ion-checkbox>
<ion-note slot="end">{{checkbox}}</ion-note>
</ion-item>
</ion-list>
<p>
<ion-button (click)="setValues()" id="set-button">Set values</ion-button>
<ion-button (click)="resetValues()" id="reset-button">Reset values</ion-button>
</p>
</ion-content>

View File

@@ -0,0 +1,38 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-inputs',
templateUrl: './inputs.component.html',
})
export class InputsComponent {
datetime? = '1994-03-15';
input? = 'some text';
checkbox = true;
toggle = true;
select? = 'nes';
changes = 0;
setValues() {
console.log('set values');
this.datetime = '1994-03-15';
this.input = 'some text';
this.checkbox = true;
this.toggle = true;
this.select = 'nes';
}
resetValues() {
console.log('reset values');
this.datetime = undefined;
this.input = undefined;
this.checkbox = false;
this.toggle = false;
this.select = undefined;
}
counter() {
this.changes++;
return Math.floor(this.changes / 2);
}
}

View File

@@ -0,0 +1,2 @@
export * from './keep-contents-mounted.component';
export * from './keep-contents-mounted.module';

View File

@@ -0,0 +1,16 @@
import { NgModule } from "@angular/core";
import { RouterModule } from "@angular/router";
import { OverlayKeepContentsMounted } from ".";
@NgModule({
imports: [
RouterModule.forChild([
{
path: '',
component: OverlayKeepContentsMounted
}
])
],
exports: [RouterModule]
})
export class OverlayKeepContentsMountedRoutingModule { }

View File

@@ -0,0 +1,22 @@
<ion-content>
<ion-button id="open-modal" (click)="modal.present()">Open Modal</ion-button>
<ion-button id="open-popover" (click)="popover.present()">Open Popover</ion-button>
<ion-modal [keepContentsMounted]="true" #modal>
<ng-template>
<ion-content>
<ion-button (click)="modal.dismiss()">Dismiss</ion-button>
Modal Content
</ion-content>
</ng-template>
</ion-modal>
<ion-popover [keepContentsMounted]="true" #popover>
<ng-template>
<ion-content>
<ion-button (click)="popover.dismiss()">Dismiss</ion-button>
Popover Content
</ion-content>
</ng-template>
</ion-popover>
</ion-content>

View File

@@ -0,0 +1,13 @@
import { Component } from "@angular/core";
/**
* Validates that inline modals correctly mount
* inner components when keepContentsMounted is
* enabled.
*/
@Component({
selector: 'app-keep-contents-mounted',
templateUrl: 'keep-contents-mounted.component.html'
})
export class OverlayKeepContentsMounted {
}

View File

@@ -0,0 +1,12 @@
import { CommonModule } from "@angular/common";
import { NgModule } from "@angular/core";
import { IonicModule } from "@ionic/angular";
import { OverlayKeepContentsMountedRoutingModule } from "./keep-contents-mounted-routing.module";
import { OverlayKeepContentsMounted } from "./keep-contents-mounted.component";
@NgModule({
imports: [CommonModule, IonicModule, OverlayKeepContentsMountedRoutingModule],
declarations: [OverlayKeepContentsMounted],
exports: [OverlayKeepContentsMounted]
})
export class OverlayAutoMountModule { }

View File

@@ -0,0 +1,38 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-button (click)="closeModal()" id="close-modal">
Close
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<h1>Value</h1>
<h2>{{value}}</h2>
<h3>{{valueFromParams}}</h3>
<p>modal is defined: <span id="modalInstance">{{ !!modal }}</span></p>
<p>ngOnInit: <span id="ngOnInit">{{onInit}}</span></p>
<p>ionViewWillEnter: <span id="ionViewWillEnter">{{willEnter}}</span></p>
<p>ionViewDidEnter: <span id="ionViewDidEnter">{{didEnter}}</span></p>
<p>ionViewWillLeave: <span id="ionViewWillLeave">{{willLeave}}</span></p>
<p>ionViewDidLeave: <span id="ionViewDidLeave">{{didLeave}}</span></p>
<p *ngIf="!!nav">
<ion-button (click)="push()" class="push-page">Push page</ion-button>
<ion-button (click)="pop()" class="pop-page">Pop page</ion-button>
</p>
<form [formGroup]="form">
<ion-item id="inputWithFloatingLabel">
<ion-label color="primary" position="floating">Floating Label</ion-label>
<ion-select multiple="false" formControlName="select">
<ion-select-option [value]="0">Option 0</ion-select-option>
<ion-select-option [value]="1">Option 1</ion-select-option>
</ion-select>
</ion-item>
<ion-button id="set-to-null" (click)="setSelect(null)">Set select value to "null"></ion-button>
<ion-button id="set-to-undefined" (click)="setSelect(undefined)">Set select value to "undefined"></ion-button>
</form>
</ion-content>

View File

@@ -0,0 +1,74 @@
import { Component, Input, NgZone, OnInit, Optional } from '@angular/core';
import { UntypedFormControl, UntypedFormGroup } from '@angular/forms';
import { ModalController, NavParams, IonNav, ViewWillLeave, ViewDidEnter, ViewDidLeave } from '@ionic/angular';
@Component({
selector: 'app-modal-example',
templateUrl: './modal-example.component.html',
})
export class ModalExampleComponent implements OnInit, ViewWillLeave, ViewDidEnter, ViewWillLeave, ViewDidLeave {
@Input() value?: string;
form = new UntypedFormGroup({
select: new UntypedFormControl([])
});
valueFromParams: string;
onInit = 0;
willEnter = 0;
didEnter = 0;
willLeave = 0;
didLeave = 0;
modal!: HTMLElement;
constructor(
private modalCtrl: ModalController,
@Optional() public nav: IonNav,
navParams: NavParams
) {
this.valueFromParams = navParams.get('prop');
}
ngOnInit() {
NgZone.assertInAngularZone();
this.onInit++;
}
setSelect(value: null | undefined) {
this.form.get('select')!.setValue(value);
}
ionViewWillEnter() {
if (this.onInit !== 1) {
throw new Error('ngOnInit was not called');
}
NgZone.assertInAngularZone();
this.willEnter++;
}
ionViewDidEnter() {
NgZone.assertInAngularZone();
this.didEnter++;
}
ionViewWillLeave() {
NgZone.assertInAngularZone();
this.willLeave++;
}
ionViewDidLeave() {
NgZone.assertInAngularZone();
this.didLeave++;
}
closeModal() {
this.modalCtrl.dismiss();
}
push() {
this.nav.push(ModalExampleComponent, {
'value': 'pushed!'
});
}
pop() {
this.nav.pop();
}
}

View File

@@ -0,0 +1,2 @@
export * from './modal-inline.component';
export * from './modal-inline.module';

View File

@@ -0,0 +1,16 @@
import { NgModule } from "@angular/core";
import { RouterModule } from "@angular/router";
import { ModalInlineComponent } from ".";
@NgModule({
imports: [
RouterModule.forChild([
{
path: '',
component: ModalInlineComponent
}
])
],
exports: [RouterModule]
})
export class ModalInlineRoutingModule { }

View File

@@ -0,0 +1,20 @@
<ion-button id="open-modal">Open Modal</ion-button>
<ul>
<li>
breakpointDidChange event count: <span id="breakpointDidChange">{{ breakpointDidChangeCounter }}</span>
</li>
</ul>
<ion-modal [animated]="false" trigger="open-modal" [breakpoints]="[0.1, 0.5, 1]" [initialBreakpoint]="0.5"
(ionBreakpointDidChange)="onBreakpointDidChange()">
<ng-template>
<ion-content>
<ion-list>
<ion-item *ngFor="let item of items">
<ion-label>{{ item }}</ion-label>
</ion-item>
</ion-list>
</ion-content>
</ng-template>
</ion-modal>

View File

@@ -0,0 +1,28 @@
import { AfterViewInit, Component } from "@angular/core";
/**
* Validates that inline modals will correctly display
* dynamic contents that are updated after the modal is
* display.
*/
@Component({
selector: 'app-modal-inline',
templateUrl: 'modal-inline.component.html'
})
export class ModalInlineComponent implements AfterViewInit {
items: string[] = [];
breakpointDidChangeCounter = 0;
ngAfterViewInit(): void {
setTimeout(() => {
this.items = ['A', 'B', 'C', 'D'];
}, 1000);
}
onBreakpointDidChange() {
this.breakpointDidChangeCounter++;
}
}

View File

@@ -0,0 +1,12 @@
import { CommonModule } from "@angular/common";
import { NgModule } from "@angular/core";
import { IonicModule } from "@ionic/angular";
import { ModalInlineRoutingModule } from "./modal-inline-routing.module";
import { ModalInlineComponent } from "./modal-inline.component";
@NgModule({
imports: [CommonModule, IonicModule, ModalInlineRoutingModule],
declarations: [ModalInlineComponent],
exports: [ModalInlineComponent]
})
export class ModalInlineModule { }

View File

@@ -0,0 +1,17 @@
<ion-header>
<ion-toolbar>
<ion-title>
Modal test
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<ion-button (click)="openModal()" id="action-button">Open Modal</ion-button>
<ion-button (click)="openNav()" id="action-button-2">Open Nav in Modal</ion-button>
<p>
onWillDismiss: <span id="onWillDismiss">{{onWillDismiss}}</span>
</p>
<p>
onDidDismiss: <span id="onDidDismiss">{{onDidDismiss}}</span>
</p>
</ion-content>

View File

@@ -0,0 +1,49 @@
import { Component, NgZone } from '@angular/core';
import { ModalController } from '@ionic/angular';
import { ModalExampleComponent } from '../modal-example/modal-example.component';
import { NavComponent } from '../nav/nav.component';
@Component({
selector: 'app-modal',
templateUrl: './modal.component.html',
})
export class ModalComponent {
onWillDismiss = false;
onDidDismiss = false;
constructor(
private modalCtrl: ModalController
) { }
async openModal() {
return this.open(ModalExampleComponent);
}
async openNav() {
return this.open(NavComponent);
}
async open(TheModalComponent: any) {
const modal = await this.modalCtrl.create({
component: TheModalComponent,
animated: false,
componentProps: {
value: '123',
prop: '321'
}
});
await modal.present();
modal.onWillDismiss().then(() => {
NgZone.assertInAngularZone();
this.onWillDismiss = true;
});
modal.onDidDismiss().then(() => {
NgZone.assertInAngularZone();
if (!this.onWillDismiss) {
throw new Error('onWillDismiss should be emitted first');
}
this.onDidDismiss = true;
});
}
}

View File

@@ -0,0 +1 @@
<ion-nav [root]="rootPage" [rootParams]="rootParams"></ion-nav>

View File

@@ -0,0 +1,20 @@
import { Component } from '@angular/core';
import { ModalExampleComponent } from '../modal-example/modal-example.component';
import { NavParams } from '@ionic/angular';
@Component({
selector: 'app-nav',
templateUrl: './nav.component.html',
})
export class NavComponent {
rootPage = ModalExampleComponent;
rootParams: any;
constructor(
params: NavParams
) {
this.rootParams = {
...params.data
};
}
}

View File

@@ -0,0 +1,3 @@
<p>
navigation-page1 works!
</p>

View File

@@ -0,0 +1,20 @@
import { Component } from '@angular/core';
import { NavController } from '@ionic/angular';
let count = 0;
@Component({
selector: 'app-navigation-page1',
templateUrl: './navigation-page1.component.html',
})
export class NavigationPage1Component {
constructor(
private navController: NavController
) {}
ionViewDidEnter() {
if (count < 1) {
this.navController.navigateBack('/navigation/page2');
}
count++;
}
}

View File

@@ -0,0 +1,3 @@
<p>
navigation-page2 works!
</p>

View File

@@ -0,0 +1,17 @@
import { Component } from '@angular/core';
import { NavController } from '@ionic/angular';
@Component({
selector: 'app-navigation-page2',
templateUrl: './navigation-page2.component.html',
})
export class NavigationPage2Component {
constructor(
private navController: NavController
) {}
ionViewDidEnter() {
this.navController.navigateForward('/navigation/page1');
}
}

View File

@@ -0,0 +1,3 @@
<p>
navigation-page3 works!
</p>

View File

@@ -0,0 +1,16 @@
import { Component, OnInit } from '@angular/core';
import { NavController } from '@ionic/angular';
@Component({
selector: 'app-navigation-page3',
templateUrl: './navigation-page3.component.html',
})
export class NavigationPage3Component {
constructor(
private navController: NavController
) {}
ionViewDidEnter() {
this.navController.navigateRoot('/navigation/page2');
}
}

View File

@@ -0,0 +1,7 @@
<ion-content>
<h1>Nested page 1</h1>
<p>
<ion-button routerLink="/tabs" id="goto-tabs">Go To Tabs</ion-button>
<ion-button routerLink="/nested-outlet/page2" id="goto-nested-page2">Go To SECOND</ion-button>
</p>
</ion-content>

View File

@@ -0,0 +1,16 @@
import { Component, NgZone, OnDestroy, OnInit } from '@angular/core';
@Component({
selector: 'app-nested-outlet-page',
templateUrl: './nested-outlet-page.component.html',
})
export class NestedOutletPageComponent implements OnDestroy, OnInit {
ngOnInit() {
NgZone.assertInAngularZone();
}
ngOnDestroy() {
NgZone.assertInAngularZone();
}
}

View File

@@ -0,0 +1,6 @@
<ion-content>
<h1>Nested page 2</h1>
<p>
<ion-button routerLink="/nested-outlet/page" id="goto-nested-page1">Go To FIRST</ion-button>
</p>
</ion-content>

View File

@@ -0,0 +1,16 @@
import { Component, NgZone, OnDestroy, OnInit } from '@angular/core';
@Component({
selector: 'app-nested-outlet-page2',
templateUrl: './nested-outlet-page2.component.html',
})
export class NestedOutletPage2Component implements OnDestroy, OnInit {
ngOnInit() {
NgZone.assertInAngularZone();
}
ngOnDestroy() {
NgZone.assertInAngularZone();
}
}

View File

@@ -0,0 +1,13 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button></ion-back-button>
</ion-buttons>
<ion-title>
NESTED OUTLET
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-router-outlet></ion-router-outlet>
</ion-content>

View File

@@ -0,0 +1,9 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-nested-outlet',
templateUrl: './nested-outlet.component.html',
})
export class NestedOutletComponent {
}

View File

@@ -0,0 +1 @@
export * from './overlays-inline.module';

View File

@@ -0,0 +1,14 @@
import { NgModule } from "@angular/core";
import { RouterModule } from "@angular/router";
import { OverlaysInlineComponent } from "./overlays-inline.component";
@NgModule({
imports: [RouterModule.forChild([
{
path: '',
component: OverlaysInlineComponent
}
])],
exports: [RouterModule]
})
export class OverlaysInlineRoutingModule { }

View File

@@ -0,0 +1,40 @@
<ion-content>
<ion-button id="open-alert">Open Alert</ion-button>
<ion-button id="open-action-sheet">Open Action Sheet</ion-button>
<ion-button id="open-loading">Open Loading</ion-button>
<ion-button id="open-toast">Open Toast</ion-button>
<ion-button id="open-picker">Open Picker</ion-button>
<ion-alert
trigger="open-alert"
header="Alert"
message="This is an alert."
[buttons]="['Ok', 'Cancel']"
>
</ion-alert>
<ion-action-sheet
trigger="open-action-sheet"
header="Action Sheet"
[buttons]="['Button A', 'Button B']"
>
</ion-action-sheet>
<ion-loading
trigger="open-loading"
message="This is a loading message"
[backdropDismiss]="true"
></ion-loading>
<ion-toast
trigger="open-toast"
message="This is a toast message"
[buttons]="['Button']"
></ion-toast>
<ion-picker
trigger="open-picker"
[columns]="pickerColumns"
[buttons]="pickerButtons"
></ion-picker>
</ion-content>

View File

@@ -0,0 +1,31 @@
import { Component } from "@angular/core";
/**
* Validates that inline overlays will will display correctly.
*/
@Component({
selector: 'app-overlays-inline',
templateUrl: 'overlays-inline.component.html'
})
export class OverlaysInlineComponent {
public pickerButtons = [{ text: 'Ok' }, { text: 'Cancel', role: 'cancel' }];
public pickerColumns = [
{
name: 'Colors',
options: [
{
text: 'Red',
value: 'red',
},
{
text: 'Blue',
value: 'blue',
},
{
text: 'Green',
value: 'green',
},
],
},
];
}

View File

@@ -0,0 +1,11 @@
import { CommonModule } from "@angular/common";
import { NgModule } from "@angular/core";
import { IonicModule } from "@ionic/angular";
import { OverlaysInlineRoutingModule } from "./overlays-inline-routing.module";
import { OverlaysInlineComponent } from "./overlays-inline.component";
@NgModule({
imports: [CommonModule, IonicModule, OverlaysInlineRoutingModule],
declarations: [OverlaysInlineComponent],
})
export class OverlaysInlineModule { }

View File

@@ -0,0 +1 @@
export * from './popover-inline.module';

View File

@@ -0,0 +1,14 @@
import { NgModule } from "@angular/core";
import { RouterModule } from "@angular/router";
import { PopoverInlineComponent } from "./popover-inline.component";
@NgModule({
imports: [RouterModule.forChild([
{
path: '',
component: PopoverInlineComponent
}
])],
exports: [RouterModule]
})
export class PopoverInlineRoutingModule { }

View File

@@ -0,0 +1,13 @@
<ion-button (click)="openPopover(popover)">Open Popover</ion-button>
<ion-popover #popover>
<ng-template>
<ion-content>
<ion-list>
<ion-item *ngFor="let item of items">
<ion-label>{{ item }}</ion-label>
</ion-item>
</ion-list>
</ion-content>
</ng-template>
</ion-popover>

View File

@@ -0,0 +1,26 @@
import { Component } from "@angular/core";
import { IonPopover } from "@ionic/angular";
/**
* Validates that inline popovers will correctly display
* dynamic contents that are updated after the modal is
* display.
*/
@Component({
selector: 'app-popover-inline',
templateUrl: 'popover-inline.component.html'
})
export class PopoverInlineComponent {
items: string[] = [];
openPopover(popover: IonPopover) {
popover.present();
setTimeout(() => {
this.items = ['A', 'B', 'C', 'D'];
}, 1000);
}
}

View File

@@ -0,0 +1,11 @@
import { CommonModule } from "@angular/common";
import { NgModule } from "@angular/core";
import { IonicModule } from "@ionic/angular";
import { PopoverInlineRoutingModule } from "./popover-inline-routing.module";
import { PopoverInlineComponent } from "./popover-inline.component";
@NgModule({
imports: [CommonModule, IonicModule, PopoverInlineRoutingModule],
declarations: [PopoverInlineComponent],
})
export class PopoverInlineModule { }

View File

@@ -0,0 +1,39 @@
<ion-header>
<ion-toolbar>
<ion-title>
Providers Test
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<p>
isLoaded: <span id="is-loaded">{{isLoaded}}</span>
</p>
<p>
isReady: <span id="is-ready">{{isReady}}</span>
</p>
<p>
isResumed: <span id="is-resumed">{{isResumed}}</span>
</p>
<p>
isPaused: <span id="is-paused">{{isPaused}}</span>
</p>
<p>
isResized: <span id="is-resized">{{isResized}}</span>
</p>
<p>
isTesting: <span id="is-testing">{{isTesting}}</span>
</p>
<p>
isDesktop: <span id="is-desktop">{{isDesktop}}</span>
</p>
<p>
isMobile: <span id="is-mobile">{{isMobile}}</span>
</p>
<p>
keyboardHeight: <span id="keyboard-height">{{keyboardHeight}}</span>
</p>
<p>
queryParams: <span id="query-params">{{queryParams}}</span>
</p>
</ion-content>

View File

@@ -0,0 +1,85 @@
import { Component, NgZone } from '@angular/core';
import {
Platform, ModalController, AlertController, ActionSheetController,
PopoverController, ToastController, PickerController, MenuController,
LoadingController, NavController, DomController, Config
} from '@ionic/angular';
@Component({
selector: 'app-providers',
templateUrl: './providers.component.html',
})
export class ProvidersComponent {
isLoaded = false;
isReady = false;
isResumed = false;
isPaused = false;
isResized = false;
isTesting?: boolean = undefined;
isDesktop?: boolean = undefined;
isMobile?: boolean = undefined;
keyboardHeight = 0;
queryParams = '';
constructor(
actionSheetCtrl: ActionSheetController,
alertCtrl: AlertController,
loadingCtrl: LoadingController,
menuCtrl: MenuController,
pickerCtrl: PickerController,
modalCtrl: ModalController,
platform: Platform,
popoverCtrl: PopoverController,
toastCtrl: ToastController,
navCtrl: NavController,
domCtrl: DomController,
config: Config,
zone: NgZone
) {
// test all providers load
if (
actionSheetCtrl && alertCtrl && loadingCtrl && menuCtrl && pickerCtrl &&
modalCtrl && platform && popoverCtrl && toastCtrl && navCtrl && domCtrl && config
) {
this.isLoaded = true;
}
// test platform ready()
platform.ready().then(() => {
NgZone.assertInAngularZone();
this.isReady = true;
});
platform.resume.subscribe(() => {
console.log('platform:resume');
NgZone.assertInAngularZone();
this.isResumed = true;
});
platform.pause.subscribe(() => {
console.log('platform:pause');
NgZone.assertInAngularZone();
this.isPaused = true;
});
platform.resize.subscribe(() => {
console.log('platform:resize');
NgZone.assertInAngularZone();
this.isResized = true;
});
const firstQuery = platform.getQueryParam('firstParam');
const secondQuery = platform.getQueryParam('secondParam');
this.queryParams = `firstParam: ${firstQuery}, firstParam: ${secondQuery}`;
this.isDesktop = platform.is('desktop');
this.isMobile = platform.is('mobile');
// test config
this.isTesting = config.getBoolean('_testing');
this.keyboardHeight = config.getNumber('keyboardHeight');
zone.runOutsideAngular(() => {
document.dispatchEvent(new CustomEvent('pause'));
document.dispatchEvent(new CustomEvent('resume'));
window.dispatchEvent(new CustomEvent('resize'));
});
}
}

View File

@@ -0,0 +1,17 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button></ion-back-button>
</ion-buttons>
<ion-title>router-link page</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<p>ngOnInit: <span id="ngOnInit">{{onInit}}</span></p>
<p>canGoBack: <span id="canGoBack">{{canGoBack}}</span></p>
<p>ionViewWillEnter: <span id="ionViewWillEnter">{{willEnter}}</span></p>
<p>ionViewDidEnter: <span id="ionViewDidEnter">{{didEnter}}</span></p>
<p>ionViewWillLeave: <span id="ionViewWillLeave">{{willLeave}}</span></p>
<p>ionViewDidLeave: <span id="ionViewDidLeave">{{didLeave}}</span></p>
</ion-content>

View File

@@ -0,0 +1,52 @@
import { Component, OnInit, NgZone } from '@angular/core';
import { IonRouterOutlet, ViewDidEnter, ViewDidLeave, ViewWillLeave } from '@ionic/angular';
@Component({
selector: 'app-router-link-page',
templateUrl: './router-link-page.component.html',
})
export class RouterLinkPageComponent implements OnInit, ViewWillLeave, ViewDidEnter, ViewWillLeave, ViewDidLeave {
onInit = 0;
willEnter = 0;
didEnter = 0;
willLeave = 0;
didLeave = 0;
canGoBack: boolean | null | undefined = null;
constructor(
private ionRouterOutlet: IonRouterOutlet
) {}
ngOnInit() {
NgZone.assertInAngularZone();
this.canGoBack = this.ionRouterOutlet.canGoBack();
this.onInit++;
}
ionViewWillEnter() {
if (this.onInit !== 1) {
throw new Error('ngOnInit was not called');
}
if (this.canGoBack !== this.ionRouterOutlet.canGoBack()) {
throw new Error('canGoBack() changed');
}
NgZone.assertInAngularZone();
this.willEnter++;
}
ionViewDidEnter() {
if (this.canGoBack !== this.ionRouterOutlet.canGoBack()) {
throw new Error('canGoBack() changed');
}
NgZone.assertInAngularZone();
this.didEnter++;
}
ionViewWillLeave() {
NgZone.assertInAngularZone();
this.willLeave++;
}
ionViewDidLeave() {
NgZone.assertInAngularZone();
this.didLeave++;
}
}

View File

@@ -0,0 +1,12 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button></ion-back-button>
</ion-buttons>
<ion-title>Router Page 2</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<ion-button routerLink="/router-link-page3" id="goToPage3">Go to Page 3</ion-button>
</ion-content>

View File

@@ -0,0 +1,15 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-router-link-page2',
templateUrl: './router-link-page2.component.html'
})
export class RouterLinkPage2Component implements OnInit {
constructor() { }
ngOnInit() {
console.log('ngOnInit')
}
}

View File

@@ -0,0 +1,12 @@
<ion-header>
<ion-toolbar color="dark">
<ion-buttons slot="start">
<ion-back-button defaultHref="/?token=ABC#fragment" id="goBackFromPage3"></ion-back-button>
</ion-buttons>
<ion-title>Router Page 3</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
Page 3
</ion-content>

View File

@@ -0,0 +1,15 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-router-link-page3',
templateUrl: './router-link-page3.component.html'
})
export class RouterLinkPage3Component implements OnInit {
constructor() { }
ngOnInit() {
console.log('ngOnInit')
}
}

View File

@@ -0,0 +1,39 @@
<ion-header>
<ion-toolbar>
<ion-title>
Router Link Test
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<p>ngOnInit: <span id="ngOnInit">{{onInit}}</span></p>
<p>ionViewWillEnter: <span id="ionViewWillEnter">{{willEnter}}</span></p>
<p>ionViewDidEnter: <span id="ionViewDidEnter">{{didEnter}}</span></p>
<p>ionViewWillLeave: <span id="ionViewWillLeave">{{willLeave}}</span></p>
<p>ionViewDidLeave: <span id="ionViewDidLeave">{{didLeave}}</span></p>
<p>Change Detections: <span id="counter">{{counter()}}</span></p>
<p>
<ion-button routerLink="/router-link-page" expand="block" color="dark" id="routerLink">ion-button[routerLink]
</ion-button>
<ion-button routerLink="/router-link-page" routerDirection="root" expand="block" color="dark" id="routerLink-root">
ion-button[routerLink] (direction:root)</ion-button>
<ion-button routerLink="/router-link-page" routerDirection="back" expand="block" color="dark" id="routerLink-back">
ion-button[routerLink] (direction:back)</ion-button>
</p>
<p><a [routerLink]="null">null router link</a></p>
<p><a routerLink="/router-link-page" id="a">a[routerLink]</a></p>
<p><a routerLink="/router-link-page" routerDirection="root" id="a-root">a[routerLink] (direction:root)</a></p>
<p><a routerLink="/router-link-page" routerDirection="back" id="a-back">a[routerLink] (direction:back)</a></p>
<p><button (click)="navigate()" id="button">navigate</button></p>
<p><button (click)="navigateForward()" id="button-forward">navigateForward</button></p>
<p><button (click)="navigateRoot()" id="button-root">navigateForward</button></p>
<p><button (click)="navigateBack()" id="button-back">navigateBack</button></p>
<p><button id="queryParamsFragment" routerLink="/router-link-page2/MyPageID==" [queryParams]="{ token: 'A&=#Y' }"
fragment="myDiv1">Query Params and Fragment</button></p>
</ion-content>

View File

@@ -0,0 +1,68 @@
import { Component, NgZone, OnInit } from '@angular/core';
import { NavController, ViewDidEnter, ViewDidLeave, ViewWillEnter, ViewWillLeave } from '@ionic/angular';
import { Router } from '@angular/router';
@Component({
selector: 'app-router-link',
templateUrl: './router-link.component.html',
})
export class RouterLinkComponent implements OnInit, ViewWillEnter, ViewDidEnter, ViewWillLeave, ViewDidLeave {
onInit = 0;
willEnter = 0;
didEnter = 0;
willLeave = 0;
didLeave = 0;
changes = 0;
constructor(
private navCtrl: NavController,
private router: Router
) {}
navigate() {
this.router.navigateByUrl('/router-link-page');
}
navigateForward() {
this.navCtrl.navigateForward('/router-link-page');
}
navigateBack() {
this.navCtrl.navigateBack('/router-link-page');
}
navigateRoot() {
this.navCtrl.navigateRoot('/router-link-page');
}
counter() {
this.changes++;
return Math.floor(this.changes / 2);
}
ngOnInit() {
NgZone.assertInAngularZone();
this.onInit++;
}
ionViewWillEnter() {
if (this.onInit !== 1) {
throw new Error('ngOnInit was not called');
}
NgZone.assertInAngularZone();
this.willEnter++;
}
ionViewDidEnter() {
NgZone.assertInAngularZone();
this.didEnter++;
}
ionViewWillLeave() {
NgZone.assertInAngularZone();
this.willLeave++;
}
ionViewDidLeave() {
NgZone.assertInAngularZone();
this.didLeave++;
}
}

View File

@@ -0,0 +1,16 @@
import { NgModule } from "@angular/core";
import { RouterModule } from "@angular/router";
import { SearchbarComponent } from "./searchbar.component";
@NgModule({
imports: [
RouterModule.forChild([
{
path: '',
component: SearchbarComponent
}
])
],
exports: [RouterModule]
})
export class SearchbarRoutingModule { }

View File

@@ -0,0 +1,16 @@
<ion-content>
<form [formGroup]="form">
<ion-list>
<ion-item>
<ion-label>Searchbar</ion-label>
<ion-searchbar formControlName="searchbar"></ion-searchbar>
</ion-item>
</ion-list>
</form>
<p>
Form status: <span id="status">{{ form.status }}</span>
</p>
<p>
Form value: <span id="value">{{ form.value | json }}</span>
</p>
</ion-content>

View File

@@ -0,0 +1,16 @@
import { Component } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
@Component({
selector: 'app-searchbar',
templateUrl: 'searchbar.component.html',
})
export class SearchbarComponent {
form = this.fb.group({
searchbar: ['', Validators.required]
})
constructor(private fb: FormBuilder) { }
}

View File

@@ -0,0 +1,21 @@
import { CommonModule } from "@angular/common";
import { NgModule } from "@angular/core";
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
import { IonicModule } from "@ionic/angular";
import { SearchbarRoutingModule } from "./searchbar-routing.module";
import { SearchbarComponent } from "./searchbar.component";
@NgModule({
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
IonicModule,
SearchbarRoutingModule
],
declarations: [
SearchbarComponent
]
})
export class SearchbarModule { }

View File

@@ -0,0 +1,40 @@
import { AfterViewInit, Component, ViewChild } from '@angular/core';
import { IonSlides } from '@ionic/angular';
@Component({
selector: 'app-slides',
templateUrl: './slides.component.html',
})
export class SlidesComponent implements AfterViewInit {
@ViewChild(IonSlides, { static: true }) slides!: IonSlides;
slideIndex = 0;
slideIndex2 = 0;
slidesData: string[] = [];
constructor() { }
ngAfterViewInit() {
this.slides.ionSlideDidChange.subscribe(async () => {
this.slideIndex2 = await this.slides.getActiveIndex();
});
}
addSlides() {
const start = this.slidesData.length + 1;
this.slidesData.push(`Slide ${start}`, `Slide ${start + 1}`, `Slide ${start + 2}`);
}
prevSlide() {
this.slides.slidePrev();
}
nextSlide() {
this.slides.slideNext();
}
async checkIndex() {
this.slideIndex = await this.slides.getActiveIndex();
}
}

View File

@@ -0,0 +1,4 @@
<ion-content>
<p>This is a standalone component rendered from a route.</p>
<ion-button routerLink="/">Return home</ion-button>
</ion-content>

View File

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

View File

@@ -0,0 +1,16 @@
import { NgModule } from "@angular/core";
import { RouterModule } from "@angular/router";
import { TabsGlobalComponent } from "./tabs-global.component";
@NgModule({
imports: [
RouterModule.forChild([
{
path: '',
component: TabsGlobalComponent
}
])
],
exports: [RouterModule]
})
export class TabsGlobalRoutingModule { }

View File

@@ -0,0 +1,17 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button></ion-back-button>
</ion-buttons>
<ion-title>
Global Page
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-content>
<ion-button id="goto-prev-pop" (click)="navCtrl.pop()">Go To Previous</ion-button>
</ion-content>
</ion-content>

View File

@@ -0,0 +1,17 @@
import { Component } from "@angular/core";
import { NavController } from "@ionic/angular";
/**
* This component is used in conjunction with a tabs router-outlet,
* to validate the behavior of different routing APIs (e.g. NavController)
* when leaving and re-entering a router-outlet.
*/
@Component({
selector: 'app-tabs-global',
templateUrl: 'tabs-global.component.html'
})
export class TabsGlobalComponent {
constructor(public navCtrl: NavController) { }
}

View File

@@ -0,0 +1,13 @@
import { NgModule } from "@angular/core";
import { IonicModule } from "@ionic/angular";
import { TabsGlobalRoutingModule } from "./tabs-global-routing.module";
import { TabsGlobalComponent } from "./tabs-global.component";
@NgModule({
imports: [
IonicModule,
TabsGlobalRoutingModule
],
declarations: [TabsGlobalComponent]
})
export class TabsGlobalModule { }

View File

@@ -0,0 +1,21 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { TabsTab3Component } from './tabs-tab3/tabs-tab3.component';
import { TabsTab3NestedComponent } from './tabs-tab3-nested/tabs-tab3-nested.component';
const routes: Routes = [
{
path: '',
component: TabsTab3Component
},
{
path: 'nested',
component: TabsTab3NestedComponent
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class TabsLazyRoutingModule { }

View File

@@ -0,0 +1,17 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TabsLazyRoutingModule } from './tabs-lazy-routing.module';
import { TabsTab3Component } from './tabs-tab3/tabs-tab3.component';
import { IonicModule } from '@ionic/angular';
import { TabsTab3NestedComponent } from './tabs-tab3-nested/tabs-tab3-nested.component';
@NgModule({
declarations: [TabsTab3Component, TabsTab3NestedComponent],
imports: [
CommonModule,
IonicModule,
TabsLazyRoutingModule
]
})
export class TabsLazyModule { }

View File

@@ -0,0 +1,15 @@
<ion-header>
<ion-toolbar>
<ion-title>Tab 3 - Page 2</ion-title>
<ion-buttons slot="start">
<ion-back-button defaultHref="/tabs/lazy"></ion-back-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<p>
<ion-button routerLink="/tabs/account" id="goto-tab1-page1">Go to Tab 1 - Page 1</ion-button>
<ion-button routerLink="/tabs/account/nested/1" id="goto-tab1-page2">Go to Tab 1 - Page 2</ion-button>
</p>
</ion-content>

View File

@@ -0,0 +1,7 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-tabs-tab3-nested',
templateUrl: './tabs-tab3-nested.component.html',
})
export class TabsTab3NestedComponent {}

View File

@@ -0,0 +1,15 @@
<ion-header>
<ion-toolbar>
<ion-title>Tab 3 - Page 1</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<h1>LAZY LOADED TAB</h1>
<p>
<ion-button routerLink="/tabs/account" id="goto-tab1-page1">Go to Tab 1 - Page 1</ion-button>
<ion-button routerLink="/tabs/account/nested/1" id="goto-tab1-page2">Go to Tab 1 - Page 2</ion-button>
<ion-button routerLink="/tabs/lazy/nested" id="goto-tab3-page2">Go to Tab 3 - Page 2</ion-button>
</p>
</ion-content>

View File

@@ -0,0 +1,7 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-tabs-tab3',
templateUrl: './tabs-tab3.component.html',
})
export class TabsTab3Component {}

View File

@@ -0,0 +1,32 @@
import { Component } from "@angular/core";
import { IonicModule } from "@ionic/angular";
/**
* Test purpose: Validates that the tab bar is relocated to the
* correct location when the slot attribute changes or is bound
* to a variable.
*/
@Component({
selector: 'app-tabs-slots',
template: `
<ion-tabs>
<ion-tab-bar [slot]="slot"></ion-tab-bar>
</ion-tabs>
<ion-fab vertical="bottom" horizontal="end">
<button id="set-slot-top" (click)="setSlot('top')">Slot - top</button>
<br />
<button id="set-slot-bottom"(click)="setSlot('bottom')">Slot - bottom</button>
</ion-fab>
`,
imports: [IonicModule],
standalone: true
})
export class TabsSlotsComponent {
slot?: string;
setSlot(newSlot: string) {
this.slot = newSlot;
}
}

View File

@@ -0,0 +1,20 @@
<ion-header>
<ion-toolbar>
<ion-title>Tab 1 - Page 2 ({{id}})</ion-title>
<ion-buttons slot="start">
<ion-back-button></ion-back-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<h1>Welcome to NESTED PAGE {{id}}</h1>
<p>
<ion-button routerLink="/tabs/account" id="goto-tab1-page1">Go to Tab 1 - Page 1</ion-button>
<ion-button routerLink="/tabs/contact" id="goto-tab2-page1">Go to Tab 2 - Page 1</ion-button>
<ion-button routerLink="/tabs-global" id="goto-global">Go to Global Page</ion-button>
<ion-button routerLink="/tabs-global" id="goto-prev" (click)="navCtrl.pop()">Go to Previous Page (NavController).
</ion-button>
<ion-button routerLink="/tabs/account/nested/{{next()}}" id="goto-next">Go to Next</ion-button>
</p>
</ion-content>

View File

@@ -0,0 +1,27 @@
import { ActivatedRoute } from '@angular/router';
import { Component, OnInit } from '@angular/core';
import { NavController } from '@ionic/angular';
@Component({
selector: 'app-tabs-tab1-nested',
templateUrl: './tabs-tab1-nested.component.html',
})
export class TabsTab1NestedComponent implements OnInit {
id: string | null = '';
constructor(
private route: ActivatedRoute,
public navCtrl: NavController
) { }
ngOnInit() {
this.id = this.route.snapshot.paramMap.get('id');
}
next() {
if (this.id === null) {
return '1';
}
return parseInt(this.id, 10) + 1;
}
}

View File

@@ -0,0 +1,28 @@
<ion-header>
<ion-toolbar>
<ion-title>{{title}}</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<h1>Welcome to Tab1</h1>
<ion-segment [(ngModel)]="segment" (ionChange)="segmentChanged($event)">
<ion-segment-button value="one">
<ion-label>One</ion-label>
</ion-segment-button>
<ion-segment-button value="two">
<ion-label>Two</ion-label>
</ion-segment-button>
</ion-segment>
<p>
Segment changed: <span class="segment-changed">{{changed}}</span>
</p>
<p>
<ion-button routerLink="/tabs/account/nested/1" id="goto-tab1-page2">Go to Page 2</ion-button>
<ion-button routerLink="/tabs/account/nested/1" [queryParams]="{search:'hello'}" fragment="fragment"
id="goto-nested-page1-with-query-params">Go to Page 2 with Query Params</ion-button>
<ion-button routerLink="/tabs/lazy/nested" id="goto-tab3-page2">Go to Tab 3 - Page 2</ion-button>
<ion-button routerLink="/nested-outlet/page" id="goto-nested-page1">Go to nested</ion-button>
<ion-button (click)="navCtrl.pop()" id="goto-previous-page">Go to Previous Page</ion-button>
</p>
</ion-content>

View File

@@ -0,0 +1,27 @@
import { Component, NgZone } from '@angular/core';
import { NavController } from '@ionic/angular';
@Component({
selector: 'app-tabs-tab1',
templateUrl: './tabs-tab1.component.html',
})
export class TabsTab1Component {
title = 'ERROR';
segment = 'one';
changed = 'false';
constructor(public navCtrl: NavController) {}
ionViewWillEnter() {
NgZone.assertInAngularZone();
setTimeout(() => {
NgZone.assertInAngularZone();
this.title = 'Tab 1 - Page 1';
});
}
segmentChanged(ev: any) {
console.log('Segment changed', ev);
this.changed = 'true';
}
}

View File

@@ -0,0 +1,25 @@
<ion-header>
<ion-toolbar>
<ion-title>{{title}}</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<h1>Welcome to Tab 2</h1>
<ion-segment [(ngModel)]="segment" (ionChange)="segmentChanged($event)">
<ion-segment-button value="one">
<ion-label>One</ion-label>
</ion-segment-button>
<ion-segment-button value="two">
<ion-label>Two</ion-label>
</ion-segment-button>
</ion-segment>
<p>
Segment changed: <span class="segment-changed">{{changed}}</span>
</p>
<p>
<ion-button routerLink="/tabs/account" id="goto-tab1-page1">Go to Tab 1 - Page 1</ion-button>
<ion-button routerLink="/tabs/account/nested/1" id="goto-tab1-page2">Go to Tab 1 - Page 2</ion-button>
<ion-button routerLink="/nested-outlet/page" id="goto-nested-page1">Go to nested</ion-button>
</p>
</ion-content>

View File

@@ -0,0 +1,24 @@
import { Component, NgZone, OnInit } from '@angular/core';
@Component({
selector: 'app-tabs-tab2',
templateUrl: './tabs-tab2.component.html',
})
export class TabsTab2Component implements OnInit {
title = 'ERROR';
segment = 'two';
changed = 'false';
ngOnInit() {
NgZone.assertInAngularZone();
setTimeout(() => {
NgZone.assertInAngularZone();
this.title = 'Tab 2 - Page 1';
});
}
segmentChanged(ev: any) {
console.log('Segment changed', ev);
this.changed = 'true';
}
}

View File

@@ -0,0 +1,5 @@
#test {
position: absolute;
bottom: 100px;
left: 0;
}

View File

@@ -0,0 +1,46 @@
<ion-tabs (ionTabsDidChange)="tabChanged($event)" (ionTabsWillChange)="tabsWillChange($event)">
<ion-tab-bar>
<ion-tab-button tab="account">
<ion-label>Tab One</ion-label>
<ion-icon name="add"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="contact">
<ion-label>Tab Two</ion-label>
<ion-icon name="logo-ionic"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="lazy">
<ion-label>Tab Three</ion-label>
<ion-icon name="save"></ion-icon>
</ion-tab-button>
</ion-tab-bar>
</ion-tabs>
<ion-fab horizontal="end" vertical="top">
<ion-fab-button id="tabs-state" style="width: 100px;">{{tabsDidChangeCounter}}.{{tabsDidChangeEvent}}</ion-fab-button>
</ion-fab>
<div id="test">
<ul>
<li>
ionTabsWillChange counter: <span id="ionTabsWillChangeCounter">{{ tabsWillChangeCounter }}</span>
</li>
<li>
ionTabsWillChange event: <span id="ionTabsWillChangeEvent">{{ tabsWillChangeEvent }}</span>
</li>
<li>
ionTabsWillChange selectedTab: <span id="ionTabsWillChangeSelectedTab">{{ tabsWillChangeSelectedTab }}</span>
</li>
</ul>
<ul>
<li>
ionTabsDidChange counter: <span id="ionTabsDidChangeCounter">{{ tabsDidChangeCounter }}</span>
</li>
<li>
ionTabsDidChange event: <span id="ionTabsDidChangeEvent">{{ tabsDidChangeEvent }}</span>
</li>
<li>
ionTabsDidChange selectedTab: <span id="ionTabsDidChangeSelectedTab">{{ tabsDidChangeSelectedTab }}</span>
</li>
</ul>
</div>

View File

@@ -0,0 +1,33 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { IonTabBar } from '@ionic/angular';
@Component({
selector: 'app-tabs',
templateUrl: './tabs.component.html',
styleUrls: ['./tabs.component.css']
})
export class TabsComponent {
tabsDidChangeCounter = 0;
tabsDidChangeEvent = '';
tabsDidChangeSelectedTab? = '';
tabsWillChangeCounter = 0;
tabsWillChangeEvent = '';
tabsWillChangeSelectedTab? = '';
@ViewChild(IonTabBar) tabBar!: IonTabBar;
tabChanged(ev: { tab: string }) {
console.log('ionTabsDidChange', this.tabBar.selectedTab);
this.tabsDidChangeCounter++;
this.tabsDidChangeEvent = ev.tab;
this.tabsDidChangeSelectedTab = this.tabBar.selectedTab;
}
tabsWillChange(ev: { tab: string }) {
console.log('ionTabsWillChange', this.tabBar.selectedTab);
this.tabsWillChangeCounter++;
this.tabsWillChangeEvent = ev.tab;
this.tabsWillChangeSelectedTab = this.tabBar.selectedTab;
}
}

View File

@@ -0,0 +1,27 @@
import { IonicModule } from '@ionic/angular';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { TabsPageRoutingModule } from './tabs.router.module';
import { TabsComponent } from './tabs.component';
import { TabsTab1Component } from '../tabs-tab1/tabs-tab1.component';
import { TabsTab2Component } from '../tabs-tab2/tabs-tab2.component';
import { TabsTab1NestedComponent } from '../tabs-tab1-nested/tabs-tab1-nested.component';
@NgModule({
imports: [
IonicModule,
CommonModule,
FormsModule,
TabsPageRoutingModule
],
declarations: [
TabsComponent,
TabsTab1Component,
TabsTab2Component,
TabsTab1NestedComponent
]
})
export class TabsPageModule {}

View File

@@ -0,0 +1,52 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { TabsComponent } from './tabs.component';
import { TabsTab1NestedComponent } from '../tabs-tab1-nested/tabs-tab1-nested.component';
import { TabsTab1Component } from '../tabs-tab1/tabs-tab1.component';
import { TabsTab2Component } from '../tabs-tab2/tabs-tab2.component';
const routes: Routes = [
{
path: '',
component: TabsComponent,
children: [
{
path: 'account',
children: [
{
path: 'nested/:id',
component: TabsTab1NestedComponent
},
{
path: '',
component: TabsTab1Component
}
]
},
{
path: 'contact',
children: [
{
path: 'one',
component: TabsTab2Component
},
{
path: '',
redirectTo: 'one',
pathMatch: 'full'
}
]
},
{
path: 'lazy',
loadChildren: () => import('../tabs-lazy/tabs-lazy.module').then(m => m.TabsLazyModule)
}
]
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class TabsPageRoutingModule {}

View File

@@ -0,0 +1,16 @@
import { NgModule } from "@angular/core";
import { RouterModule } from "@angular/router";
import { TextareaComponent } from "./textarea.component";
@NgModule({
imports: [
RouterModule.forChild([
{
path: '',
component: TextareaComponent
}
])
],
exports: [RouterModule]
})
export class TextareaRoutingModule { }

Some files were not shown because too many files have changed in this diff Show More