Javascript 如果内容未保存,则阻止导航到其他视图
我们有一个backbone.js应用程序,可以向用户显示许多表单。我们想要的非常简单:如果用户转到另一个页面而不保存填写的表单,我们希望显示一个确认对话框 在经典表单中,这非常简单,只需在jQuerysh中实现window.onbeforeunload(或$(window.on('beforeunload'))。但主干应用程序通常只有一个视图。我尝试过使用onHashChange,但是在回调中返回false并不能阻止主干仍然转到另一个视图Javascript 如果内容未保存,则阻止导航到其他视图,javascript,backbone.js,Javascript,Backbone.js,我们有一个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次。你知道为什么会这样吗?