重定向
Sometimes, your users will end up on a page that isn't a good place for them to be. Maybe the data they were looking for has moved, maybe they were on an admin panel page and logged out, or maybe they just created a new object and you want them to end up on the page for the thing they just created.
Usually, we can redirect in response to a user's action by calling FlowRouter.go()
and friends, like in our list creation example above, but if a user browses directly to a URL that doesn't exist, it's useful to know how to redirect immediately.
If a URL is simply out-of-date (sometimes you might change the URL scheme of an application), you can redirect inside the action
function of the route:
FlowRouter.route('/old-list-route/:_id', {
action(params) {
FlowRouter.go('Lists.show', params);
}
});
Redirecting dynamically
The above approach will only work for static redirects. However, sometimes you need to load some data to figure out where to redirect to. In this case you'll need to render part of the component hierarchy to subscribe to the data you need. For example, in the Todos example app, we want to make the root (/
) route redirect to the first known list. To achieve this, we need to render a special App_rootRedirector
route:
FlowRouter.route('/', {
name: 'App.home',
action() {
BlazeLayout.render('App_body', {main: 'App_rootRedirector'});
}
});
The App_rootRedirector
component is rendered inside the App_body
layout, which takes care of subscribing to the set of lists the user knows about before rendering its sub-component, and we are guaranteed there is at least one such list. This means that if the App_rootRedirector
ends up being created, there'll be a list loaded, so we can simply do:
Template.App_rootRedirector.onCreated(() => {
// We need to set a timeout here so that we don't redirect from inside a redirection
// which is a limitation of the current version of FR.
Meteor.setTimeout(() => {
FlowRouter.go('Lists.show', Lists.findOne());
});
});
If you need to wait on specific data that you aren't already subscribed to at creation time, you can use an autorun
and subscriptionsReady()
to wait on that subscription:
Template.App_rootRedirector.onCreated(() => {
// If we needed to open this subscription here
this.subscribe('lists.public');
// Now we need to wait for the above subscription. We'll need the template to
// render some kind of loading state while we wait, too.
this.autorun(() => {
if (this.subscriptionsReady()) {
FlowRouter.go('Lists.show', Lists.findOne());
}
});
});
Redirecting after a user's action
Often, you just want to go to a new route programmatically when a user has completed a certain action. Above we saw a case (creating a new list) when we wanted to do it optimistically---i.e. before we hear back from the server that the Method succeeded. We can do this because we reasonably expect that the Method will succeed in almost all cases (see the UI/UX article for further discussion of this).
However, if we wanted to wait for the method to return from the server, we can put the redirection in the callback of the method:
Template.App_body.events({
'click .js-new-list'() {
lists.insert.call((err, listId) => {
if (!err) {
FlowRouter.go('Lists.show', { _id: listId });
}
});
}
});
You will also want to show some kind of indication that the method is working in between their click of the button and the redirect completing. Don't forget to provide feedback if the method is returning an error.