3d cartoon hands holding a phone

Unlock full course by purchasing a membership

Lesson 1

Adding Ionic to an Angular Application

How do we go from Angular application to Ionic application?

STANDARD

Adding Ionic to an Angular Application

We’ve spent a little while working with a standard Angular application generated using the Angular CLI. We’ve also already created an Ionic application using the Ionic CLI. This is great because it sets up everything for us out of the box, and we can just start building!

But… it does muddy the waters a bit. If our application already includes Ionic by default then it is kind of hard to tell what is “Ionic” and what is “Angular”. We’ve let the Ionic CLI handle the jump from an “Angular” application to an “Ionic/Angular” application for us.

This is most certainly what you should do when building Ionic applications: use the Ionic CLI to generate the application for you. However, it is useful as a learning exercise to start off with a standard Angular application generated with the Angular CLI and add Ionic to it manually. Not only will this show us what Ionic changes about a standard Angular application, it is also going to give us some experience setting up and configuring a third party library in Angular.

It is also useful to demonstrate that all of the Angular knowledge you are gaining by learning Ionic is also applicable more broadly to Angular applications in general.

So, that is exactly what we are going to do in this lesson!

Generate an application with the Angular CLI

First, let’s generate a new Angular application with the Angular CLI:

Run the following command

ng new angular-to-ionic

You can use the following configurations:

? Would you like to add Angular routing? Yes
? Which stylesheet format would you like to use? SCSS

Install @ionic/angular

Ionic comes with an ng add schematic for Angular. This means that we can just run:

ng add @ionic/angular

And the schematic will take care of everything for us… but it seems like this kind of defeats the purpose of what we are doing in this lesson. We may as well just use the Ionic CLI.

We are going to do this anyway, as we will be able to inspect specifically what this schematic is adding. We will see the files that have been modified output to the terminal, and we can also use git to check what specifically has changed.

Run the following command inside of your new project:

ng add @ionic/angular

You should see an output something like this:

> ng add @ionic/angular
i Using package manager: npm
✔ Found compatible package version: @ionic/[email protected].
✔ Package information loaded.

The package @ionic/[email protected] will be installed and executed.
Would you like to proceed? Yes
✔ Packages successfully installed.
CREATE src/theme/variables.css (288 bytes)
UPDATE package.json (1118 bytes)
UPDATE src/app/app.module.ts (466 bytes)
UPDATE angular.json (5501 bytes)
✔ Packages installed successfully.

Understanding the Changes

We can see that the Ionic schematic has created the following file:

  • src/theme/variables.css

and it has modified the following files:

  • package.json
  • src/app/app.module.ts
  • angular.json

To more closely inspect what has changed we can use git diff inside of our project in the terminal. If you are using something like VS Code that highlights changes, you can also go to the files marked with an M and then in the gutter of the editor (where the code line numbers are) the sections that have been modified will have a line next to the numbers. I will save you the hassle of doing this if you aren’t comfortable with it, and I will just paste the changes below.

Let’s start out with the src/theme/variables.css file. I don’t need to tell you what has changed here because the entire file is new.

variables.css

/* Ionic Variables and Theming. */
/* This is just a placeholder file For more info, please see: */
/* https://ionicframework.com/docs/theming/basics */

/* To quickly generate your own theme, check out the color generator */
/* https://ionicframework.com/docs/theming/color-generator */

There isn’t actually anything in this file yet, but this is where we will define the CSS variables that will configure what the Ionic components look like. Often this means just specifying what colours certain components should use, but we can get more in-depth as well. We will be discussing theming and styling in another lesson.

package.json

  "dependencies": {
    "@angular/animations": "^14.1.0",
    "@angular/common": "^14.1.0",
    "@angular/compiler": "^14.1.0",
    "@angular/core": "^14.1.0",
    "@angular/forms": "^14.1.0",
    "@angular/platform-browser": "^14.1.0",
    "@angular/platform-browser-dynamic": "^14.1.0",
    "@angular/router": "^14.1.0",
    "@ionic/angular": "^6.2.0",
    "rxjs": "~7.5.0",
    "tslib": "^2.3.0",
    "zone.js": "~0.11.4"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "^14.1.0",
    "@angular/cli": "~14.1.0",
    "@angular/compiler-cli": "^14.1.0",
    "@types/jasmine": "~4.0.0",
    "jasmine-core": "~4.2.0",
    "karma": "~6.4.0",
    "karma-chrome-launcher": "~3.1.0",
    "karma-coverage": "~2.2.0",
    "karma-jasmine": "~5.1.0",
    "karma-jasmine-html-reporter": "~2.0.0",
    "typescript": "~4.7.2",
    "@ionic/angular-toolkit": "latest"
  }

The schematic has added and installed the @ionic/angular and @ionic/angular-toolkit dependencies. The @ionic/angular package is what provides most of what Ionic does including all of the Ionic web components (technically, the web components come from @ionic/core but that is included within @ionic/angular). The @ionic/angular-toolkit package provides additional schematics for Ionic.

src/app/app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { IonicModule } from '@ionic/angular';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    IonicModule.forRoot()
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

The ng add command has also made a modification to our root module file. See if you can pick out what it is.

We are now importing the IonicModule:

IonicModule.forRoot()

Just like how we would import things like CommonModule to allow us to use things like *ngIf and *ngFor, we import the IonicModule to allow us to use Ionic’s components. We do this once in the root module and call the forRoot() static method. A static method is a method on a class that can be called without actually needing to create an instance of that class. The important part here is that we call forRoot in the root module, and we can optionally supply forRoot with a configuration object for Ionic.

