用流星铁路由器中止导航

时间:2014-06-23 14:00:03

标签: meteor iron-router

我在Meteor应用程序中有一个(客户端)路由器,并使用{{pathFor}}帮助器进行链接。

当用户更改表单字段时,我在dirty中设置Session标记,并且我想触发警告并允许用户停止导航离开页面,如果标记是设置,基本上像onunload处理程序。

我试图这样做:

Router.onBeforeAction(function(pause) {
    var self = this;

    if (!this.ready()) {
        return;
    }

    if(Session.get('dirty')) {
        if(!confirm("Are you sure you want to navigate away?")) {
            pause();
        }
    }
});

然而,当我得到提示时,我仍然被导航了。也就是说,pause()似乎不会阻止后续路由器操作,无论它是什么。

我做错了什么?

5 个答案:

答案 0 :(得分:6)

据我所知,铁路由器API无法实现这一点。但你可以做的是覆盖Router.go方法,就像这样(在你的客户端代码中的某个地方):

var go = Router.go; // cache the original Router.go method
Router.go = function () {
  if(Session.get('dirty')) {
    if (confirm("Are you sure you want to navigate away?")) {
      go.apply(this, arguments);
    }
  } else {
    go.apply(this, arguments);
  }
};

答案 1 :(得分:0)

这是某个你想去的地方吗?还有Router.go(routeName),它将使页面指向给定的routeName。我想要的是,也许你可以强迫路由器转到当前页面,因此忽略了后退动作。

答案 2 :(得分:0)

铁路由器的新行为应该使这更容易,因为它需要在onBeforeAction挂钩中调用this.next()(请参阅iron router guide),因此只在会话不脏或用户时调用它确认警告:

if(Session.get('dirty')) {
    if(confirm("Are you sure you want to navigate away?")) {
        this.next();
    }
} else {
    this.next();
}

答案 3 :(得分:0)

我发现stop中的重定位有效,即使您没有通过Router.go更改路由(例如我的应用程序中的链接)也能正常工作。

以下是使用继承自RouteController

的类的coffeescript实现
class MyRouteController extends RouteController
  stop: ->
    # Save whether you data/form is dirty or whatever state you have in 
    # a Session variable.
    if Session.get('formIsDirty') 
      if !confirm('You have unsaved data. Are you sure you want to leave?')
        # Redirecting to the current route stops the current navigation.
        # Although, it does rerun the route, so it isn't a perfect solution.
        Router.go '/my_route'
        # Return here so we don't perform any more of the stop operation.
        return
    # Otherwise do as normal.
    super

答案 4 :(得分:0)

Iron Router API无法提供实现此目的的简便方法。无法取消onBeforeAction挂钩的持续转换。它必须通过重定向到前一个路径来解决。

/*
 * Adds a confirmation dialogue when the current route contains unsaved changes.
 *
 * This is tricky because Iron Router doesn't support this out of the box, and
 * the reactivity gets in the way.
 * In this solution, redirecting to the current route is abused
 * as a mechanism to stop the current transition, which Iron Router has no API
 * for. Because the redirect would trigger the onStop hook, we keep track of
 * whether to run the onStop hook or not ourselves in
 * `skipConfirmationForNextTransition`.
 *
 * When `Session.get('formIsDirty')` returns `true`, the user will be asked
 * whether he really wants to leave the route or not.
 *
 * Further, another confirmation is added in case the browser window is closed
 * with unsaved data.
 * 
 * This gist shows the basics of how to achieve a navigation confirmation,
 * also known as canceling a route transition.
 * This approach may fail if other route hooks trigger reruns of hooks reactively.
 * Maybe setting `skipConfirmationForNextTransition` to `true` could help in those
 * cases.
 */
Session.setDefault('formIsDirty', false)
const confirmationMessage = 'You have unsaved data. Are you sure you want to leave?'

// whether the user should confirm the navigation or not,
// set to `true` before redirecting programmatically to skip confirmation
let skipConfirmationForNextTransition = false
Router.onStop(function () {
  // register dependencies immediately
  const formIsDirty = Session.equals('formIsDirty', true)
  // prevent duplicate execution of onStop route, because it would run again
  // after the redirect
  if (skipConfirmationForNextTransition) {
    skipConfirmationForNextTransition = false
    return
  }
  if (formIsDirty) {
    const shouldLeave = confirm(confirmationMessage)
    if (shouldLeave) {
      Session.set('formIsDirty', false)
      return
    }
    // obtain a non-reactive reference to the current route
    let currentRoute
    Tracker.nonreactive(function () {
      currentRoute = Router.current()
    })
    skipConfirmationForNextTransition = true
    // "cancel" the transition by redirecting to the same route
    // this had to be used because Iron Router doesn't support cancling the
    // current transition. `url` contains the query params and hash.
    this.redirect(currentRoute.url)
    return
  }
})

// Bonus: confirm closing of browser window
window.addEventListener('beforeunload', event => {
  if (Session.get('formIsDirty')) {
    // cross-browser requries returnValue to be set, as well as an actual
    // return value
    event.returnValue = confirmationMessage // eslint-disable-line no-param-reassign
    return confirmationMessage
  }
})

可以找到最新版本in this gist