TL;DR: In this article, you will learn about what's new in Ionic 4, changes in the existing features, how to migrate your Ionic 3 app to Ionic 4 app, and about using Ionic with Stencil and Capacitor. Finally, you will create a simple application using Ionic 4 to see the new version of this framework in action. If you need, you can check the final code of the app that you will create on this GitHub repository.
"Learn about what's new in Ionic 4, changes in the existing features, how to migrate your Ionic 3 app to Ionic 4 app, and about using Ionic with Stencil and Capacitor."
Tweet This
Ionic 4 Beta: Learn What's New
The Ionic framework is quite a famous name in the mobile application development area. Ever since this framework came into existence, it's been tightly coupled to Angular. For example, Ionic 1 was built on Angularjs, and Ionic 2 and Ionic 3 are based on Angular2+.
Starting with Ionic 4, things are now different. Ionic components are now built using Web Components with the help of Stencil. What is interesting about Web Components is that you can use them with any framework or even without a framework. That makes it possible to use Ionic with Angular, React, Vue, or whatever other frameworks you can imagine.
Right now, the new (but still beta) version 4 of Ionic is available via the @ionic/core
NPM package. Also, there are Angular bindings available via the @ionic/angular
NPM package. Alongside with that, the official bindings for React and Vue are currently in development.
This article will mostly focus on @ionic/angular
. Here, you will learn how to create new Ionic/Angular applications, make navigation, migrate Ionic3 app to Ionic4 app, and you will also create a demo application using Ionic/Angular.
Ionic 4 and Angular
In this section, you will read about the changes that Ionic 4 brought with respect to Angular. In other words, you will be learning about the changes in @ionic/angular@v4
.
Routing in Ionic 4 and Angular
One of the biggest changes in Ionic 4 is the support of Angular Router. With Ionic 4, you can use @angular/router
to handle the navigation of your Ionic apps. However, keep in mind that Ionic 4 still supports the Push/Pop ion-nav
navigation system.
In a typical Angular application, you have to use router-outlet
component to project the view. In Ionic 4 applications, you will use a version of router-outlet
called as ion-router-outlet
. Basically, you have to add following HTML element in the template where you want to project the view.
<ion-router-outlet></ion-router-outlet>
After adding the ion-router-outlet
component, you will need to create a router config, which is required to configure the navigation paths of your Angular application. This can be done as follows:
let routes:Routes = [
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home',loadChildren:'./pages/home/home.module#HomePageModule' },
{ path: 'user/:id', loadChildren: './pages/user/user.module#UserPageModule' },
{ path: '**', redirectTo: 'home', pathMatch: 'full'},
]
Angular Router parses the router config from top to bottom. The above config basically conveys the follows:
- If users navigate to the
/
route, then your app will redirect them the/home
route. - If users navigate to
/home
route, then your app will load theHomePageModule
. - If users navigate to
/user/:id
route, then your app will load theUserPageModule
. In this case,:id
is a navigation parameter. For example: if users try to reach/users/u101
,u101
is the value:id
will get. - If users open any route which doesn't match any path in your config, your app will redirect them to
/home
.
Note: the
loadChildren
property in a route is used for Lazily Loading an Angular Module. By default, if you generate a page using Ionic CLI, it will be lazily loaded.
Then you have to provide these routes to the RouterModule
in the root @NgModule
of your Angular application, as follows:
// your imports here
@NgModule({
...
imports: [
...
RouterModule.forRoot(routes);
]
})
Now, when your Angular application bootstraps, it will load the default route specified in your Router configuration. But the rest of the navigation will depend upon users' interaction with the app per se. To trigger navigation, you two options: one is declarative, and the other one is imperative.
For example, to define a declarative navigaiton in your Angular application, you can use the routerLink
directive:
<a routerLink="/home">Home</a>
With that, if your users click on the above anchor element, the app will trigger navigation to the /home
route. If you come from previous versions of the Ionic framework, you will probably remember that you used to have nice animation flows that appeared when you were pushing a new view or page to your users. This is still possible by using routerDirection
directive along with routerLink
.
Basically, there are three possible values for this directive:
forward
: It's just like pushing a view to the navigation stack.backward
: It's like popping a view from the navigation stack.root
: It behaves likesetRoot
method of NavController. This means that it will make the page that you are about to navigate to the root page of the application.
For example, to use the forward
option you could have something like this:
<a routerLink="/home" routerDirection="forward">Home<a>
Now, when it comes to define imperative navigation in your app, you can call the navigateForward
, navigateBack
, and navigateRoot
methods of NavController
:
// similar to routerDirection="forward"
this.navCtrl.navigateForward('/user/u101');
// similar to routerDirection="backward"
this.navCtrl.navigateBack('/home');
// similar to routerDirection="root"
this.navCtrl.navigateRoot('/home');
Using Ionic and Angular CLI
With Ionic 4, Ionic CLI projects are almost similar to Angular CLI projects. As a matter of fact, Ionic CLI uses Angular CLI internally.
To use the latest Ionic CLI version, make sure you install it:
npm install -g ionic
Next step, if you were going to create a new app, you would issue commands with starter templates as below (don't really worry about running this now, you will soon create your app):
ionic start myApp blank --type=angular
Note:
--type=angular
tells Ionic CLI to generate an Angular-based project.
When you scaffold your app, you will get a file structure that looks like the following:
e2e
: This is a directory that contains default end-to-end test code and where you can write your own tests.resources
: This is a directory that contains resources like icons and images for your mobile app.src
: This is where you will put the source code of your Ionic application.angular.json
: A file that contains all the configuration of your project. You can configure yourproduction
ordev
environment and lots of the other things. This file makes it possible to use Angular CLI with Ionic projects.config.xml
: The config file for Cordova.ionic.config.json
: The config file used by Ionic CLI.tsconfig.json
: The config file that defines rules for the TypeScript compiler.tslint.json
: The config file that defines rules for the TypeScript linter.
Just like with Angular CLI, after scaffolding your app, you will be able to run a hello-world app by issuing a single command:
ionic serve
Theming in Ionic 4
With Ionic 4, the Ionic team is adopting web standards. In the new version of the framework, all of the Ionic Components are built using Stencil (which means that they are relying on the Web Component spec). With respect to theming, Ionic is using CSS variables.
By default, Ionic comes with nine colors, and each one of these has two extra variants called as shade and tint. You can easily edit the default color values along with their shade and tint variant.
Color Structure
Before you start modifying existing colors in Ionic, you need to understand some concepts. Each default color in Ionic has multiple properties, kind of like having multiple layers of colors. Each of these properties is applied to a different part of a particular component. These properties are as follows:
base
: The main color that will be applied to the component.shade
: A bit darker than the base color and used to add shades.tint
: It's a bit lighter than the base color.contrast
: Contrast color is a color which is easily visible (perceptible) on the base color. For example, if the base is black, then contrast would be white. Basically, contrast is used for showing text on components like button, list, etc.
Following is an excerpt of the default primary color values from an Ionic project:
:root {
--ion-color-primary: #488aff;
--ion-color-primary-rgb: 72,138,255;
--ion-color-primary-contrast: #fff;
--ion-color-primary-contrast-rgb: 255,255,255;
--ion-color-primary-shade: #3f79e0;
--ion-color-primary-tint: #5a96ff;
}
Modifying Existing Color
When you open the /src/theme/variable.scss
file, you can see all nine default colors in that file. You can modify any of the values as shown below:
:root {
--ion-color-primary: #00ff00;
--ion-color-primary-shade: #00fff0;
--ion-color-primary-tint: #00fa00;
}
Above example changes the primary color (along with it's shade
and tint
properties) to #00ff00
for the whole application. You can also change this particular color for a particular component as follows:
.my-button {
--ion-color-primary: #00ff00;
}
Migrating from Ionic 3 to Ionic 4
Migration is always a difficult and painful subject. However, the team behind the Ionic framework worked hard to make things as easy as possible. The following list presents some tasks that you will have to do to migrate from Ionic 3 to Ionic 4:
- Angular Router: As mentioned earlier, you need to migrate your Push/Pop navigation to Angular Router navigation. You can follow the step mentioned above to do so.
- NavParams Service: With Angular Router, you can't use the beloved
NavParams
service, but there are many alternatives to it. One of the best would be using NgRx to manage the state. It's fairly easy to use NgRx to mimic the same behavior of theNavParams
service. However, there is a file inside Ionic 4's GitHub repo which might be hinting atNavParams
debut in Ionic 4. - LifeCycleEvents: Since you will not use the
ion-nav
navigation element, you can't useionViewWillLoad
or similar life cycle events. However, you can use Angular's life cycle hooks in their place. To learn more about Angular lifecycle hooks, take a look here. - Markup: There are lots of changes in markup due to the fact that Ionic uses the Web component spec. It's out of scope of this blog post to talk about all those changes. Nevertheless, it is important to notice that the Ionic team introduced a migration linter for markup that you can use.
Using the Migration Linter
It's definitely fun to learn things and sometimes you need to change things manually. But if at a certain point you can do things automatically, you should opt for it. As mentioned, the Ionic team also introduced a migration linter tool that you can use to migrate your Ionic 3 markup to Ionic 4 markup.
To use it, you will have to install the lint rules:
npm i -D @ionic/v4-migration-tslint
Then you will need to create a file called ionic-migration.json
at the root of your project and paste in the following JSON:
{
"rulesDirectory": ["@ionic/v4-migration-tslint/rules"],
"rules": {
"ion-action-sheet-method-create-parameters-renamed": true,
"ion-alert-method-create-parameters-renamed": true,
"ion-datetime-capitalization-changed": true,
"ion-item-option-method-get-sliding-percent-renamed": true,
"ion-overlay-method-create-should-use-await": true,
"ion-overlay-method-present-should-use-await": true,
"ion-back-button-not-added-by-default": { "options": [true], "severity": "warning" },
"ion-button-attributes-renamed": true,
"ion-button-is-now-an-element": true,
"ion-chip-markup-has-changed": true,
"ion-fab-button-is-now-an-element": true,
"ion-fab-attributes-renamed": true,
"ion-fab-fixed-content": true,
"ion-col-attributes-renamed": true,
"ion-icon-attribute-is-active-removed": true,
"ion-item-is-now-an-element": true,
"ion-item-ion-label-required": true,
"ion-item-attributes-renamed": true,
"ion-item-divider-ion-label-required": true,
"ion-item-options-attribute-values-renamed": true,
"ion-item-option-is-now-an-element": true,
"ion-label-attributes-renamed": true,
"ion-list-header-ion-label-required": true,
"ion-menu-toggle-is-now-an-element": true,
"ion-navbar-is-now-ion-toolbar": true,
"ion-option-is-now-ion-select-option": true,
"ion-radio-attributes-renamed": true,
"ion-radio-slot-required": true,
"ion-radio-group-is-now-an-element": true,
"ion-range-attributes-renamed": true,
"ion-spinner-attribute-values-renamed": true,
"ion-tab-attributes-renamed": true,
"ion-text-is-now-an-element": true,
"ion-buttons-attributes-renamed": true
}
}
Then you can lint your project by running the following command:
npx tslint -c ionic-migration.json -p tsconfig.json
"Ionic 4 will have tools to help you migrate your code automatically."
Tweet This
Note :
npx
is a new utility that comes with NPMv5.2.0
. This tool allows you to run local Node executables from thenode_modules
directory.
Ionic 4 and Stencil
Before you start digging into this section, you should learn a few things about Stencil. Basically speaking, Stencil is a compiler that lets you create UI Components and Web Apps using the Web Component specification. Coding with Stencil is kind of like a mixture of Angular and React. While developing with Stencil, you will use Decorators, JSX, and TypeScript to name a few.
A typical Stencil Component looks like follows:
import { Component, Prop } from '@stencil/core';
@Component({
tag: 'name-component',
styleUrl: 'name-component.scss'
})
export class NameComponent {
@Prop() name: string;
render() {
return (
<p>
Welcome, {this.name}
</p>
);
}
}
Then, to use your new web component, you can just add it to your HTML like any other element:
<name-componet name="Indermohan Singh"></name-component>
Using Ionic with Stencil
You might be wondering, why would I want to read about Stencil in an Ionic article? In other words, how would you benefit from learning about Ionic and Stencil?
The best answer to that question is that Ionic Core is built using the Stencil compiler. That is, they play really nicely with each other. Moreover, Stencil has enough plug-ins that you can use to write a complete application. Also, Stencil comes with an Ionic PWA (Progressive Web App) starter that can be used to create Ionic applications.
As you will see here, it's fairly easy to get started with Ionic and Stencil. To do so, you will need to run the following command to trigger Stencil CLI:
npm init stencil
This command will ask you questions like:
- Pick a starter: First, it will ask you to pick a starter. There is three starters for now:
ionic-pwa
, which is Ionic with the PWA toolkit;app
, a minimal starter to build stencil app or website; andcomponent
, a collection of web components to test the PWA capabilities, select theionic-pwa
option. - Project name: For this, you can add any name that you can come up with.
- Confirm?: Finally, it will ask you to confirm. Press y to confirm.
Now you can run the application as follows:
npm run start
To build the application, you can run the following command:
npm run build
The above command will generate a www
directory with all the files required to deploy or build a mobile application using Capacitor or Cordova.
Using Stencil Plugins with Ionic 4
With Ionic, there are some plug-ins available to do certain things, for example:
@stencil/router
: This is a router plugin for stencil applications that can be used to manage routing in your apps.@stencil/redux
: If you like managing the state of your apps with Redux style, you can do that via this package.
Furthermore, you can use any JavaScript library with Stencil applications, because Stencil components transpile into plain JavaScript web components.
Building Mobile Apps with Capacitor on Ionic 4
Capacitor is the latest kid on the block when it comes to hybrid app development. What is impressive about Capacitor is that you can literally drop it inside any JavaScript project.
Capacitor is a new tool doing the same thing (like Cordova for example) with a different API for creating plugins. But in the end, both things are running your application inside a web view (i.e., an embedded browser instead of a real native app).
Capacitor supports Cordova Plugin. So it's easy to migrate your Cordova application to Capacitor application. To add Capacitor into your front-end application, you can simply go to some app that you have and run the following command:
npm install --save @capacitor/core @capacitor/cli
After installing it, you will have to initialize Capacitor by running this:
npx cap init
Then, you need to add the platform for which you want to build the application. For example, to configure your project to run on Android, you can issue the following command:
npx cap add android
Finally, you need to open the project and run
or build
it via platform's provide IDE. Run the following command to open Android Studio:
npx cap open android
After that, you can use respective platform features to build or run the application.
Note: It's also worth mentioning that you can combine Ionic, Stencil, and Capacitor to create a mobile application. Just follow the above instructions inside your Stencil Ionic PWA app, and you are good to go.
Ionic 4 In Action
In this section, you are going to create a very simple IonicaApplication with a couple of pages. What will be interesting about this app is that some of the content will be publicly available (that is, no authenticated is required) and some are for authenticated users only.
The application that you will build is similar to a contact directory. The first page will have a list of people with their names and avatar picture. Then, when users click on contact, the app will show the details of the contact clicked.
"Start building Ionic 4 apps right now!"
Tweet This
Creating an Auth0 Account
Before you move on and start developing the application, you will need to sign up to Auth0. The idea of using Auth0 is to leverage some state-of-the-art functionality that will give you enough power (and confidence) to focus on what matters the most, your app details. As such, the first thing you will do is to sign up for a free Auth0 account here .
After signing up to your Auth0 account, you will need to create an Auth0 Application to represent your new app. So, move to the Applications page in your Auth0 dashboard and click on Create Application. After clicking on it, Auth0 will show a dialog where you will have to type the name of your application (e.g., "Ionic 4 In Action") and you will have to select the type of application (in this case, you will have to choose Native App). Then, you can press the Create button.
Clicking on this button will redirect you to the configuration details of your app. In this page, you will have to add the following values to the Allowed Web Origins box:
http://localhost:8100
This makes Auth0 accept requests from this URLs. Alongside with that, you will also need to configure the callback URL for the application:
http://localhost:8100/callback
This is the URL that Auth0 will call after users authenticate.
Scaffolding the Ionic Application
First, you need to have the latest Ionic CLI installed, as mentioned earlier. If you haven't done so yet, run the following command:
npm install -g ionic
Now, run the following command to create a new blank project and to move into the project folder:
# scaffold your new app
ionic start ionic-4-demo blank --type=angular
# move into it
cd ionic-4-demo
The first command will ask you a couple of questions:
- Integrate your new app with Cordova to target native iOS and Android? (y/N): Press Y
- Install the free Ionic Pro SDK and connect your app? (Y/n): Press N
Inside your new project, issue the following command to install the auth0-js
library:
npm install --save auth0-js
Also install a CSS library to show some country flags, as follows:
npm install --save flag-icon-css
After that, run the default app using Ionic CLI:
ionic serve
CLI will spin-up the web server and open this app in your default browser. If you take a closer look, you will see that Ionic CLI under the hood is using Angular CLI to run this app.
Creating a Service for your Data
Now that you have scaffolded your Ionic app, you will create a service called DataService
to fetch dummy data to your app. To create this service, run the following command from the project root:
ionic g service services/data/data
This will generate a couple of files in the ./src/app/services/data
directory. Now, open the data.service.ts
file and replace its content with the following:
import {Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {of as observableOf} from 'rxjs';
import {map, tap} from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class DataService {
people: any = [];
constructor(private http: HttpClient) {
}
getPeopleList() {
if (this.people.length) {
return observableOf(this.people);
} else {
return this.http.get('https://randomuser.me/api/?results=20&?seed=foobar')
.pipe(
map((data: any) => data.results),
tap(people => this.people = people),
);
}
}
getPerson(index) {
if (!this.people[index]) {
return this.getPeopleList()
.pipe(
map(people => this.people[index])
);
} else {
return observableOf(this.people[index]);
}
}
}
As you can see, this service is using an online API called Random User Generator to load a list of dummy contacts to your app. This service contains two methods:
getPeopleList
: The method that loads the dummy data and returns a list of 20 random contacts.getPerson
: A method that returns the details of a particular contact.
After creating this service, you will have to create another one. This other service will be responsible for handling your users' sessions and, as such, you will call it AuthService
. To create this service, issue the following command:
ionic g service services/auth/auth
Again, this command will generate a couple of files in the /src/app/services/auth
directory. Now, open the auth.service.ts
file and replace its code with the following:
import {Injectable} from '@angular/core';
import {Router} from '@angular/router';
import {Subject} from 'rxjs';
import * as auth0 from 'auth0-js';
(window as any).global = window;
@Injectable()
export class AuthService {
isLoggedIn$ = new Subject();
isLoggedIn: Boolean = false;
auth0 = new auth0.WebAuth({
clientID: 'YOUR_CLIENT_ID',
domain: 'YOUR_AUTH_DOMAIN',
responseType: 'token id_token',
audience: 'https://YOUR_AUTH_DOMAIN/userinfo',
redirectUri: 'http://localhost:8100/callback',
scope: 'openid'
});
constructor(public router: Router) {
// Check if user is logged In when Initializing
const loggedIn = this.isLoggedIn = this.isAuthenticated();
this.isLoggedIn$.next(loggedIn);
}
public login(): void {
this.auth0.authorize();
}
public handleAuthentication(): void {
this.auth0.parseHash((err, authResult) => {
if (authResult && authResult.accessToken && authResult.idToken) {
window.location.hash = '';
this.setSession(authResult);
const loggedIn = this.isLoggedIn = true;
this.isLoggedIn$.next(loggedIn);
this.router.navigate(['/home']);
} else if (err) {
const loggedIn = this.isLoggedIn = false;
this.isLoggedIn$.next(loggedIn);
this.router.navigate(['/home']);
}
console.log(this.isLoggedIn);
});
}
private setSession(authResult): void {
// Set the time that the Access Token will expire at
const expiresAt = JSON.stringify((authResult.expiresIn * 1000) + new Date().getTime());
localStorage.setItem('access_token', authResult.accessToken);
localStorage.setItem('id_token', authResult.idToken);
localStorage.setItem('expires_at', expiresAt);
}
public logout(): void {
// Remove tokens and expiry time from localStorage
localStorage.removeItem('access_token');
localStorage.removeItem('id_token');
localStorage.removeItem('expires_at');
// Go back to the home route
const loggedIn = this.isLoggedIn = false;
this.isLoggedIn$.next(loggedIn);
}
public isAuthenticated(): boolean {
// Check whether the current time is past the
// Access Token's expiry time
const expiresAt = JSON.parse(localStorage.getItem('expires_at') || '{}');
return new Date().getTime() < expiresAt;
}
}
Note that, before running your app again, you will have to replace YOUR_CLIENT_ID
and both places that show YOUR_AUTH_DOMAIN
with your own Auth0 values. The first value, you will have to replace with the Client ID property of the Auth0 Application you created before. The second value, YOUR_AUTH_DOMAIN
, you will have to replace with the Domain value of the same Auth0 Application.
Besides configuring your app to communicate with Auth0, this service also contains the following methods:
login
: This method initiates the login process and shows an Auth0's hosted login page.handleAuthentication
: This method is called when the authentication process is finished. If the user is successfully logged in, this method callsthis.setSession
so it setsthis.isloggedIn
totrue
and redirect users to the/home
route. If the authentication is not successful, it setsthis.isloggedIn
tofalse
but also redirects to the/home
route. You will have to call this method inside the root component of your application so that it can read the URL hash using Auth0 library to determine the authentication status.setSession
: This method stores user's login information likeaccess_token
,id_token
, andexpires_at
into local storage to facilitate the management of these data.logout
: This method removes all information about logged in user from local storage and setsthis.loggedin
value tofalse
.isAuthenticated
: This method checks if the user is authenticated or not by checking thetoken
expiration date from local storage.
Creating Pipes in Ionic 4
To see pipes in action, you are going to create a very simple pipe to capitalize the first character of words. As such, the first thing you will need to do is to create the pipe:
# from the project root
ionic g pipe pipes/capitalize
The above command will generate a file named capitalize.pipe.ts
inside the ./src/app/pipes/
directory. Open this file and replace its content with this:
import {Pipe, PipeTransform} from '@angular/core';
@Pipe({
name: 'capitalize'
})
export class CapitalizePipe implements PipeTransform {
transform(value: any, args?: any): any {
return this.capitalize(value);
}
capitalize(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
}
The capitalize
method takes a string as an input and returns another string by capitalizing the first character of that string. When you use capitalize
pipe inside Angular Template, it will call the transform method, which will further the call to this capitalize
method from this pipe.
As you will need to use pipes in multiple sub-modules inside your application, it's a good idea to create an extra NgModule
for all the pipes and then import those modules in your other modules. To do so, create a file named pipes.module.ts
inside ./src/app/pipes/
directory and add the following code to it:
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {CapitalizePipe} from './capitalize.pipe';
@NgModule({
imports: [
CommonModule,
],
declarations: [CapitalizePipe],
exports: [CapitalizePipe]
})
export class AppPipesModule { }
Creating Pages in Ionic 4
By Default, the blank ionic project has only one page called HomePage
. To avoid creating a new one and removing this one, you will use that page to show the list of people.
So, replace the content of the ./src/app/home/home.page.ts
with the following:
import { Component, OnInit } from '@angular/core';
import { DataService } from '../services/data/data.service';
import { NavController } from '@ionic/angular';
import { AuthService } from '../services/auth/auth.service';
@Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss'],
})
export class HomePage implements OnInit {
people: Array<any> = [];
constructor(private dataService: DataService, private navCtrl: NavController, private authService: AuthService) {
}
ngOnInit() {
this.dataService.getPeopleList()
.subscribe((people: any) => {
this.people = people;
});
}
openPerson(index) {
this.navCtrl.navigateForward(`/person/${index}`);
}
}
Then, replace the content of the ./src/app/home/home.page.html
file with the following:
<ion-header>
<ion-toolbar color="primary">
<ion-title>
People
</ion-title>
<ion-buttons slot="end">
<ion-button shape="round" fill="outline" color="light" (click)="authService.login()" *ngIf="!authService.isLoggedIn">Login</ion-button>
<ion-button shape="round" fill="outline" color="light" (click)="authService.logout()" *ngIf="authService.isLoggedIn">Logout</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
<ion-item *ngFor="let person of people; let i = index" (click)="openPerson(i)">
<ion-avatar slot="start">
<img [src]="person.picture.thumbnail" />
</ion-avatar>
<ion-label>
<h2>{{ person.name?.title | capitalize }} {{ person.name?.first | capitalize }} {{person.name?.last | capitalize }}</h2>
</ion-label>
</ion-item>
</ion-list>
</ion-content>
Since each page in Ionic is lazily loaded by default, each page comes with its own NgModule
. As such, you need to import the AppPipesModule
you just created in your ./src/app/home/home.module.ts
file, as shown below:
// ... other imports ...
import {AppPipesModule} from '../pipes/pipes.module';
@NgModule({
imports: [
// ... other imports ...
AppPipesModule,
],
// ... the rest of the config ...
})
export class HomePageModule {}
When users open your application, the app will load the home page. This homepage shows a list of people with their names and avatar. Users can click on anyone from the list and, clicking on any item, will trigger the openPerson
method from the HomePage
component. This will make your app load the /person
route with that person's id
as a query parameter.
As such, you will also need to create a new page to show the details of a particular person. You can call this new page as PersonPage
and create it as follows:
# from the project root
ionic g page person
This will generate multiple files related to the PersonPage
inside the ./src/app/person/
directory. Now, open the ./src/app/person/person.page.ts
and replace its contents with this:
import {Component, OnInit} from '@angular/core';
import {ActivatedRoute, ParamMap, Router} from '@angular/router';
import {DataService} from '../services/data/data.service';
import {AuthService} from '../services/auth/auth.service';
import {switchMap} from 'rxjs/operators';
@Component({
selector: 'app-person',
templateUrl: './person.page.html',
styleUrls: ['./person.page.scss'],
})
export class PersonPage implements OnInit {
person: any;
constructor(private dataService: DataService, private authService: AuthService,
private route: ActivatedRoute,
private router: Router) {
}
ngOnInit() {
this.route.paramMap
.pipe(
switchMap((params: ParamMap) => this.dataService.getPerson(params.get('id')))
).subscribe(person => {
this.person = person;
});
}
}
Also, replace the contents of the ./src/app/person/person.page.html
file with the following:
<ion-header>
<ion-toolbar color="secondary">
<ion-buttons slot="start">
<ion-back-button></ion-back-button>
</ion-buttons>
<ion-title>Person</ion-title>
<ion-buttons slot="end">
<ion-button routerLink="/home" routerDirection="root">Switch User</ion-button>
<ion-button shape="round" fill="outline" color="light" (click)="authService.login()" *ngIf="!authService.isLoggedIn">Login</ion-button>
<ion-button shape="round" fill="outline" color="light" (click)="authService.logout()" *ngIf="authService.isLoggedIn">Logout</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-card *ngIf="person">
<ion-img [src]="person.picture?.large"></ion-img>
<ion-card-content>
<ion-card-title>{{ person.name?.title | capitalize }} {{ person.name?.first | capitalize }} {{ person.name?.last | capitalize }}</ion-card-title>
<div padding *ngIf="!authService.isLoggedIn">
<ion-button color="secondary" shape="round" fill="outline" (click)="authService.login()">Login to See More Detail</ion-button>
</div>
<div padding [hidden]="!authService.isLoggedIn">
<ion-item icon-left>
<span ngClass="flag-icon flag-icon-{{person.nat | lowercase}}"></span>
<ion-icon slot="start" [name]="person.gender"></ion-icon>
</ion-item>
<ion-item icon-left>
<ion-icon slot="start" name="call"></ion-icon>
{{ person.phone }}
</ion-item>
<ion-item text-wrap>
<ion-icon slot="start" name="mail"></ion-icon>
{{ person.email }}
</ion-item>
<ion-button color="secondary" shape="round" fill="outline" (click)="authService.logout()">Logout</ion-button>
</div>
</ion-card-content>
</ion-card>
</ion-content>
Although the code of this new page is lengthy, you can see that it is not making anything too complex. When users click on someone from the list of people, the will open the PersonPage
. Then, on this page, users will be able to see information about that person. Some information is hidden and can be seen after logging in ([hidden]="!authService.isLoggedIn"
).
The last thing you will need to do in relation to your PersonPage
is to import the AppPipesModule
you created before in the ./src/app/person/person.module.ts
file, as shown below:
// ... other imports ...
import {AppPipesModule} from '../pipes/pipes.module';
@NgModule({
imports: [
// ... other imports ...
AppPipesModule,
],
// ... the rest of the config ...
})
export class PersonPageModule {}
Importing the Flag Icon Library
Since you want to show some nice country flags alongside with each person's detail, you will have to import this library inside your global.scss
file. To do so, open this file and add the following line into it:
@import "~flag-icon-css/css/flag-icon.min.css";
Handling Authentication
After authentication requests are processed, Auth0 redirects users back to your application. Alongside with the user, it sends back authentication data via URL hashes. You need to parse that data and save it somewhere. As you probably remember, there is a method called handleAuthentication
in the AuthService
to do this. Now, you just have to call this method inside the constructor of app.component.ts
, as shown below:
// ... other import statements ...
import { AuthService } from './services/auth/auth.service';
// ... @Component definition ...
export class AppComponent {
constructor(
private platform: Platform,
private splashScreen: SplashScreen,
private statusBar: StatusBar,
private authService: AuthService
) {
this.initializeApp();
this.authService.handleAuthentication();
}
// ... initializeApp ...
}
Now, whenever you start a new project with Ionic CLI, it generates a module called app-routing.module.ts
for handling routing in your app. This file contains all the routing configuration. When you generate a page, CLI also adds that page to the routing configuration in this file. As such, you should have following in the app-routing.module.ts
file:
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
const routes: Routes = [
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home', loadChildren: './home/home.module#HomePageModule' },
{ path: 'person', loadChildren: './person/person.module#PersonPageModule' },
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
To make your PersonPage
support query parameters, modify the path: 'person'
route in the above file, as follows:
{ path: 'person/:id', loadChildren: './person/person.module#PersonPageModule' },
Then, to wrap things up, you will have to update the app.module.ts
file (which is located inside the /src/app/
directory) to tie everything together. In this file, you will need to add all the services, pipes, and pages that you created into it so that Angular can recognize them:
import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {RouteReuseStrategy} from '@angular/router';
import {HttpClientModule} from '@angular/common/http';
import {IonicModule, IonicRouteStrategy} from '@ionic/angular';
import {SplashScreen} from '@ionic-native/splash-screen/ngx';
import {StatusBar} from '@ionic-native/status-bar/ngx';
import {AppComponent} from './app.component';
import {AppRoutingModule} from './app-routing.module';
import {AuthService} from './services/auth/auth.service';
import {DataService} from './services/data/data.service';
@NgModule({
declarations: [AppComponent],
entryComponents: [],
imports: [BrowserModule, HttpClientModule, IonicModule.forRoot(), AppRoutingModule],
providers: [
StatusBar,
SplashScreen,
AuthService,
DataService,
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy }
],
bootstrap: [AppComponent]
})
export class AppModule {}
Now you can run the application as follows:
ionic serve
Note: If you get an error like, CapitalizePipe is declared in two modules, it's because, when you create a pipe using Ionic CLI, it also adds that pipe inside the
app.module.ts
file. In this case, you have to removeCapitalizePipe
from theapp.module.ts
file.
Now, if you open the app in your browser (which actually Ionic does by default for you), you will see the following screen:
"I just built my first Ionic 4 app."
Tweet This
Conclusion
In this post, you learned about what's new in Ionic 4, changes from Ionic 3, and how to migrate to the new version. After that, you learned about using Stencil and Capacitor along with Ionic to create mobile applications and, finally, you created and secured a simple Ionic 4 app. To learn more about Ionic 4, check out the official Ionic 4 documentation.