Ci-dessous le code jusqu'ici
<!doctype html>
<html ng-app>
<head>
<script src="http://code.angularjs.org/1.1.2/angular.min.js"></script>
<script type="text/javascript">
function Ctrl($scope) {
var initial = {text: 'initial value'};
$scope.myModel = angular.copy(initial);
$scope.revert = function() {
$scope.myModel = angular.copy(initial);
$scope.myForm.$setPristine();
}
}
</script>
</head>
<body>
<form name="myForm" ng-controller="Ctrl">
myModel.text: <input name="input" ng-model="myModel.text">
<p>myModel.text = {{myModel.text}}</p>
<p>$pristine = {{myForm.$pristine}}</p>
<p>$dirty = {{myForm.$dirty}}</p>
<button ng-click="revert()">Set pristine</button>
</form>
</body>
</html>
Comment alerter sur browser close
ou url redirect
au cas où il y aurait des données non sauvegardées, afin que l'utilisateur puisse décider de continuer?
Quelque chose comme ça devrait le faire:
<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://code.angularjs.org/1.1.2/angular.min.js"></script>
<script type="text/javascript">
function Ctrl($scope) {
var initial = {text: 'initial value'};
$scope.myModel = angular.copy(initial);
$scope.revert = function() {
$scope.myModel = angular.copy(initial);
$scope.myForm.$setPristine();
}
}
angular.module("myApp", []).directive('confirmOnExit', function() {
return {
link: function($scope, elem, attrs) {
window.onbeforeunload = function(){
if ($scope.myForm.$dirty) {
return "The form is dirty, do you want to stay on the page?";
}
}
$scope.$on('$locationChangeStart', function(event, next, current) {
if ($scope.myForm.$dirty) {
if(!confirm("The form is dirty, do you want to stay on the page?")) {
event.preventDefault();
}
}
});
}
};
});
</script>
</head>
<body>
<form name="myForm" ng-controller="Ctrl" confirm-on-exit>
myModel.text: <input name="input" ng-model="myModel.text">
<p>myModel.text = {{myModel.text}}</p>
<p>$pristine = {{myForm.$pristine}}</p>
<p>$dirty = {{myForm.$dirty}}</p>
<button ng-click="revert()">Set pristine</button>
</form>
</body>
</html>
Notez que l'écouteur de $ locationChangeStart n'est pas déclenché dans cet exemple car AngularJS ne gère aucun routage dans un exemple aussi simple, mais qu'il devrait fonctionner dans une application Angular réelle.
J'ai étendu la réponse @Anders pour nettoyer les écouteurs (non-rattachés aux listes) lorsque la directive est détruite (par exemple, lorsque la route change), et ajouté du sucre syntaxique pour généraliser l'utilisation.
Directive confirmOnExit :
/**
* @name confirmOnExit
*
* @description
* Prompts user while he navigating away from the current route (or, as long as this directive
* is not destroyed) if any unsaved form changes present.
*
* @element Attribute
* @scope
* @param confirmOnExit Scope function which will be called on window refresh/close or AngularS $route change to
* decide whether to display the Prompt or not.
* @param confirmMessageWindow Custom message to display before browser refresh or closed.
* @param confirmMessageRoute Custom message to display before navigating to other route.
* @param confirmMessage Custom message to display when above specific message is not set.
*
* @example
* Usage:
* Example Controller: (using controllerAs syntax in this example)
*
* angular.module('AppModule', []).controller('pageCtrl', [function () {
* this.isDirty = function () {
* // do your logic and return 'true' to display the Prompt, or 'false' otherwise.
* return true;
* };
* }]);
*
* Template:
*
* <div confirm-on-exit="pageCtrl.isDirty()"
* confirm-message-window="All your changes will be lost."
* confirm-message-route="All your changes will be lost. Are you sure you want to do this?">
*
* @see
* http://stackoverflow.com/a/28905954/340290
*
* @author Manikanta G
*/
ngxDirectivesModule.directive('confirmOnExit', function() {
return {
scope: {
confirmOnExit: '&',
confirmMessageWindow: '@',
confirmMessageRoute: '@',
confirmMessage: '@'
},
link: function($scope, elem, attrs) {
window.onbeforeunload = function(){
if ($scope.confirmOnExit()) {
return $scope.confirmMessageWindow || $scope.confirmMessage;
}
}
var $locationChangeStartUnbind = $scope.$on('$locationChangeStart', function(event, next, current) {
if ($scope.confirmOnExit()) {
if(! confirm($scope.confirmMessageRoute || $scope.confirmMessage)) {
event.preventDefault();
}
}
});
$scope.$on('$destroy', function() {
window.onbeforeunload = null;
$locationChangeStartUnbind();
});
}
};
});
Utilisation: Exemple Controller : (en utilisant la syntaxe controllerAs dans cet exemple)
angular.module('AppModule', []).controller('pageCtrl', [function () {
this.isDirty = function () {
// do your logic and return 'true' to display the Prompt, or 'false' otherwise.
return true;
};
}]);
Modèle :
<div confirm-on-exit="pageCtrl.isDirty()"
confirm-message-window="All your changes will be lost."
confirm-message-route="All your changes will be lost. Are you sure you want to do this?">
La réponse d'Anders fonctionne bien. Toutefois, pour les personnes utilisant un routeur angulaire ui, vous devez utiliser '$stateChangeStart'
au lieu de '$locationChangeStart'
.
J'ai modifié la réponse @Anders afin que la directive ne contienne pas le nom du formulaire codé en dur:
app.directive('confirmOnExit', function() {
return {
link: function($scope, elem, attrs, ctrl) {
window.onbeforeunload = function(){
if ($scope[attrs["name"]].$dirty) {
return "Your edits will be lost.";
}
}
}
};
});
Voici le code html pour cela:
<form name="myForm" confirm-on-exit>
Peut-être que ce sera utile pour quelqu'un . https://github.com/umbrella-web/Angular-unsavedChanges
En utilisant ce service, vous pouvez écouter les modifications non enregistrées pour n’importe quel objet de l’étendue (pas seulement le formulaire).
Pour utiliser l'excellente réponse de Anders Ekdahl avec un composant Angular 1.5, injectez le $scope
dans le contrôleur du composant:
angular
.module('myModule')
.component('myComponent', {
controller: ['$routeParams', '$scope',
function MyController($routeParams, $scope) {
var self = this;
$scope.$on('$locationChangeStart', function (event, next, current) {
if (self.productEdit.$dirty && !confirm('There are unsaved changes. Would you like to close the form?')) {
event.preventDefault();
}
});
}
]
});
La réponse acceptée est excellente, mais j’ai eu un problème avec l’obtention d’un descriptif correct sur mon contrôleur de formulaire de manière cohérente, car certains formulaires utilisent la balise form
avec l’attribut name
et, à d’autres moments, la directive ng-form
. De même, si vous utilisez des fonctions de style TypeScript, utilisez le modèle de type this
ou vm
, par ex. <form name='$ctrl.myForm'...
Je suis surpris que personne d'autre n'ait mentionné cela, mais ma solution était d'utiliser la propriété require de la directive et de laisser angular me donner une référence au contrôleur de formulaire lui-même.
J'ai mis à jour la réponse acceptée ci-dessous pour afficher mes modifications, notez la propriété require et le paramètre supplémentaire de la fonction de liaison.
angular.module("myApp", []).directive('confirmOnExit', function() {
return {
restrict: 'A',
require: 'form',
link: function($scope, elem, attrs, form) {
window.onbeforeunload = function(){
if (form.$dirty) {
return "The form is dirty, do you want to stay on the page?";
}
}
$scope.$on('$locationChangeStart', function(event, next, current) {
if (form.$dirty) {
if(!confirm("The form is dirty, do you want to stay on the page?")) {
event.preventDefault();
}
}
});
}
};
});
Avec cela, je peux garantir que je maîtrise bien le contrôleur de formulaire, car angular émettra une erreur s’il ne parvient pas à trouver un contrôleur de formulaire sur l’élément.
Vous pouvez également ajouter des modificateurs tels que ^ et? tel que require='^form'
pour extraire un formulaire ancêtre ou require='?form'
si le formulaire est optionnel (cela ne cassera pas la directive, mais vous devrez vérifier que vous avez vous-même un descripteur sur un contrôleur de formulaire valide).