Upgrading an Ionic 3 application to Ionic 4

Muhammad Ahsan Ayaz
Modus Create: Front End Development
8 min readJun 21, 2018

Ionic is one of the coolest frameworks out there for developing hybrid mobile apps and Progressive Web Applications (PWAs). As an Ionic partner, we have been following Ionic’s development closely and are very excited about the upcoming major release of the framework, Ionic 4. One of the main reasons is Ionic v4 is based on Stencil, which generates standard Web Components already supported in most major browsers today. For non-supported browsers, there’s a really small and robust polyfill that makes the usage of Web Components seamless. If you want to know more about Stencil, read our previous post.
Ionic v4 is a ground-up rewrite with a focus on being independent of any particular framework. Ionic v4 apps can be bootstrapped with Angular and support for other frameworks like React is planned.
Thankfully, migration from Ionic v3 to v4 is fairly straightforward and nothing like the painful upgrade from v1 to v2. In this article, we’ll show you how we can migrate an Ionic v3 app to v4. The code is already up on https://github.com/ModusCreateOrg/ion-meetups.

Setting up Ionic 4 project and moving our code

First off, install the latest version of Ionic and Cordova:

npm install -g ionic@rc cordova
// OR
yarn add --global ionic@rc cordova

Next, initialize a new Ionic 4 project

ionic start ion-meetups tabs --type=angular

Initialising a new project is the recommended way of migrating projects from v3 to v4. That way, we get all the supporting files, directory structure and a package.json compatible with v4 out-of-box. Notice the --type=angular? It specifies that our Ionic project will be based on Angular. Going forward, this flag will allow bootstrapping projects using other frameworks like React, Vue or vanilla Javascript with Ionic. However, at the time of writing this blog post, only Angular is supported with the --type flag.
Looking at the directory structure of projects, we note our first change from v3. All the pages, providers, etc. have moved to /src/app. This is in sync with how ng-cli bootstraps new projects. Keeping with convention, we move our code as follows:

/src/pages -> /src/app/pages
/src/models -> /src/app/models
/src/providers -> /src/app/providers

This pattern applies for any other custom directories or files we may have directly under /src in v3. All of them become citizens of /src/app in v4. In the following image, the directory structures are for v3 on the left and v4 on the right.

Including third party modules

If your v3 application depends upon third-party modules like moment etc., make sure to install them in the v4 project too.

Updating our imports

We need to replace ionic-angular with @ionic/angular across the project.
@IonicPage isn’t valid in v4. We can remove it from our project – both from the imports and the decorator from our pages.
We also replace any imports in NgModule like IonicPageModule.forRoot(...) or similar with a simple IonicModule. With v4, Ionic has moved to using a platform specific router instead of the NavController. Since we’re building an Angular project, we’re going to use the Angular Router. Updates to routing are possibly the biggest change in v4, so let’s talk about that next.

Changes to routing

Ionic v4 is the biggest update to the framework yet. It makes Ionic framework agnostic — we can use it with any framework out there, or with vanilla Javascript, if we so wish.
Beginning with Ionic v4, it is recommended to use the router provided by the underlying framework; and in our case, Angular.
Migrating our applications from Ionic router to Angular router is a large change in v4, and we’ve compiled a list of things that have changed with routing to make the switch easier.
In Ionic v3, we set up routes using IonicPageModule. In v4, we follow the familiar “angular way” of setting routes. Here’s a sample routing module for the tabs page, with a single child route being lazy loaded.

