Ionic Cookbook

Inhaltsverzeichnis

Routing and Navigation

Basic Angular Router configuration

Create a routing module that is ‘visible’ to all components in your app

With Angular CLI

ng generate module app-routing

With Ionic

ionic start my-app blank --type=angular

Match URL paths to Pages/Components

const routes: Routes = [
    { path: 'home',  loadChildren: './home/home.module#HomePageModule'    },
    { path: 'list',  loadChildren: './list/list.module#ListPageModule'    },
    { path: 'about', loadChildren: './about/about.module#AboutPageModule' }
]

Update routing module imports and exports

app.modules.ts

imports { RouterModule, Routes } from '@angular/routes';
imports: [
    RouterModule.forRooot(routes) # makes module available 'application wide'
]   
exports:[          
    RouterModule
]

Do not forget to import the routing module to you main app module

app.routing.module

import { AppRoutingModule } from './app-routing.module';
imports:[
    AppRoutingModule
]

Add a router-outlet to indicate where the pages will be rendered

app.component.html

<ion-app>
    <ion-router-outlet></ion-router-outlet>
</ion-app>

Basics

src/app/app-routing.module.ts

const routes: Routes = [
   { path: 'hello', component: HelloPage }
];

app.component.html

<ion-app>
  <ion-router-outlet></ion-router-outlet> 
</ion-app>
  // Regular Route
  { path: 'eager', component: MyComponent },

  // Lazy Loaded Route (Page)
  { path: 'lazy', loadChildren: './lazy/lazy.module#LazyPageModule' },

    // Redirect
  { path: 'here', redirectTo: 'there', pathMatch: 'full' }
];
<ion-button href="/hello">Hello</ion-button>
<a routerLink="/hello">Hello</a>

Navigate Programmatically

import { Component } from '@angular/core';
import { Router } from '@angular/router';

@Component({ ... })
export class HomePage {
  constructor(private router: Router) {}

  go() {
    this.router.navigateByUrl('/animals');
  }
}

Navigate to Dynamic URLS

const routes: Routes = [
  // Regular Route
  { path: 'items/:id', component: MyComponent },
];
<ion-button href="/items/abc">ABC</ion-button>
<ion-button href="/items/xyz">XYZ</ion-button>

Extract Data from Routes with ActivatedRoute

When working with dynamic data, you need to extract the params from the URL.

For example, you might want to read from the database when the user navigates to /items/:id, using the ID from the route to make a query.

Angular has an ActivatedRoute service that allows us to grab information from the current route as a plain object or Observable.

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({ ... })
export class ProfileComponent implements OnInit {

  id: string;

  constructor(private route: ActivatedRoute) {}

  ngOnInit() {
    this.id = this.route.snapshot.paramMap.get('id');
  }
}

Or if we need to react to changes, we can subscribe to an Observable.

ngOnInit() {
  this.route.params.subscribe(...);
}

Migrate from Ionic X to Ionic 4 Routing

Set Root

<ion-button href="/support" routerDirection="root">

or in class

this.navCtrl.navigateRoot('/support');

Push

<ion-button href="/products/12" routerDirection="forward">
this.navCtrl.navigateForward('/products/12');

Pop

<ion-button href="/products" routerDirection="backward">
<ion-back-button defaultHref="/products"></ion-back-button>

Navigate backwards programatically:

this.navCtrl.navigateBack('/products');

Routing in Tabs

{
  path: 'contact',
  outlet: 'modal',
  component: ContactModal
}
http://.../(modal:contact)

Lazy Loading

Code Snippets

// home.module.ts
@NgModule({
  imports: [
    IonicModule,
    RouterModule.forChild([{ path: '', component: HomePage }])
  ],
  declarations: [HomePage]
})
export class HomePageModule {}
// app.module.ts
@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    IonicModule.forRoot(),
    RouterModule.forRoot([
      { path: 'home', loadChildren: './pages/home/home.module#HomePageModule' },
      { path: '', redirectTo: 'home', pathMatch: 'full' }
    ])
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

Code Snippet

app-routing.module.ts
const routes: Routes = [
  { path: 'about', loadChildren: './about/about.module#AboutPageModule' },
];
about/about.module.ts
const routes: Routes = [
  { path: '', component: AboutPage },
];

Using Guards

@Injectable({
  providedIn: 'root'
})
export class AuthGuard implements CanActivate {
  constructor(private router: Router) {}

  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): boolean {

    const loggedIn = false; // replace with actual user auth checking logic

    if (!loggedIn) {
      this.router.navigate(['/']);
    }

    return loggedIn;
  }
}
const routes: Routes = [
  { path: 'special', component: SpecialPage, canActivate: [AuthGuard] },
];

Storage

Configuration

import { IonicStorageModule } from '@ionic/storage';

