简体   繁体   中英

Navigating from canActivate in Aurelia view-model

In my Aurelia view-model, I am trying to check some precondition, in canActivate() , and based on that it is decided whether navigate to a different view or not.

export class ThisView{

    canActivate() {        
        const self = this;
        const promise = self.services.get(url)
            .then((items: any) => {
                if (items) {
                    self.items= items;
                    if (self.items.length === 1) {
                        this.router.navigate("some/other/view");
                        //return false; 
                    }
                    //return true;
                } else {
                    self.logger.error("No item found");
                    //return false;
                }                
            });
        return promise;
    }
}

Now even though I am navigating to some/other/view if there is only one item found, view of ThisView still gets activated (ie can be seen in browser).

Is there a way, to avoid that? There couple of things I tried for this.

  1. Return true , or false from promise to accept, or reject activation of this view. But as this view is kind of landing page of my application, if rejected (returned false) it tries to restore the previous location, which is not available, and throws an error. And restoring a previous location is also not desired by the application for this specific case.
  2. Another idea was to do something like a pipeline step where we can do something like next.cancel(new Redirect("some/other/view")) , where we can instruct to cancel the current navigation instruction with a new one. But I am not sure how to do the same from a view-model.

Please suggest.

Workaround: I have finally used a simple trick of using if.bind on view of ThisView . However, it could have been more interesting, if we can somehow cancel the current instruction (from page lifecycle) with a new one.

instead of this.router.navigate("some/other/view) can you not import redirect and add the redirect in there, ie

import {Redirect} from 'aurelia-router';

export class ThisView{

canActivate() {        
    const self = this;
    var items = self.services.get(url);
    if(items){
        self.items= items;
        if (self.items.length === 1) {
           return new Redirect("some/other/view");
        }
        return true;
    } 
    else {
        self.logger.error("No item found");
        return new Redirect("not-found-view");
    }                
}

}

I've made a basic GistRun showing how this works - I haven't used a promise, but I believe the concept is the same.

https://gist.run/?id=52301f1b2898691ff4d54f320f61f6c6

You can use activate() event to process router navigation. Throw error to stop navigation or goto/redirect to another location. Example:

import {Router} from 'aurelia-router';

export class SampleModel {
  static inject() { return [Router] };

  constructor(router) {
    this.router = router;
  }

  activate(){
    if(this.somedata === null){
      // stop navigation or goto start location
      if(this.router.history.previousLocation){
        // stop from navigation
        throw new Error('Wrong data');
      }else{
        // return to start page
        this.router.navigate('start');
      }
    }
  }
}

You could also inject some middleware in the router pipeline the same way authorization steps are done.

You would then call your service in a particular step and route to a different view depending on the result.

Some code snippets on how steps are added to the router pipeline can be found here: Aurelia: During a Router's Pipeline Step, how do I bind a variable to that router?

I figured out another way to handle navigation in canActive()

   canActivate(params, routeConfig, navigationInstruction) {

        return new Promise((resolve, reject) => {

            this.api.request(params.id).then(result => {

                if (result) {
                    resolve(true);
                } else {
                    toastr.error('Invalid URL');
                    reject(navigationInstruction.router.navigate('/xxx'));
                }
            }).catch(error => {
                toastr.error('Request error');
                resolve(false);
            });
        });
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM