ANGULAR START v19 has launched! ... Get 25% off LEARN MORE
Tutorial hero
Lesson icon

My Method for Upgrading from Ionic 3 to Ionic 4

Originally published May 23, 2018 Time 17 mins

For a major version upgrade, the transition from Ionic 3 to Ionic 4 is going to be relatively minor. Most of your code will be able to remain as is, with some minor syntax changes due to the upgrade to Angular 6 and the web component approach in Ionic 4. However, this doesn’t mean that the upgrade process is going to be as quick and simple as updating a few dependencies and tweaking a few things here and there.

I’ve been playing around with the Ionic 4 alpha for a while now, and have upgraded around 10 applications of varying sizes from Ionic 3 to Ionic 4. The amount of time it takes to upgrade is going to depend a lot on your application, but I would estimate for most people it would take on average anywhere from 1 hour to 8 hours to upgrade most applications. If you intend to switch to using the Angular router (which is recommended) you could probably double that estimate. You would also need to take into consideration how familiar you are with the changes required for Ionic 4 and Angular 6 – if you need to spend some time figuring out what changes are required it’s going to add to this time estimate as well.

This article will just be covering the method that I have been using to upgrade my applications from Ionic 3 to Ionic 4. It has been working well for me, and maybe it will suit you too. However, make sure you also check out the official migration guide.

If you need more general background information about Ionic 4, you should also check out my Ionic 4 Migration Survival Guide.

My Approach

Whenever dealing with a major version upgrade, I prefer to take the “from scratch” approach and port existing apps over, rather than trying to upgrade existing apps. I will create a fresh new application using the latest tooling and then copy over code bit by bit (making modifications as required) until my application works on the new version.

I think this approach is especially suitable for upgrading from Ionic 3 to Ionic 4. There have been very few changes in terms of the APIs and syntax you will be using, but there have been significant changes to the overall project structure and behind the scenes stuff. Therefore, a lot of the stuff that needs to be changed is taken care of for you by creating a new application, and there are only minor changes required when copy and pasting your old code into the new project.

I find that with this approach you are going to be much less likely to run into weird errors that ultimately end up being caused by some obscure configuration or missing dependency. I see it as the slow and steady approach. It might be quicker to make upgrades in place in your existing application if everything goes well, but there is a higher chance that you will mess something up.

In this article, I am going to be walking through the exact step-by-step approach I have been using to update Ionic 3 applications to Ionic 4. There are some aspects of the approach I have been using that you may find a little weird, but it works for me, so feel free to modify this approach to suit your own workflow.

1. Generate a New Application

First, make sure that you have the latest version of the Ionic CLI installed and create a new Ionic 4 application. As I mentioned, Ionic 4 is not officially out yet, so for most people, I would recommend waiting until Ionic actually announces something before actually doing this.

2. Recreate Your Project Structure

The project structure in Ionic 4 has changed. Most notably, your entire application will now live inside of the app folder, rather than just the root component. Rather than manually recreating or copying your existing folders over, I recommend using the CLI to recreate your project structure. This way, you can be confident that the structure is correct.

Bring up your old application in a new window, and explore your project structure. For each page, provider, component, pipe, and so on that you see, run the appropriate generate command in your new application:

ionic g page PageName
ionic g service ProviderName
ionic g component ComponentName

Once you have completed this step, you should have recreated your old project structure in the format required for Ionic 4.

3. Install Dependencies

Open up the package.json file from your old application, and make sure that you add any dependencies that you have installed in your old application to your new application. You can do this either by pasting the dependencies into the package.json file and running npm install, or by installing them with the npm install package-name --save command (which will add the dependencies to package.json for you).

You should only install dependencies that you have added to the project (e.g. third-party libraries you are using in your project), not the default dependencies that were added by Ionic/Angular (because the dependencies that Ionic 4 require are different to Ionic 3). If you are not sure what dependencies you have added to the project, and which were there by default, don’t install them in your new application. If you are missing required dependencies, when you test your project later you will run into errors saying that a particular module cannot be found. When this happens, you will know you are missing a dependency and you can install it then. This avoids installing pointless packages in your application, or worse, installing dependencies that are going to conflict with your project.

4. Copy Old Files

This is one of those “weird” steps that I mentioned before, but I find it helpful. I don’t recommend simply pasting entire files into your new project, you should use the files that the CLI has generated and only copy over the required code (updating as required). If you simply replace the new files with your old files, especially for your TypeScript files, you are going to have your imports and the structure of the class messed up.

Instead, what I like to do is copy and paste the files from my old project alongside their counterparts in the new project. The naming convention in Ionic 4 is different, so you shouldn’t have any issue with files overwriting each other. If you do, just rename the old file. I will go into each page, component, service, and so on, and paste the file from my old project next to it. For example, the structure of my home page in the new application would look like this initially:

  • home.module.ts
  • home.page.html
  • home.page.scss
  • home.page.spec.ts
  • home.page.ts