@NgModule({
  declarations: [
    // ...
  ],
  imports: [
    BrowserModule,
    IonicModule.forRoot(MyApp),
    IonicStorageModule.forRoot()
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    // ...
  ],
  providers: [
    // ...
  ]
})
export class AppModule {}
Finally, inject it into any of your components or pages:

import { Storage } from '@ionic/storage';

export class MyApp {
  constructor(private storage: Storage) {
      storage.set('name', 'Max');

      storage.get('age').then((val) => {
          console.log('Your age is',  val);
});
}
}

Code sample

class MyClass {
  constructor(public storage: Storage) {}

  async setData(key, value) {
    const res = await this.storage.set(key, value);
    console.log(res);
  }

  async getData(key) {
    const keyVal = await this.storage.get(key);
    console.log('Key is', keyVal);
  }
}

Storage with Capacitor

import { Plugins } from '@capacitor/core';

const { Storage } = Plugins;

async setObject() {
  await Storage.set({
    key: 'user',
    value: JSON.stringify({
      id: 1,
      name: 'Max'
    })
  });
}

async getObject() {
  const ret = await Storage.get({ key: 'user' });
  const user = JSON.parse(ret.value);
}

async setItem() {
  await Storage.set({
    key: 'name',
    value: 'Max'
  });
}

async getItem() {
  const value = await Storage.get({ key: 'name' });
  console.log('Got item: ', value);
}

async removeItem() {
  await Storage.remove({ key: 'name' });
}

async keys() {
  const keys = await Storage.keys();
  console.log('Got keys: ', keys);
}

async clear() {
  await Storage.clear();
}

Components

Alerts

Code Snippets

showAlert() {
  this.alertCtrl.create({
    message: "Hello There",
    subHeader: "I'm a subheader"
  }).then(alert => alert.present());
}

// Or using async/await

async showAlert() {
  const alert = await this.alertCtrl.create({
    message: "Hello There",
    subHeader: "I'm a subheader"
  });

  await alert.present();
}

Local Notifications

import { Plugins } from '@capacitor/core';
const { LocalNotifications } = Plugins;

LocalNotifications.schedule({
  notifications: [
    {
      title: "Title",
      body: "Body",
      id: 1,
      schedule: { at: new Date(Date.now() + 1000 * 5) },
      sound: null,
      attachments: null,
      actionTypeId: "",
      extra: null
    }
  ]
});

Custom Components

Create custom component

$ ionic generate component components/Sample
> ng generate component components/Sample
CREATE src/app/components/sample/sample.component.scss (0 bytes)
CREATE src/app/components/sample/sample.component.html (25 bytes)
CREATE src/app/components/sample/sample.component.spec.ts (628 bytes)
CREATE src/app/components/sample/sample.component.ts (270 bytes)
UPDATE src/app/components/components.module.ts (621 bytes)
[OK] Generated component!
$ ionic generate module components/Components --flat
> ng generate module components/Components --flat
CREATE src/app/components/components.module.ts (194 bytes)
[OK] Generated module!

Modify selector for component in app/components/sample/sample.component.ts

@Component({
    selector: 'c-sample',
    templateUrl: './c-sample.component.html',
    styleUrls: [ './c-sample.component.scss' ]
})

Rename files for component

cd src/app/components/sample
mv sample.component.html c-sample.component.scss
mv sample.component.html c-sample.component.html
mv sample.component.html c-sample.component.spec.ts
mv sample.component.html c-sample.component.ts

Export created component in app/components/components.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { IonicModule } from '@ionic/angular';

import { SampleComponent } from './sample/sample.component';

@NgModule({
    imports: [ CommonModule, IonicModule.forRoot(), ],
    declarations: [ SampleComponent ],
    exports: [ SampleComponent ],
    entryComponents: [],
})
export class ComponentsModule { }

Add page to display the component

$ ionic generate page pages/Sample
> ng generate page pages/Sample
CREATE src/app/pages/sample/sample.module.ts (543 bytes)
CREATE src/app/pages/sample/sample.page.scss (0 bytes)
CREATE src/app/pages/sample/sample.page.html (133 bytes)
CREATE src/app/pages/sample/sample.page.spec.ts (691 bytes)
CREATE src/app/pages/sample/sample.page.ts (256 bytes)
UPDATE src/app/app-routing.module.ts (539 bytes)
[OK] Generated page!

Add custom component to new page sample.page.html

<ion-content padding>
    <c-sample></c-sample>
</ion-content>

Register components module in sample.module.ts

import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { Routes, RouterModule } from '@angular/router';
import { IonicModule } from '@ionic/angular';

import { ComponentsModule } from 'src/app/components/components.module';
import { SamplePage } from './sample.page';

const routes: Routes = [ { path: '', component: SamplePage } ];

