How can I pass data back to my root page in Ionic

2019-04-28 18:48发布

问题:

I have an Ionic 2 app. The premise of the app is to take a class. Once a class has been opened the user is marked as taking this class in the remote API.

The flow of data is:

  1. User opens 'Classes' tab.
  2. App requests 'Classes' data from API. Each class holds a 'status' for that user.
  3. User selects a class. This opens a new view in the app using NavController.push(Page, { 'classes': classObjFromApi });.
  4. There are a few pages added to the stack, each has the classes object passed to it.
  5. On the final page the API is informed that the current class has been taken and the user can navigate back to the root using NavController.popToRoot()

On the root page each class' status is shown ('Not started', 'In progress', 'completed'). At point 5 in the above flow the user is taken back to the root, but this has the data from when it was originally constructed, which shows the class as 'Not started', even though it's 'completed' now.

How can I update the data on the root page from a different page in the stack? I had assumed that popToRoot() would accept navParams so I could check for the presence of that data in the root page and use it if it exists or request from the API if not.

I can get around this by re-requesting data from the API on the ionViewDidEnter method, but that means an API/HTTP request every time the user views this page, even though the app has the data. Seems messy.

I'd rather not use internal storage if I can help it, as it creates a delay when loading the page, as I have to wait for data to be pulled from storage before displaying it.

So my question: what's the best way of doing this? If it is to pass data to the view, how do I do that if popToRoot() doesn't support nav params?

回答1:

The best way to do that would be by using Events:

In your root page, subscribe to a custom event, to execute some code every time that event is published:

import { Events } from 'ionic-angular';

constructor(public events: Events) {
  events.subscribe('status:updated', (updatedData) => {
    // You can use the updatedData to update the view
    // ...
  });
}

// Clean up the subscription when the page is about to be destroyed
ionViewWillUnload() {
  this.events.unsubscribe('status:updated');
}

And publish that event in any other page, when the user changes that data:

import { Events } from 'ionic-angular';

constructor(public events: Events) {}

yourMethod(): void {
  // Your custom logic...
  // ...

  // Now you can publish the event to update the root page (and any other page subscribed to this event)
  this.events.publish('status:updated', updatedData);
}

EDIT

Another way to do that, would be by using a shared service, and using a Subject to publish/subscribe to changes. The result would be pretty similar to Ionic Event's, but in this scenario you'd need to add a shared service and then use it when the data is updated. I'd prefer Ionic Events, but just in case, this is how you could do it:

import { Subject } from 'rxjs/Subject';

@Injectable()
export class MySharedService {
  public onDataChange: Subject<any>;

  constructor() {
    this.onDataChange = new Subject<any>();
  }

  public publishUpdate(newData): void {
    // Send the data to all the subscribers
    this.onDataChange.next(newData);
  } 
}

So now in your root page, subscribe to the changes using the shared service

import { Subscription } from 'rxjs/Subscription';

// ...

private onDataChangeSubscription: Subscription;

constructor(public mySharedService: MySharedService) {
  this.onDataChangeSubscription = mySharedService.onDataChange.subscribe(
    updatedData => {
      // You can use the updatedData to update the view
      // ...
    });
}

// Clean up the subscription when the page is about to be destroyed
ionViewWillUnload() {
  this.onDataChangeSubscription.unsubscribe();
}

And you'd need to use the shared service too when updating the data from any other page:

constructor(public mySharedService: MySharedService) {}

yourMethod(): void {
  // Your custom logic...
  // ...

  // Now you can publish the event to update the root page (and any other page subscribed to this event)
  this.mySharedService.publishUpdate(updatedData);
}

This approach may be better if you need to execute some custom logic every time the data is updated, so instead of doing that on each subscriber, you could just do it in the shared service, and broadcast the result...