But then after I paste in the files from the old project it would look like this:

  • home.html
  • home.module.ts
  • home.module 2.ts
  • home.page.html
  • home.page.scss
  • home.page.spec.ts
  • home.page.ts
  • home.scss
  • home.ts

The old files will be used purely for reference as we are porting the code over, and they will be deleted from the project once we are done. This makes it easier to switch back and forth between the old and the new code within a project, and it’s also a convenient way to track progress since the old files are deleted once you are finished porting code over.

5. Create Your Routes

If you are switching to Angular routing then you will need to set up some routes for your application in the app-routing.module.ts file. If you are unfamiliar with using Angular routing in Ionic, you should read Using Angular Routing with Ionic 4 and Implementing a Master Detail Pattern in Ionic 4 with Angular Routing. It’s going to take a little extra work to convert your application to using Angular routing, but I would highly recommend doing it.

Once you are done, your app-routing.module.ts file might look something like this:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

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

6. Copy Over and Update Your Old Code

Now you should go through each of your pages, services, and so on, one-by-one to get your old code working in the application (using the old code you copied into the new project as a reference). The general steps I would take for updating a page look like this (I will also provide code samples of the end result from one of the upgraded themes from Building Mobile Apps with Ionic):

1) Replace all of the code in the new template file with the code from the old template file and update the syntax to reflect the new changes. For example, changing all instances of <button ion-button> to <ion-button>, <ion-navbar> to <ion-toolbar>, <ion-buttons end> to <ion-buttons slot="end">, and so on. These are not the only changes required, you will need to reference the breaking changes provided by Ionic to make sure you get everything. If you are using Angular routing, you should also replace (click) handlers with routerLink where possible for navigation.

home.page.html

<ion-content>
  <ion-slides pager="true" loop="true" autoplay="3000">
    <ion-slide>
      <img src="assets/images/pizza.jpeg" />
    </ion-slide>
    <ion-slide>
      <img src="assets/images/cake.jpeg" />
    </ion-slide>
    <ion-slide>
      <img src="assets/images/donuts.jpeg" />
    </ion-slide>
    <ion-slide>
      <img src="assets/images/veg.jpeg" />
    </ion-slide>
  </ion-slides>

  <ion-toolbar color="dark">
    <ion-title>Browse</ion-title>
  </ion-toolbar>

  <form [formGroup]="searchForm">
    <ion-searchbar
      (ionInput)="onSearchInput()"
      (ionClear)="dataService.clearFilter()"
      formControlName="searchControl"
      placeholder="search categories..."
    ></ion-searchbar>
  </form>

  <div *ngIf="searching" class="spinner-container">
    <ion-spinner></ion-spinner>
  </div>

  <ion-card
    tappable
    routerLink="/category/{{category.id}}"
    *ngFor="let category of categories"
  >
    <img [src]="category.image" />
    <div class="card-title">{{category.title}}</div>
  </ion-card>
</ion-content>

2) Compare your old module file to the new module file, and make sure that you are importing everything that is required.

home.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { IonicModule } from '@ionic/angular';
import { RouterModule } from '@angular/router';
import { ReactiveFormsModule } from '@angular/forms';

import { HomePage } from './home.page';

@NgModule({
  imports: [
    CommonModule,
    IonicModule,
    ReactiveFormsModule,
    RouterModule.forChild([
      {
        path: '',
        component: HomePage,
      },
    ]),
  ],
  declarations: [HomePage],
})
export class HomePageModule {}

3) Compare the imports from the old TypeScript class definition file to the new one. When updating this file, try to keep the default structure of the new file intact, whilst only bringing over imports you require. Remember that some of the imports may no longer be required. If you are unsure if something is required in the new project, leave it out and wait until you run into an error later. Once you have your imports sorted, copy over all of the methods from your old file into the new one and make any updates that are required (you might be able to remove some event bindings that are no longer required for navigation, for example). If you are relying on methods like ionViewDidLoad then you should swap them out for their Angular counterparts like ngOnInit or ngAfterViewInit.

home.page.ts

import { Component, ViewEncapsulation, OnInit } from '@angular/core';
import { DataService } from '../../services/data.service';
import { FormGroup, FormControl } from '@angular/forms';
import { RecipeCategory } from '../../interfaces/recipe-category';
import { debounceTime } from 'rxjs/operators';

@Component({
  selector: 'app-page-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
  encapsulation: ViewEncapsulation.None
})
export class HomePage implements OnInit {

  public categories: RecipeCategory[] = [];
  public searching: boolean = false;
  public searchForm: FormGroup;

