Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/377.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/backbone.js/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 如果内容未保存,则阻止导航到其他视图_Javascript_Backbone.js - Fatal编程技术网

Javascript 如果内容未保存,则阻止导航到其他视图

Javascript 如果内容未保存,则阻止导航到其他视图,javascript,backbone.js,Javascript,Backbone.js,我们有一个backbone.js应用程序,可以向用户显示许多表单。我们想要的非常简单:如果用户转到另一个页面而不保存填写的表单,我们希望显示一个确认对话框 在经典表单中,这非常简单,只需在jQuerysh中实现window.onbeforeunload(或$(window.on('beforeunload'))。但主干应用程序通常只有一个视图。我尝试过使用onHashChange,但是在回调中返回false并不能阻止主干仍然转到另一个视图 欢迎指点。搜索互联网站没有找到任何有效的响应。我想你可以

我们有一个backbone.js应用程序,可以向用户显示许多表单。我们想要的非常简单:如果用户转到另一个页面而不保存填写的表单,我们希望显示一个确认对话框

在经典表单中,这非常简单,只需在jQuerysh中实现window.onbeforeunload(或$(window.on('beforeunload'))。但主干应用程序通常只有一个视图。我尝试过使用onHashChange,但是在回调中返回false并不能阻止主干仍然转到另一个视图


欢迎指点。搜索互联网站没有找到任何有效的响应。

我想你可以破解Backbone.history.loadUrl()。我做了一个快速测试,这段代码会在每次更改页面时进行检查。您需要添加代码,以便仅在实际有原因时才激活支票

var goingBack = false;
function doCheck() {
  // TODO: add code that checks the app state that we have unsaved data
  return goingBack || window.confirm("Are you sure you want to change pages?");
}

var oldLoad = Backbone.History.prototype.loadUrl;
Backbone.History.prototype.loadUrl = function() {
  if(doCheck()) {
    return oldLoad.apply(this, arguments);
  } else {
    // change hash back
    goingBack = true;
    history.back();
    goingBack = false;
    return true;
  }
}

您还必须在卸载之前处理window.on,因为用户可能仍然会完全离开页面。

我会避免使用主干进行黑客攻击。通过将通常启动
Backbone.history
的部分替换为

initRouter: function () {
    Backbone.history.start({ pushState: true });
    $(document).on('click', 'a', function (ev) {
        var href = $(this).attr('href');
        ev.preventDefault();
        if (changesAreSaved) {
            router.navigate(href, true);
        }
    });
}

当然,您需要将保存的
更改替换为有意义的内容,并添加处理链接的任何其他登录名。

我处理这个问题已经有一段时间了,我已经想出了一个解决方案。我的解决方案基于一个例子

其思想是覆盖
导航
方法,并使用
jQuery
延迟
对象等待适当的时间进行导航。在我的情况下,如果用户试图从脏视图导航,则需要显示一个对话框,询问用户是否:

1) 保存更改,然后导航 2) 不保存更改,然后导航 3) 取消导航并保留在现有页面上

下面是我在
路由器中导航方法的代码:

navigate: function(fragment, trigger) {
    var answer,
          _this = this;

    answer = $.Deferred();
    answer.promise().then(function() {
        return Backbone.Router.prototype.navigate(fragment, trigger);
    });

    if(fragment !== undefined){     
        var splitRoute = fragment.split('/');
        app.currentPatronSection = splitRoute[splitRoute.length - 1];
    }

    if (app.recordChanged) {
        this.showConfirm(function(ans){
            // Clear out the currentView
            app.currentView = undefined;
            answer.resolve();
        }, function(){

        });
        return answer.promise();
    } else {
        return answer.resolve();
    }
    return Backbone.Router.prototype.navigate(fragment, trigger);

},

showConfirm
方法使用上面列出的三个选项显示对话框。根据用户的选择,我保存表单,然后解析导航的答案,等等。

我还会破解
Backbone.history.loadUrl
,这就是加载路由回调的地方

// ALLOW PREVENTING HASH NAVIGATION

var originalFn = Backbone.history.loadUrl;

