Recently, I published an article that talked through some of the expected changes for the upcoming Ionic 4.x release. Most of the changes that will be introduced are behind the scenes, and there won’t be that many changes required for updating your Ionic 3.x applications to 4.x.
Perhaps the most noticeable change coming in Ionic 4, and the one that will require the biggest change in your existing applications, is the move to Angular style routing. The typical push/pop style navigation that Ionic has used in the past will still be available, and you can even use this style of navigation directly through Ionic’s web components, but the recommended approach moving forward will be to use the Angular Router if you are building an Ionic/Angular application.
NOTE: If you need more context around the upcoming changes to Ionic, specifically the focus on web components and what that is all about, I would recommend reading this article.
The Ionic team is aiming to make Ionic more generic so that it isn’t tied to any specific framework, and implementing their own navigation/routing for each framework could get quite messy and would ultimately be somewhat unnecessary. Instead, you should just rely on the native navigation for whatever framework it is that you are using Ionic with. If you are not using a framework, you can still use Ionic’s own navigation components for push/pop style navigation, but you probably shouldn’t do that in an Angular application.
Angular is a bit of a special case – since Ionic/Angular have been tightly integrated in the past, Ionic already has a specific @ionic/angular
package that helps with integration with Angular. The NavController
is included in this package and can be used for interacting with the Angular router, but it is still just using Angular routing underneath. The NavController
in Ionic 4 is different to the NavController
in Ionic 3 even though it has retained the same name.
You can just use Angular router methods directly, but by using Ionic’s NavController
instead it will make sure to apply the appropriate screen transition animations since screen transitions in a typical Ionic application have a “direction” (e.g. a forward navigation will animate the new screen in from the right).
Keep in mind that although the way Angular routing will work with Ionic is mostly the same as normal Angular routing, and so you can just rely on Angular documentation or resources for routing examples/advice, Ionic does have its own router outlet implementation called <ion-router-outlet>
(basically, you just plop the router outlet wherever you want the component for the active route to be displayed). This is mostly the same as Angular’s <router-outlet>
except that it will automatically apply the screen transition animations that I just mentioned.
What Is Angular Routing?
If you’ve been building Ionic applications then you would be used to navigating through your applications by using the NavController
to push new pages to navigate forward in the navigation stack:
this.navCtrl.push('SignupPage');
Popping pages to move backward in the navigation stack:
this.navCtrl.pop();
or starting a completely new navigation stack by changing the root page:
this.navCtrl.setRoot('HomePage');
Angular routing is different in that it is based on the browser model of navigation and uses the URL to determine which page/component to show. In your application, you would need to supply a set of routes that might look something like this:
const routes: Routes = [
{ path: 'login', component: LoginPage },
{ path: 'home', component: HomePage },
{ path: 'detail/:id', component: DetailPage },
{ path: '', redirectTo: '/login', pathMatch: 'full' },
];
The path
property defines the URL, and the component
property defines which component should be displayed by the <ion-router-outlet>
when that URL is hit. If I were to go to the following URL:
http://localhost:8100/home
then the HomePage would be displayed. We also have a default route defined at the bottom so that if no route is supplied, it will use the login
route.
NOTE: This is a simple approach to routing, make sure to also read the section on lazy loading later in this article.
Navigation in the application is then based on whatever the URL currently is, and what route it matches. Simply changing the URL would change the current page, but in order to navigate in your application, you have a few options.
You can use routerLink to link to another page:
<ion-item
[routerLink]="'/detail/' + item.id"
routerDirection="forward"
></ion-item>
In this case, we would be launching the DetailPage and supplying it with an id
route parameter (we could then use that data inside of the component). This is somewhat advantageous because it means you don’t always need to set up an event binding and involve the NavController
every time you want to transition to another page. Also note that we supply a routerDirection
to indicate whether this is a forward or backward navigation so that the screen transition animation can be applied correctly. If this is not supplied, Ionic will guess at what animation it should use but it is better to be explicit.
You can also navigate programmatically by injecting the standard Angular Router into your pages and calling either of these methods:
this.router.navigateByUrl('/login');
or
this.router.navigate(['/detail', { id: itemId }]);
However, the best way to navigate programmatically in an Ionic/Angular application is to use the NavController
from @ionic/angular
. This is actually quite similar to how you would use the NavController
previously to push/pop, except now you would just use one of these methods:
this.navCtrl.navigateForward('/route');
this.navCtrl.navigateBack('/route');
this.navCtrl.navigateRoot('/route');
The benefit of using NavController
to navigate is that it allows you to specify a “direction” for the navigation, which will help Ionic’s <ion-router-outlet>
display the page transition properly.
Why Switch to Angular Routing in Ionic?
As I mentioned, push/pop navigation does still exist in Ionic, but it is advisable to switch to Angular routing if you are building an Ionic/Angular application.
Aside from the potential future-proofing aspect of switching to Angular routing, there are a couple of big reasons to switch to it as soon as you upgrade to Ionic 4.x:
- The
@IonicPage
decorator has been removed and it is no longer possible to enable lazy loading without Angular routing - If you are developing a PWA, the browser-based navigation approach is much simpler
Lazy loading is the big ticket item here. If you are unfamiliar with lazy loading the basic idea is that it breaks your application down into smaller chunks, and only loads what is necessary at the time. When your application is first loaded, only the components that are necessary to display the first screen need to be loaded.
With lazy loading, your application can boot much faster because it doesn’t need to load much – you could have a huge application, with 50 pages, but it could still load just as fast as an application with just 2 pages. Without lazy loading, you need to load the entire application upfront before anything can be done.
With smaller applications, it is not that much of a big deal, but it is critical for larger applications. There is no harm in using lazy loading, so I would encourage everybody to use it by default (and I think that it will be set up by default in the Ionic 4.x starter templates).
Lazy Loading with Angular Routing in Ionic
Lazy loading with Angular routes is not too dissimilar to regular routing. However, your routes would look like this instead:
const routes: Routes = [
{ path: 'login', loadChildren: './pages/login/login.module#LoginModule' },
{ path: 'home', loadChildren: './pages/home/home.module#HomeModule' },
{
path: 'detail/:id',
loadChildren: './pages/detail/detail.module#DetailModule',
},
{ path: '', redirectTo: '/login', pathMatch: 'full' },
];
Instead of supplying a component
for the route, we supply a loadChildren
property. Since we are not loading everything up front, this property is essentially saying “go to this file to determine what components need to be loaded”. We link to the module file for the page just like we did with lazy loading with @IonicPage
in Ionic 3.x, and we also need to supply the name of the module class by appending it to the end with a #
.
If we use the home
route as an example, the loadChildren
property is:
'./pages/home/home.module#HomeModule'
The router knows that it needs to open the home.module.ts file, and look for the HomeModule. That file might look like this:
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { IonicModule } from '@ionic/angular';
import { HomePage } from './home.page';
import { HomePageRoutingModule } from './home-routing.module';
@NgModule({
imports: [CommonModule, IonicModule, HomePageRoutingModule],
declarations: [HomePage],
entryComponents: [],
bootstrap: [HomePage],
})
export class HomeModule {}
NOTE: If you are using lazy loading, you should make sure that you are not also declaring these pages in your root module file (app.module.ts).
This specifies all of the dependencies required for this particular route, and then it also supplies its own HomePageRoutingModule
to define any additional routes. That file might look like this:
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomePage } from './home.page';
const routes: Routes = [{ path: '', component: HomePage }];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class HomePageRoutingModule {}
In this case, we just want the HomePage loaded when we hit the /home
route, so we supply the default route with an empty path (and this route will load the HomePage component). If you wanted to, you could also add additional routes in the HomePageRoutingModule. If you were to define a route with a path of something
in this file, the user could then activate that route by going to /home/something
, and you could use that to load a different component.
With this setup, we can make it so that Angular only loads the directives/components that we require for a particular route, rather than all of the directives/components for our entire application. The set up might seem a bit convoluted, but like with lazy loading in Ionic 3, it’s all pretty much the same for every page, and it will all likely be set up by default, so you don’t really need to think too much about it.
Summary
In my opinion, this is the one “scary” looking change in Ionic 4.x, but I don’t think that it is too much of a mental leap from the old style of navigation to this. Although you do not have to use this style of routing, I would recommend that you do.
If you are creating a new Ionic 4 application you will find that most of this stuff is already set up for you. You will have default routes set up when you generate a new application, and when you generate new pages using ionic g page
it will also automatically add additional routes to your app-routing file for you (although you might want to modify them a little).