We use forRoot in the root module, but we will also need to import the IonicModule inside of any other module we want to use it in (e.g. our HomeModule). If we don’t, the compiler will complain that Ionic’s components don’t exist. Inside of any other module, we just import it like this:

  imports: [
    IonicModule
  ],

angular.json

I won’t paste the changes to angular.json because it is quite long, so I would encourage you to go have a look for yourself. In general, Ionic adds some additional resources to be added to builds of the application, including all of its CSS files (it’s internal ones, but our variables.css that was created as well). It also adds some build configurations related to Cordova, but that isn’t relevant to us.

That’s not everything…

The changes above will allow us to use Ionic components in an Angular application, but it isn’t quite everything we get when we generate a new application with the Ionic CLI. Likely the reason Ionic doesn’t add everything is because some changes are more destructive, and if you are adding Ionic to an existing Angular application you might not want the ng add schematic making sweeping changes to your application.

Let’s manually add in a few more things.

Modify src/app/app.component.html to reflect the following:

<ion-app>
  <ion-router-outlet></ion-router-outlet>
</ion-app>

This is an important one, and there are two things going on here. One is that we are using the <ion-router-outlet> instead of the default Angular <router-outlet>. By default, Angular will apply mobile style transition animations when switching between views, and using Ionic’s version of the router outlet is what will enable this. We also surround our entire application in the <ion-app> component which is important for styling.

Modify src/styles.css to reflect the following:

/* You can add global styles to this file, and also import other style files */

/* Core CSS required for Ionic components to work properly */
@import "~@ionic/angular/css/core.css";

/* Basic CSS for apps built with Ionic */
@import "~@ionic/angular/css/normalize.css";
@import "~@ionic/angular/css/structure.css";
@import "~@ionic/angular/css/typography.css";
@import '~@ionic/angular/css/display.css';

/* Optional CSS utils that can be commented out */
@import "~@ionic/angular/css/padding.css";
@import "~@ionic/angular/css/float-elements.css";
@import "~@ionic/angular/css/text-alignment.css";
@import "~@ionic/angular/css/text-transformation.css";
@import "~@ionic/angular/css/flex-utils.css";

This includes the CSS files that were referenced in the angular.json file. Typically, Ionic actually creates a global.scss file instead of a styles.css file, but it is fine for us to just use the styles.css file if we want. We will just be using the Ionic CLI anyway so it doesn’t really matter right now.

Modify src/app/app-routing.module.ts to reflect the following:

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

const routes: Routes = [
  {
    path: 'home',
    loadChildren: () =>
      import('./home/home.component').then((m) => m.HomeComponentModule),
  },
  {
    path: '',
    redirectTo: 'home',
    pathMatch: 'full',
  },
];

@NgModule({
  imports: [
    RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules }),
  ],
  exports: [RouterModule],
})
export class AppRoutingModule {}

NOTE: I’ve set up an example route here that links to a HomeComponent, if you are following along, see if you can set that component up yourself.

The important part is this:

RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules }),

Ionic CLI will also generate an application with a specific preloadingStrategy to be used. The preloadingStrategy defines how Angular should preload modules in the application. We talked about this before when discussing lazy loading and setting up the pages in our application with modules. By using modules and supplying it to loadChildren in the route configuration, it doesn’t need to be immediately loaded when the application is booting up.

By default, modules will not be loaded until the path for that module is loaded (e.g. the HomeModule won’t load until we go to the /home path). However, with the PreloadAllModules strategy, all of the modules will be loaded in the background as quickly as possible after the application has finished booting. This means our application can still load faster initially, but we also aren’t going to get a delay when trying to access routes that haven’t loaded yet.

Modify src/app/app.module.ts to reflect the following:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import { RouteReuseStrategy } from '@angular/router';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, AppRoutingModule, IonicModule.forRoot()],
  providers: [{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy }],
  bootstrap: [AppComponent],
})
export class AppModule {}

Ionic already added the IonicModule for us, but a new Ionic application also includes a RouteReuseStrategy. This is beside the point, but this is a good example of utilising the more advanced features of tokens and dependency injection that we discussed before. Take a look at this:

{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy }

We are saying we want to provide the RouteReuseStrategy token, but we want to use the class IonicRouteStrategy for that token. That means that wherever the RouteReuseStrategy token is used, it will actually use an instance of IonicRouteStrategy.

Route reuse strategies are a much more advanced topic, and you don’t really ever need to consider it too much. The key idea is that, by default, whenever we navigate between components in Angular, Angular will destroy the component that was just navigated away from. If we later come back to that route the component will be recreated again. This might not always be what you want, and Ionic defines some more specific rules around when components should be created and destroyed.

This is where Ionic’s lifecycle hooks like ionViewWillEnter and ionViewWillLeave come into play. These are like Angular’s ngOnInit lifecycle hook, except that they run at times related to the Ionic Page Lifecycle. We will not often use Ionic’s specific hooks, but consider a situation where you are running some logic using Angular’s ngOnInit hook. This only runs when the component is first created. If, according to Ionic’s route reuse strategy, some component is not destroyed when we navigate away from it, then the ngOnInit logic will not run when we navigate back to it. It is rarer, but in situations like this we might need to explicitly run some logic inside of a hook like ionViewWillEnter which will run every time we navigate to that page, regardless of whether it has already been created or not.

Recap

That’s it! We have now converted a standard Angular application into an Ionic application that is almost identical to one created by the Ionic CLI.

What does importing IonicModule allow us to do?

Why do we need to use ion-router-outlet instead of router-outlet?