[SOLVED] Ionic navController pop with params

Thx for adding the point on the idea list.

Agree with you, with a “service” it’s possible to pass parameters to the back view, that is actually the solution I developed in Ionic1.

More than just a “coding pain”, it’s also a little bit less performant I think (in the transition itself and in the boot load time of the app, since the app have to load a directive more)

Still if someone got another idea, I would like to hear it :wink:

pass in a callback when transitioning

 // callback...
 myCallbackFunction = function(_params) {
     return new Promise((resolve, reject) => {
             resolve();
         });
 }

 // push page...
 this.navController.push(OtherPageComponent, {
    callback: myCallbackFunction
});

in the OtherPageComponent

 this.callback = this.navParams.get("callback")

 this.callback(param).then(()=>{
    this.navController.pop();
 });
33 Likes

I tried your proposition @aaronksaunders. The downside of that solution is that you can’t access the scope of the back view from the callback function. So yes you could pass params back to the back view but how to use these params to update it ? Or maybe I miss something…

i dont understand what you are trying to accomplish so it is difficult to respond… also when you say scope what do you mean?

I meant the variable of the calling page, something like

myCallbackFunction = function(_params) {
     return new Promise((resolve, reject) => {
         this.test = _params;
         resolve();
     });
}

Where this.test is a variable of the calling page.

But I solve the issue with a service, have to go forward.

2 Likes
myCallbackFunction = (_params) => {
     return new Promise((resolve, reject) => {
         this.test = _params;
         resolve();
     });
}
17 Likes

I’m so dumb…shame…shame…shame :wink:

Your solution works like a charm, one service less, thx @aaronksaunders !

tricky ! thanks :slight_smile:

Thats smart. Thanks!!!

Hi,

I’m unsure how to use your solution to this issue. In my navigation stack I have a view called add-teams that navigates to a second view called choose-player. Now when I add a player I would like to pass back a player object and the position in the team the player would fill from choose-player back to the previous view which is add-teams and then remove that view from the stack. Using your solution above how would I do this?

Apologies if this is a stupid question, I’m still very new to angular2 and ionic.

hi i cant use this.test in other functions i mean that _params instance. can u help.

do not understand what you are asking?

Hello @aaronksaunders & others –

I’ve been working through a similar problem as you’ve solved here, namely, I believe I need to pass a value back to an earlier screen in my Ionic (progressive web) application’s navigation stack.

Specifically, I am handling a case where a user of my product is going back to an earlier page which may make a POST request to my API server. I have a CSRF token which was GET’ed during the initial authentication with my API and needs to be available on every screen that can make POST requests. This is easy when moving forward, since I pass the token value along via navParams when pushing items onto the stack. Without this value, my API cannot accept the request.