Backbone.history.loadUrl = function() {
    // I introduced an application state variable, but it can be solved in multiple ways
    if (app && app.states.isNavigationBlocked) {
        var previousFragment = Backbone.history.fragment;
        window.location.hash = '#' + previousFragment;
        return false;
    }
    else {
        return originalFn.apply(this, arguments);
    }
};
说明: 1) 主干侦听
hashchange
事件
并将
主干.history.checkUrl
设置为回调:

2) 检查哈希是否已更改,并调用
Backbone.history.loadUrl

checkUrl: function(e) {
  var current = this.getFragment();
  if (current === this.fragment && this.iframe) {
    current = this.getFragment(this.getHash(this.iframe));
  }
  if (current === this.fragment) return false;
  if (this.iframe) this.navigate(current);
  this.loadUrl();
},
3) 查找第一个匹配路由并调用其回调:

loadUrl: function(fragment) {
  fragment = this.fragment = this.getFragment(fragment);
  return _.any(this.handlers, function(handler) {
    if (handler.route.test(fragment)) {
      handler.callback(fragment);
      return true;
    }
  });
},
有用注意事项:


Backbone.history.fragment
存储当前散列,它在
Backbone.history.loadUrl
中设置,因此我们可以在
hashchange
事件之后,但在路由器回调完成其工作之前访问它。

我使用每次重新路由都会收到多个调用
loadUrl
,所以我决定尝试这种方法,这对我很有用

/**
 * Monkey patches Backbone to prevent a reroute under certain conditions.
 *
 * Solution inspired by: https://stackoverflow.com/a/24535463/317135
 *
 * @param Backbone {Backbone}
 *   Backbone 1.0.0 reference
 * @param disallowRouting {function(): boolean}
 *   Function returning `true` when routing should be disallowed.
 */
export default function permitRouteWhen(Backbone, permitRouting) {
  if (Backbone.VERSION !== '1.0.0') {
    console.error(
      `WARNING: Expected to be hacking Backbone version 1.0.0, but got
      ${Backbone.VERSION} - this could fail.`
    );
  }

  const { checkUrl } = Backbone.history;

  Backbone.history.checkUrl = function(event) {
    if (!permitRouting()) {
      event.preventDefault();
      return;
    }
    return checkUrl.apply(this, arguments);
  }
}
像这样使用它:

import permitRouteWhen from './backbone-permit-route-hack';
permitRouteWhen(window.Backbone, () => confirm('you wanna route?'));
execute: function(callback, args, name) {
    if (!changesAreSaved) {
        // tip: .confirm returns false if "cancel" pressed
        return window.confirm("You sure have some unsaved "
          + "work here, you want to abandon it?");
    }

    // this is the default part of "execute" - running the router action
    if (callback)
        callback.apply(this, args);
}

从版本1.2.0开始,您可以覆盖方法
Router。执行
并返回
false
以取消路由,如下所示:

import permitRouteWhen from './backbone-permit-route-hack';
permitRouteWhen(window.Backbone, () => confirm('you wanna route?'));
execute: function(callback, args, name) {
    if (!changesAreSaved) {
        // tip: .confirm returns false if "cancel" pressed
        return window.confirm("You sure have some unsaved "
          + "work here, you want to abandon it?");
    }

    // this is the default part of "execute" - running the router action
    if (callback)
        callback.apply(this, args);
}

您是否尝试过用自己的代码包装原始主干“switch view”函数,以检查是否允许使用交换机?我使用了这个,它的工作原理与宣传的一样。或者实际上,我同时使用了this和$(window).on('beforeunload')来捕获这两种情况。我还在调用某些路由时设置了两个回调(添加和编辑路由),并在每一个其他路由事件中取消设置它们。用户使用浏览器的“后退”按钮导航回上一个视图如何?@OlliM这显然在这里不起作用。我认为这个用例并不是要强迫用户保存,而是要提醒他,如果他通过点击链接导航离开,他没有保存。如果后退按钮很重要,那么我的解决方案无法解决它。这也无法解决用户重新加载页面的情况。我认为onbeforeunload是必要的。我尝试添加一个确认对话框,用于导航被阻止的情况,但它似乎会多次弹出loadUrl,有时是2次,有时是3次。你知道为什么会这样吗?