What to expect when migrating to Ionic 4

First Published: 5 May 2019
Updated on: 6 June 2019

Since Ionic 4 was just released I spent a couple of days updating one of my applications to see what I could accomplish. Today I want to share with you a bit of what I found.

The entire upgrade process was painless, since there aren’t many big changes, moving from V3 to V4 didn’t give me a headache as I was expecting, the only thing I’m still testing is the migration from Cordova to Capacitor because I haven’t made the time to read their docs.

You can see the V4 version of the app in its Github repo, so you can check the structural changes and get a bit more familiar with it: Github Repo.

Over the next few paragraphs I’ going to show you what I found, what I liked, what I didn’t like, and how I dealt with it.

Web Components

I think the biggest change that came with v4 is that all of the Ionic components are now standard Web Components. WC push more work to the browser so you require less code, which improves performance and load/startup times :-)

Structural Changes

This is one of my favorite parts, Ionic decoupled from Angular to be able to work with other frameworks, I don’t care about other frameworks and I’ll still use angular, but this decoupling means that you’re basically running an Angular project with the Ionic UI components.

So, why is this my favorite, my company Yuxi Global has a strong Angular team and taking mobile clients it’s becoming easier since the difference is almost none (really, you’re building an Angular app that you’re able to wrap on a native container to deploy to the app stores)

Angular Router

Ionic 3 uses Push/Pop navigation, and Angular’s navigation is URL based, this is the biggest (programming) change for the people that don’t have Angular experience, but if you’re willing to put in the time to get familiar with the router you’ll get the understanding you need in one day.

So, instead of using the NavController you create all of your routes:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AuthGuard } from './services/user/auth.guard';

const routes: Routes = [
  { path: '', redirectTo: 'home', pathMatch: 'full' },
  {
    path: 'home',
    loadChildren: './pages/home/home.module#HomePageModule'
  },
  {
    path: 'event-create',
    loadChildren:
      './pages/event-create/event-create.module#EventCreatePageModule'
  },
  {
    path: 'event-detail/:id',
    loadChildren:
      './pages/event-detail/event-detail.module#EventDetailPageModule'
  }
];
@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule {}

And then add them to the app.module.ts file so that they’re available throughout the application.

import { AppRoutingModule } from './app-routing.module';

@NgModule({
  ...
  imports: [..., AppRoutingModule],
  ...
})
export class AppModule {}

Then, whenever we need to navigate to a new page, we inject the router into the constructor, call the method, and pass the URL:

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

constructor( private router: Router) {}

navigateToXPage(): void {
  this.router.navigateByUrl('home');
}

Or you can use it from the HTML file with the routerLink property:

<ion-button expand="block" color="primary" routerLink="/event-create">
  Create a new Event
</ion-button>

Lazy Loading Pages

You probably noted that my router file isn’t matching URLs to components, that’s because it’s using lazy loading, it’s matching URLs to a module, and only if that module is called then the component gets loaded

{
  path: 'home',
  loadChildren: './pages/home/home.module#HomePageModule'
},

This one was a cool thing I didn’t know existed (what can I say, I started using Ionic without Angular knowledge), we can create an authentication guard (or other types of guards) to only allow components to activate if the user meets the Guards criteria.

In this case, I created an authentication guard, if you use the CLI like ionic g guard path_to_guard they’ll create all the boilerplate for you and you only need to create the logic inside.

Guards aren’t difficult to understand, you create a function that returns either true or false, then link it to a route, if the function returns true you activate or load the route, if it doesn’t then you can redirect the user somewhere else or show an error, or whatever you want to use to handle that.

This is how my authentication guard looks like:

import { Injectable } from '@angular/core';
import {
  CanActivate,
  ActivatedRouteSnapshot,
  RouterStateSnapshot,
  Router,
} from '@angular/router';
import { Observable } from 'rxjs';

import * as firebase from 'firebase/app';
import 'firebase/auth';
import { AuthService } from './auth.service';

@Injectable({
  providedIn: 'root',
})
export class AuthGuard implements CanActivate {
  constructor(private router: Router, private authService: AuthService) {}
  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): boolean | Observable<boolean> | Promise<boolean> {

    return new Promise( (resolve, reject) => {
      firebase.auth().onAuthStateChanged( user => {
        if (user) {
          resolve(true);
        } else {
          console.log('User is not logged in');
          this.router.navigate(['/login']);
          resolve(false);
        }
      });
    });
  }
}

Most of that was generated by the CLI, I created the promise that the function is returning. It uses Firebase’s onAuthStateChanged() function to see if there’s a logged in user and returns that user.

If there’s no logged in user I redirect the person to the login page.

To activate that authentication guard you need to go to your routing file and add it to the routes you want to protect:

{
  path: 'home',
  loadChildren: './pages/home/home.module#HomePageModule',
  canActivate: [AuthGuard],
},

Syntax Changes

The syntax changes are annoying, we don’t like changing things that we feel don’t need to be changed, but the Ionic team did those changes for a reason, they’re moving away from framework dependency and all of those are now Web Components.

There are several changes and the full list can be found in this Github repository.

Capacitor

This is not a required change, but I wanted to give it a try to see how easy/difficult it would be to implement and use vs Cordova.

So far I’m liking it, the Camera example required a bit less code than the Cordova version, the one thing I hate is that when Capacitor creates the projects you need to open them through Android Studio or Xcode to run them in your phone. That’s where I miss Cordova’s CLI integration that worked well for me while in development.

Next Steps

I’m going to keep working on the Capacitor integration because I want to create a web fallback, and then will move to upgrade the rest of my apps, I have a few tasks for you if you’re up to the challenge:

  1. Reach out via twitter, you can always find me as @javebratt and let me know what excites you the most (or makes you mad) about the Ionic 4 move.

  2. Go ahead and build something, try version 4, play with it, and let me know what you build, I’m always available for help if you get stuck.