SO: when popping items off the stack, does their original scope (the various “this.varName=…” from the original time they were inserted still exist? If so, I don’t need to pass anything backwards - the CSRF value will remain accessible for inclusion in any new POSTs generated by the page. If not, would you mind explaining how exactly the code you posted back in July '16 works? I’m not following it…

Many thanks and keep up the awesome work,
Andrew

One item of explanation to assist here – the reason I’m using the this.navController.pop() method here is because in the browser on my target devices (iOS and Android phones), users frequently tap the “back” button in their browser (or the hardware reverse-arrow one, for android) which seems to automatically trigger a backward navigation action.

In order to keep my application from fritzing out (losing its CSRF token) I tend to remove the built-in back button from Ionic2’s navigation bar and include a button with my own code running (the pop()). An earlier attempt to simply push on the earlier view with the correct data had the downside that users employing their browser’s back button ended up seeing earlier screens in the navigation stack with stale data on it… not to mention mucking up my data flows.

If you have any suggestion that would make this easier, I’d welcome that as well. I’m hoping for now that each page in the navigation stack simply includes the data that was on it at the time (when initially push()'ed) so the CSRF token value will continue to exist.

Thanks,
Andrew

This strategy of passing authentication tokens in NavParams seems extremely brittle to me. I think stashing them in a shared service would be vastly preferable.

Can you explain that idea a bit more? RIght now I have a service which does the GET to my API server to request a new token value which is then passed along as a navParam…

It sounds like I could have the initial request to GET the value from the server could be stored as a variable value within the service I have which does that call - and then as long as that service is attached to any of my pages, I should be able to access it (after the initial call is made)?

Apologies about being pretty new to Angular2… coming from an embedded world where things are a wee bit simpler to visualize.

Thanks,
Andrew

Right. You probably don’t even want to really access it from the pages themselves (although you can do that if you wish). Typically, all the pages care about is “are we authenticated?”.

I use JWT for authentication, and here’s my approach. You should be able to adapt it to your situation.

Sink

This is a service that interacts with ionic-storage to persist credentials across app restarts. If you don’t want to do that, just store the tokens temporarily.

export class Sink {
  private _jwt: string;
  private _authNotifier: ReplaySubject<boolean> = new ReplaySubject<boolean>(1);

  constructor(private _db: Storage) {
    // load from storage
  }

  authenticationNotifier(): Observable<boolean> {
    return this._authNotifier;
  }

  // don't call this until the authentication notifier has fired true
  getJwt(): string {
    return this._jwt;
  }

  setJwt(jwt: string): Promise<void> {
    this._jwt = jwt;
    this._authNotifier.next(!!jwt);
    // save it in storage, return resultant promise
  }
}

AuthedHttp

This is a wrapper for Http that handles adding authorization headers. Mostly fairly boring boilerplate, but it can inject Sink in its constructor, get the authentication token, add it to headers, and use that in requests, all transparently to consumers.

App component

constructor(sink: Sink) {
  sink.authenticationNotifier().subscribe((authed) => {
    if (authed) {
      this.rootPage = AuthedPage;
    } else {
      this.rootPage = UnauthedPage;
    }
  });
}

Now any time anybody anywhere calls setJwt(somethingValid), the root page automatically flips to the authorized home page. If setJwt(null) is called, the page automatically goes to the unauthed page. Logout becomes dead simple; no fooling with navigation controllers at all.

Login page

constructor(private _http: Http, private _sink: Sink, private _nav: NavController) {
}

doLogin(): void {
  this._http.get('url-for-auth-token').subscribe((rsp) => {
    let authtoken = magicallyExtractToken(rsp);
    this._sink.setJwt(authtoken).then(() => {
      this._nav.pop();
    });
  });
}
1 Like

In our use case, we need to be able to apply a specific value to every POST that our Ionic2 application makes to our API (about a half-dozen endpoints). The value is a token which is generated on a GET-only endpoint and can be parsed only by code that has permission via CORS origin controls… so the workflow is:

  • app is loaded
  • user begins interaction, causing the GET /token request to fire
  • token value is returned and stored in app
  • every new action with a POST has a header included whose value is the token body

on the API, the header value is compared with the original value provided… this allows for confirmation that a browser-based caller of the POST endpoints cannot trigger any API actions unless they have CORS access granted. Not really authentication (that’s handled another way), but just CSRF protection.

I think the same general skeleton should work perfectly for you.

export class AuthedHttp {
  constructor(private _sink: Sink, private _http: Http) {
  }

  post(url: string, body: any, options?: RequestOptionsArgs): Observable<Response> {
    let allopts = this._frotzOptions(url, options);
    return this._http.post(url, body, allopts);
  }

  private _frotzOptions(urlo: string | Request, options: RequestOptionsArgs): RequestOptionsArgs {
    if (!options) {
      options = {}
    }
    if (!options.headers) {
      options.headers = new Headers();
    }
    let token = this._sink.getToken();
    // add it to options.headers
    return options;
  }
}
export class TypicalPage {
  constructor(private _ahttp: AuthedHttp) {
  }

  foo() {
    this._ahttp.post('restricted-url', body).subscribe((rsp) => {
    });
  }
}

Not a NavParam in sight.

2 Likes

Cool, thanks! I’ll run some tests locally to see if I can get this integrated without using any annoying navParams.