NavParams Alternatives in Ionic 4

September 27th 2019 Ionic 4

Ionic 3 provided a unified way for passing parameters to newly opened pages - the NavParams object. In the target page constructor, it could be used to get any parameters passed to that page:

constructor(private navParams: NavParams) {
  this.item = this.navParams.get("item");
}

For regular pages pushed onto the navigation stack, the parameters were passed as an argument to the NavController::push method:

this.navCtrl.push("RegularPage", { item: this.item });

For modal pages, they were a part of the options passed to the ModalController::create method:

this.modalCtrl.create(ModalPage, { item: this.item }).present();

Ionic 4 doesn't have a universal NavParams object anymore. Navigation is based on Angular routing. Hence the preferred way for passing parameters to a page are route parameters. Since this makes them a part of the URL, they can't be structured objects anymore. Instead only IDs should be passed around and the target page should be responsible for retrieving the corresponding full object.

When writing a new application from scratch this isn't too much of an issue. However, when upgrading an existing Ionic 3 application, it's best if you can avoid such code changes. Even without them, there's enough to go wrong in the process. Fortunately, Angular also supports passing of custom state as part of the navigation (I'm using Ionic's NavController wrapper instead of Angular router directly):

this.navCtrl.navigateForward("/regular", { state: { item: this.item } });

This information must be retrieved in the target page constructor. It's not available anymore later in the page lifecycle:

constructor(private router: Router) {
  if (this.router.getCurrentNavigation().extras.state) {
    this.item = this.router.getCurrentNavigation().extras.state.item;
  }
}

The approach mostly worked well, but not in all cases. When I tried to pass across a Moment instance, the navigation failed with a DataCloneError:

DataCloneError: Failed to execute 'pushState' on 'History': function (number) {
    var b = number % 10,
        output = (toInt(number % 100 / 10) === 1) ...<omitted>... } could not be cloned.

This worked just fine in Ionic 3. Fortunately, the target page didn't really need the full Moment instance. It immediately converted it to a JavaScript Date anyway, so I resolved the issue by converting the value before passing it as a parameter.

For modal pages, a different approach must be used. Parameters are passed as part of the options passed to the ModalController::create method, similar as Ionic 3:

(await this.modalCtrl.create({
  component: ModalPage,
  componentProps: { item: this.item }
})).present();

These parameters are now injected into the target page as input properties:

export class ModalPage {
  @Input()
  item: Item;
}

Using the above-described approaches, I could upgrade the passing of navigation parameters in a rather large Ionic 3 application to Ionic 4 without too many related code changes.

Copyright
Creative Commons License