My first encounter with Ionic 4

First Published: 11 July 2018
Updated on: 24 September 2018

Over the weekend I started migrating the EventManager tutorial application to Ionic 4 and replacing Cordova with Capacitor, today I want to share with you a bit of what I found.

The entire process has been really fast (I can’t code on side-projects for several hours at a time so if I say something took me around 3 days it means maybe 4 or 5 hours split on those 4 days) 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.

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

So, why is this my favorite, my company 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 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 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:

  • Leave a comment below letting me know what excites you the most (or makes you mad) about the Ionic 4 move.
  • If you’re like me and jumped to Ionic without a strong Angular knowledge not to worry, I took Todd Motto’s course and managed to get very familiar with angular in a couple of weeks.

It’s by far the most complete Angular course out there.

If you want to take a deeper dive on Ionic + Firestore you should go through my FREE course: Build your first Firestore powered Ionic app.