  constructor(public dataService: DataService){

    this.searchForm = new FormGroup({
      searchControl: new FormControl('')
    });

  }

  ngOnInit(){

    this.dataService.categories.subscribe((categories) => {
      this.categories = categories;
    });

    this.searchForm.get('searchControl').valueChanges.pipe(
      debounceTime(700)
    ).subscribe(search => {
      this.searching = false;
      this.setFilteredCategories();
    });

  }

  setFilteredCategories(): void {

    this.dataService.filterCategories(this.searchForm.get('searchControl').value);

  }

  onSearchInput(): void {
    this.searching = true;
  }

}

4) Copy over your old .scss file in your new one. This should remain mostly the same, you will just need to make sure to update your CSS selectors where appropriate. If you were using a page-home selector then you would need to update that to app-page-home, or if you were targeting a button element you might need to change that to ion-button. If you are making use of any SASS variables like the $colors map you will need to update that to reflect the use of CSS4 variables in Ionic 4.

home.page.scss

.ios,
.md {
  app-page-home {
    ion-searchbar {
      background-color: var(--ion-color-primary);
    }

    div ion-button {
      margin: 0;
      padding: 15px;

      ion-icon {
        font-size: 1.6em;
        padding-left: 12px;
        padding-top: 2px;
      }
    }

    ion-card {
      border-radius: 0;
      background-color: transparent;
      box-shadow: none;
      position: relative;
      margin-top: 0;

      img {
        box-shadow: 0 2px 6px rgba(0, 0, 0, 0.65);
      }

      ion-button {
        position: absolute;
        padding: 0;
        top: -5px;
        right: -10px;
        color: #fff;

        button {
          background-color: transparent !important;
        }
      }
    }

    ion-card-content {
      padding: 0px;
      text-align: center;
    }

    ion-card-title {
      padding: 5px;
      font-size: 1em;
      font-weight: bold;
      color: #fff;
    }

    ion-col {
      padding: 0;
    }

    ion-grid ion-scroll {
      height: calc(35vh);
      position: relative;
      overflow-x: scroll;
      overflow-y: hidden;
    }

    .logo {
      max-height: 36px;
      margin-top: 5px;
    }

    .button.activated {
      background-color: inherit;
    }

    .searchbar-input-container {
      width: 80%;
      margin: 0 auto;
    }

    .searchbar-input {
      background-color: #ed5f4c;
      border-radius: 50px;
    }

    .spinner-container {
      width: 100%;
      text-align: center;
      padding: 10px;
    }
  }
}

Remember, once you are done updating these files, you will need to delete the old files you were using for reference from the project. This is probably the most complicated step of this process, as it is going to require some familiarity with the changes that are required in Ionic 4/Angular 6. Here are some things to watch out for and keep in mind whilst you are working through this process:

  • Ionic Lifecycle hooks have been removed in favour of Angular lifecycle hooks like ngOnInit and ngAfterViewInit
  • You may need to specify the ViewEncapsulation.None option in the metadata for encapsulation in order for your styles to apply properly
  • Injectables have a ‘providedIn’ property in the decorator now. An injectable “provided in” root will behave as a singleton instance across your application
  • Switch to Angular routing methods instead of using Ionic concepts like push/pop
  • Change (click) bindings to use routerLink instead when navigating between pages (in most cases)
  • Ionic UI components now rely heavily on the use of “slots” for determining positioning and styling

7. Set up Variables

The theming variables work differently in Ionic 4. Instead of using SASS variables to define the primary the secondary colours for our applications, we will now use CSS4 variables.

Make sure to update the theme/variables.scss file to reflect the theme colours that you would like to use. If you were using any SASS variables to style other Ionic components, you should also make sure to update those to their CSS4 counterparts.

8. Add Global SCSS

If you defined any global styles, make sure to add them to your global.scss file.

9. Test and Fix

If you’ve got to this part, then most of the work in the upgrade should be done. However, it would be immensely surprising if your application just ran and everything worked at this point. You will likely have missed updating some syntax in your template, have some incorrect imports, your styles might not be applying correctly. Now it’s just a matter of working your way through these minor issues. Serve up your application, and start working on any issues you find. If you have automated tests defined for your application then these will help a great deal in identifying outstanding issues.

Summary

For most people, the steps above should cover the majority of what needs to be updated. These steps above don’t cover everything you could possibly need to do to upgrade a project. It’s hard to provide a step-by-step guide that is going to suit everyone since individual projects are going to be so different. I think the best thing that you can do before you upgrade is to familiarise yourself with the changes in Ionic 4 and Angular 6. Browse through new documentation as it becomes available, read through the change logs and breaking changes lists, and mess around in some newly generated applications. The more familiar you are with the new versions, the easier the upgrade process is going to be.

Learn to build modern Angular apps with my course