解決しようとしているこの問題全体の根底には、onhashchange と onbeforeunload という 2 つの DOM イベントがあります。onhashchange はチェックおよび防止できますが、ブラウザの [戻る] ボタンでは onhashchange はトリガーされません。さらに悪いことに、ページがリロードされない場合、onbeforeunload はトリガーされません。つまり、[戻る] をクリックしてページの前のハッシュに移動しても、起動しません。このため、[戻る] を押して前のルートに移動すると、フォームから離れます。
現在、ルートのキャンセルをどのように許可するかについて、Angular の todo リストにも未解決の問題があります。ハッシュの問題への戻るボタンが、この時点で彼らを妨げているものだと思います。
したがって、最終的には、フォームを編集した後にすべてのナビゲーションがフォームから離れるのを防ぎたい場合は、ソリューションを再設計して、もう少し抜本的なことを行うことができます。フォームが編集しているすべてのデータを $rootScope に保存し、それがダーティだが不完全であることを示すフラグを付けてから、イベント ハンドラーを routeChangeStart に追加して、それらの値をチェックし、フォームに送り返します。
それがどのように機能するかは次のとおりです(興味がある場合はプランカーも):
app.config(function($routeProvider) {
//set up the routes. (Important because we're going to navigate
// BACK to them.)
$routeProvider.when('/Form', {
controller: 'FormCtrl',
templateUrl: 'form.html'
}).otherwise({
controller: 'HomeCtrl',
template: '<h3>Home</h3>'
});
});
app.run(function($rootScope, $location){
//set up your rootScope formData object.
$rootScope.formData = {};
//add a routing event to check the route
// and whether or not the data has been editted and then
// send it back to the proper form.
$rootScope.$on('$routeChangeStart', function() {
if($location.path() != '/Form' && $rootScope.formData.dirty &&
!$rootScope.formData.complete && !confirm('Do you want to leave this form?')) {
$location.path('/Form');
}
});
//handle outright navigating away from the page.
$(window).on('beforeunload', function() {
if($rootScope.formData.dirty &&
!$rootScope.formData.complete) {
return 'Are you sure you want to navigate away from this form?';
}
});
});
app.controller('FormCtrl', function($scope) {
$scope.$watch(function (){
return $scope.myForm.$dirty;
}, function(dirty) {
$scope.formData.dirty = $scope.formData.dirty | dirty;
})
});
他の考え
最初にこれを支援するディレクティブを作成しましたが、上記の問題のために機能しないことに気付きました。とにかく、後世のために、ここにあります:
app.directive('form', function ($window){
return {
restrict: 'E',
link: function(scope, elem, attrs) {
//check for a prevent-if-dirty attribute on your form tag
if(attrs.preventIfDirty !== undefined) {
// first off, stop routing hash changes from
// changing the page.
scope.$on('$locationChangeStart', function(event) {
if(scope.testForm.$dirty) {
event.preventDefault();
}
});
// a little setup for our next piece
var formName = attrs.name;
function showWarning() {
return 'You have changed the form';
}
// Now stop browser navigation from moving away
// from your dirty form.
scope.$watch(function (){
return scope[formName].$dirty;
}, function(dirty) {
if(dirty) {
$(window).on('beforeunload', showWarning);
} else {
$(window).off('beforeunload', showWarning);
}
});
}
}
};
});
これは、それを示すプランカーです。