我们有一个backbone.js应用程序,可以向用户显示多个表单。我们想要的是非常简单:如果用户转到另一页而不保存填写的表单,我们想要显示一个确认对话框。
在经典表单中,这很容易,只需在jQuerysh中实现window.onbeforeunload(或$(window).on('beforeunload'))。但骨干应用程序通常只有一个视图。我尝试使用onHashChange,但在该回调中返回false并不会阻止Backbone继续进入另一个视图。
赞赏指针。搜索互联网并没有找到任何有效的回复。
答案 0 :(得分:6)
我会避免使用Backbone进行攻击。您可以通过使用
之类的内容替换通常以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);
}
});
}
您当然需要将changesAreSaved
替换为有意义的内容,并添加有关处理链接的其他任何登录信息。
答案 1 :(得分:6)
我也会破解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);
}
};
Backbone收听 hashchange
事件并将Backbone.history.checkUrl
设置为回调:
https://github.com/jashkenas/backbone/blob/1.1.2/backbone.js#L1414
Backbone.$(window).on('hashchange', this.checkUrl);
Backbone.history.checkUrl
检查散列是否已更改并调用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();
},
Backbone.history.loadUrl
找到第一个匹配的路由并调用其回调:
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
事件之后访问它,但在路由器回调完成之前就可以访问它。
答案 2 :(得分:5)
我认为你可以破解Backbone.history.loadUrl(http://documentcloud.github.com/backbone/docs/backbone.html#section-137)。我做了一个快速测试,每次更改页面时,此代码都会进行检查。您只想在实际存在原因的情况下添加代码才能激活检查。
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.onbeforeunload,因为用户可能仍然完全离开页面。
答案 3 :(得分:1)
我已经处理了这个问题一段时间了,我想出了一个解决方案。我的解决方案基于this示例。
我们的想法是覆盖navigate
方法,并使用jQuery
deferred
个对象等待适当的时间进行导航。在我的情况下,如果用户试图从我的视图中导航是脏的,则需要显示一个对话框,询问用户是否:
1)保存更改,然后导航 2)不保存更改并导航 3)取消导航并保留在现有页面上
以下是Router
中导航方法的代码:
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
方法使用上面列出的三个选项显示对话框。根据用户的选择,我保存表格,然后解决导航的答案等。
答案 4 :(得分:1)
从版本1.2.0开始,您可以覆盖方法Router.execute
并返回false
以取消路由,如下所示:
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);
}
答案 5 :(得分:0)
我使用Dénes's solution在每次重新路由时多次调用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?'));