@NgModule({
    declarations: [SamplePage],
    imports: [
        CommonModule,  IonicModule,
        RouterModule.forChild(routes),
        ComponentsModule
    ],
    schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class SamplePageModule {}

Check Routing in app-routing.modules.ts

const routes: Routes = [
  { path: '', redirectTo: 'home', pathMatch: 'full' },
  { path: 'home',   loadChildren: './pages/home/home.module#HomePageModule' },
  { path: 'list',   loadChildren: './pages/list/list.module#ListPageModule' },
  { path: 'sample', loadChildren: './pages/sample/sample.module#SamplePageModule' }
];

Add new page to sidemenu in app.components.ts

  public appPages = [
    { title: 'Home', url: '/home', icon: 'home' },
    { title: 'List', url: '/list', icon: 'list' },
    { title: 'Sample Component', url: '/sample', icon: 'list' }
  ];

Directives

Pipes

HTML Elements

Access HTML Element from Page Class

<div #box></div>
@ViewChild('box') el_box:ElementRef;
box: any;

constructor() {
this.box = this.el_box.nativeElement;

Grabbing Ionic Components with ViewChild

Let’s imagine we have a HomePage component that looks like this and we want to close the menu when an item is clicked.

<ion-menu>
  <!-- with some stuff inside -->
</ion-menu>

Our goal is to access the ion-menu from the TypeScript code so we can call its API methods, like open() and close().

import { Component, ViewChild } from '@angular/core';
import { Menu } from '@ionic/angular';

@Component(...)
export class HomePage {

  @ViewChild(Menu) menu: Menu;

  onDrag() {
    this.menu.close();
  }
}

Shortcut: Use Template Variables

There’s actually a very convenient shortcut to using ViewChild in a component. We never have to leave the HTML by setting a template variable in Angular. In this example we reference the menu component with a hashtag and variable name #mymenu.

<ion-menu #mymenu>
  <!-- with some stuff inside -->

  <ion-item (click)="mymenu.close()"></ion-item>

And we’re done. Much easier then using ViewChild in the TypeScript.

Grabbing Multiple Components with ViewChildren

You might also run into a situation where there are multiple components of the same type on the page, such as multiple FABs:

<ion-fab></ion-fab>
<ion-fab></ion-fab>
<ion-fab></ion-fab>

ViewChildren is almost the same, but it will grab all elements that match this component and return them as an Array.

import { Component, ViewChildren } from '@angular/core';
import { Fab } from '@ionic/angular';

@Component(...)
export class HomePage {

  @ViewChildren(Fab) fabs: Fab[];


  closeFirst() {
    this.fabs[0].close();
  }
}

Now that you know about ViewChild, you should have no problem accessing the API methods found on Ionic’s web components.

Loops in HTML Elements

<ul>
   <li *ngFor="let number of [0,1,2,3,4]">
      {{number}}
   </li>
</ul>
<ul>
  <li *ngFor='#key of [1,2]'>
    {{key}}
  </li>
</ul>
<ul>
  <li *ngFor='#val of "0123".split("")'>{{val}}</li>
</ul>
<ul>
  <li *ngFor='#val of counter(5) ;#i= index'>{{i}}</li>
</ul>

export class AppComponent {
  demoNumber = 5 ;

  counter = Array;

  numberReturn(length){
    return new Array(length);
  }
}

Display Array

<ion-grid class="board">
  <ion-row *ngFor="let r of [0,1,2]">
    <ion-col col-4 class="cell" *ngFor="let c of [0,1,2]" (click)="handle(c+r*3)">
            {{squares[c+r*3]}}
    </ion-col>
  </ion-row>
</ion-grid>

Add function to Button click

<ion-item (click)="onClick($event)">
onClick(ev: any){
	this.log('onClick', 'event=' + ev);
}

Change CSS class on click

Add handler to html element

<a class="btn" (click)='toggleClass($event)'>
    <ion-icon class="icon" name="bluetooth"></ion-icon>
</a>

Import Render2 in page.ts

import { Component, OnInit, Renderer2 } from '@angular/core';
...

constructor(private renderer: Renderer2) { }

Write handler to toggle class

toggleClass(event: any) {
    const classname = 'active';

    if (event.target.classList.contains(classname)) {
        this.renderer.removeClass(event.target, classname);
    } else {
        this.renderer.addClass(event.target, classname);
    }
}

Migrating to Ionic 4

Replace Http Module with HttpClient

Changes in app.module.ts


import { HttpClientModule } from '@angular/common/http';
@NgModule({
    declarations: [AppComponent],
    entryComponents: [],
    imports: [
        ...
        HttpClientModule
        ...
    ],

Changes in service.ts

import { Http } from '@angular/http';
import { HttpClient } from '@angular/common/http';
constructor(public http: Http) { }
constructor(public httpClient: HttpClient) { }