import { NgModule } from '@angular/core';
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { TabsPage } from './tabs.page';
const routes: Routes = [
{
path: 'tabs',
component: TabsPage,
children: [
{
path: 'about',
outlet: 'about',
loadChildren: '../about/about.module#AboutModule'
}
]
},
{ path: '', redirectTo: '/tabs/(about:about)', pathMatch: 'full' }
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class TabsRoutingModule { }

Further, here’s the about.module.ts, which is being lazily loaded into this tab route.

import { NgModule } from '@angular/core';
import { NgModule } from '@angular/core';
import { RouterModule, Route } from '@angular/router';
import { IonicModule } from '@ionic/angular';
import { AboutPage } from './about.page';
const routes: Array = [
{
path: '',
component: AboutPage,
outlet: 'about'
}
];
@NgModule({
declarations: [AboutPage],
entryComponents: [AboutPage],
imports: [IonicModule, RouterModule.forChild(routes)]
})
export class AboutModule { }

If you’ve not seen the outlet route property before, it enables secondary routing. It’s useful when you have more than one routing outlet on a page, and want to control which components/pages go into which outlet. To find out more about secondary routes, visit official Angular docs.
With the routes setup, we need to update the code around navigation and params in our components. We use Router instead of NavController for routing, and ActivatedRoute instead of NavParams to fetch navigation params (data passed during navigation in v4). Both are imported from @angular/router.
Following is how we navigated in v3:

this.navCtrl.push('user-detail-page', {userEmail: user.email});

This gets updated in v4 as:

this.router.navigateByUrl(`tabs/(users:user-detail/${user.email}`);

There’s more than one way to navigate in v4, all thanks to the Angular router. We can find out more about routing in Angular’s official routing docs.
To access params, we use Angular’s ActivatedRoute provider as follows:

constructor(
private route: ActivatedRoute
) {
this.route.params.pipe(first()).subscribe(params => {
// access individual parameters as params.id or params.email
});
}

Lastly, let’s look at how we set up router outlets in our templates, where we want our routes to load. Here’s what a tab component looks like in v3:

<ion-tabs>
<ion-tab [root]="tab3Root" tabTitle="About" tabIcon="information-circle"></ion-tab>
</ion-tabs>

Here’s what an equivalent v4 tab component template will look like:

<ion-tabs>
<ion-tab label="About" icon="information-circle" href="/tabs/(about:about)">
<ion-router-outlet name="about"></ion-router-outlet>
</ion-tab>
</ion-tabs>

Notice how ion-tab is just a wrapper around the actual ion-router-outlet. Unlike in v3, ion-tab itself isn’t responsible for any routing here.
With that, we’ve covered the 3 aspects of routing in v4 – setting routes up in module and navigating using component logic and template. Obviously, the entire Angular router API is available to us for use in Ionic now.
Note: If you have an Ionic v3 web app, you may want to preserve compatibility with the old URLs; i.e. for existing shared links and existing users having bookmarks etc. We wrote a RoutesFallbackService to handle the old URLs compatibility. Feel free to use the code snippet.

Changes to styles encapsulation

In v3, all of the page styles were encapsulated in the styles files under the page element tag. For instance, for AboutPage, we had something like this in the about.scss file:

page-about {
.about-page {
&__contributors {
.details {
margin-left: 10px;
}
}
}
}

Ionic v3 did magic behind the scenes for keeping the styles encapsulated. However, this does not work in v4 because of Angular’s own view encapsulation methods. So you cannot style the about-page element within the about.scss file.
If you did not have any styles for the Host Element in v3, you could remove the about-pageselector for v4. But if you did have some styles earlier, you can use the :host selector as below in v4 styles:

:host {
.about-page {
&__contributors {
.details {
margin-left: 10px;
}
}
}
}

You can read more about Angular component styles in the Angular docs.

Changes to providers / services

In v3, when we created a provider, it would go in the providers folder and the provider was added to the app.module.ts file. In v4, when you create a service for Angular, you do it using the ng g service command. In v3, the services were provided into app.module.ts. In v4, the services contain some metadata information in the @Injectable decorator introduced in Angular v6, as below:

@Injectable({
providedIn: 'root'
})
export class MyService {
}

What else changed?

There are also a few relatively minor changes to various Ionic entities — attributes for components, methods etc. It is recommended to consult the official Ionic docs whenever you run into problems with the API. Let’s review all the updates we did for our v4 upgrade of the ion-meetups app.
[tabIcon] and [tabTitle] attributes for element are now [icon] and [label], respectively. Instead of providing a [root] input with a component or locator string, we now set [href] to the URL of the tab’s root component directly. Example:

<ion-tab label="About" icon="information-circle" href="/tabs/(about:about)">
</ion-tab>

Navbar isn’t a thing in v4. We can use the more general toolbar to do everything done with a navbar. For most apps, this change is as small as updating the HTML tags from to . Here’s a more full-fledged example from our app:

<ion-toolbar color="primary">
<ion-buttons slot="start">
<ion-back-button text="Events" defaultHref="/tabs/(events:event-list)">
</ion-back-button>
</ion-buttons>
<ion-title>{{($eventItem | async)?.name}}</ion-title>
</ion-toolbar>

Across the board, positioning directives such as item-start, start, item-end and end are now replaced with more consistent slot="start" or slot="end". Example:

<ion-avatar slot="start">
<img [src]="user.picture.thumbnail">
</ion-avatar>

Positioning directives such as right, bottom etc. have been brought together into a more general API. Example:

<ion-fab vertical="bottom" horizontal="end">
<ion-fab-button (click)="createEvent()">
<ion-icon name="create"></ion-icon>
</ion-fab-button>
</ion-fab>

These updates focus on making the API consistent no matter which framework you’re using ionic with.

Conclusion

We think most Ionic 3.x applications can be updated to Ionic 4 with minimal effort. Even for more complex applications, it should be a reasonably straightforward migration path.
We’ll be happy to help with any problems you face with migrations to Ionic 4. Use the comments below to tell us your experience upgrading your apps and how you’re liking Ionic 4 so far.

Modus is an official partner of Ionic. To read more about our partnership and the benefits of working with Modus + Ionic, go here.

P.S. Thanks for reading this far! If you found value in this, I’d really appreciate it if you recommend this post (by clicking the 👏 button) so other people can see it!

--

--

Muhammad Ahsan Ayaz
Modus Create: Front End Development

Educator | Angular GDE | Speaker | Author of Angular Cookbook | Left medium and now on https://dev.to/codewithahsan