Mam wymóg zaimplementowania monitu „Niezapisane zmiany” w aplikacji ASP .Net. Jeśli użytkownik zmodyfikuje formanty w formularzu internetowym i spróbuje opuścić stronę przed zapisaniem, powinien pojawić się monit ostrzegający go o niezapisanych zmianach i umożliwiający anulowanie i pozostanie na bieżącej stronie. Monit nie powinien być wyświetlany, jeśli użytkownik nie dotknął żadnego elementu sterującego.
Idealnie chciałbym zaimplementować to w JavaScript, ale zanim przejdę na ścieżkę rozwijania własnego kodu, czy są jakieś istniejące frameworki lub zalecane wzorce projektowe, aby to osiągnąć? Idealnie byłoby coś, co można łatwo ponownie wykorzystać na wielu stronach przy minimalnych zmianach.
javascript
asp.net
prompt
tbreffni
źródło
źródło
Odpowiedzi:
Korzystanie z jQuery:
var _isDirty = false; $("input[type='text']").change(function(){ _isDirty = true; }); // replicate for other input types and selects
W razie potrzeby połącz z
onunload
/onbeforeunload
Methods.Poniższe komentarze odnoszą się do wszystkich pól wejściowych bez powielania kodu:
$(':input').change(function () {
Użycie
$(":input")
odnosi się do wszystkich elementów wejściowych, tekstowych, zaznaczania i przycisków.źródło
change
przypadku wprowadzania tekstu wyzwalane są jednorazowo po opuszczeniu / utracie fokusu, natomiastinput
wyzwalacze przy każdym naciśnięciu klawisza. (Zwykle używam go razemchange
z JQueryone()
, aby zapobiec wielu zdarzeniom.)Jeden element układanki:
/** * Determines if a form is dirty by comparing the current value of each element * with its default value. * * @param {Form} form the form to be checked. * @return {Boolean} <code>true</code> if the form is dirty, <code>false</code> * otherwise. */ function formIsDirty(form) { for (var i = 0; i < form.elements.length; i++) { var element = form.elements[i]; var type = element.type; if (type == "checkbox" || type == "radio") { if (element.checked != element.defaultChecked) { return true; } } else if (type == "hidden" || type == "password" || type == "text" || type == "textarea") { if (element.value != element.defaultValue) { return true; } } else if (type == "select-one" || type == "select-multiple") { for (var j = 0; j < element.options.length; j++) { if (element.options[j].selected != element.options[j].defaultSelected) { return true; } } } } return false; }
I jeszcze :
window.onbeforeunload = function(e) { e = e || window.event; if (formIsDirty(document.forms["someForm"])) { // For IE and Firefox if (e) { e.returnValue = "You have unsaved changes."; } // For Safari return "You have unsaved changes."; } };
Podsumuj to wszystko i co otrzymasz?
var confirmExitIfModified = (function() { function formIsDirty(form) { // ...as above } return function(form, message) { window.onbeforeunload = function(e) { e = e || window.event; if (formIsDirty(document.forms[form])) { // For IE and Firefox if (e) { e.returnValue = message; } // For Safari return message; } }; }; })(); confirmExitIfModified("someForm", "You have unsaved changes.");
Prawdopodobnie będziesz również chciał zmienić rejestrację programu
beforeunload
obsługi zdarzeń, aby używałaLIBRARY_OF_CHOICE
rejestracji zdarzeń.źródło
onclick="window.onbeforeunload=null"
)Na stronie .aspx potrzebujesz funkcji Javascript, aby stwierdzić, czy informacje w formularzu są „brudne”
<script language="javascript"> var isDirty = false; function setDirty() { isDirty = true; } function checkSave() { var sSave; if (isDirty == true) { sSave = window.confirm("You have some changes that have not been saved. Click OK to save now or CANCEL to continue without saving."); if (sSave == true) { document.getElementById('__EVENTTARGET').value = 'btnSubmit'; document.getElementById('__EVENTARGUMENT').value = 'Click'; window.document.formName.submit(); } else { return true; } } } </script> <body class="StandardBody" onunload="checkSave()">
aw polu za kodem dodaj wyzwalacze do pól wejściowych, a także zresetuj na przyciskach przesyłania / anulowania ....
btnSubmit.Attributes.Add("onclick", "isDirty = 0;"); btnCancel.Attributes.Add("onclick", "isDirty = 0;"); txtName.Attributes.Add("onchange", "setDirty();"); txtAddress.Attributes.Add("onchange", "setDirty();"); //etc..
źródło
Poniższe elementy wykorzystują funkcję onbeforeunload przeglądarki i jQuery do przechwytywania dowolnego zdarzenia onchange. IT szuka również jakichkolwiek przycisków przesyłania lub resetowania, aby zresetować flagę wskazującą na zmiany.
dataChanged = 0; // global variable flags unsaved changes function bindForChange(){ $('input,checkbox,textarea,radio,select').bind('change',function(event) { dataChanged = 1}) $(':reset,:submit').bind('click',function(event) { dataChanged = 0 }) } function askConfirm(){ if (dataChanged){ return "You have some unsaved changes. Press OK to continue without saving." } } window.onbeforeunload = askConfirm; window.onload = bindForChange;
źródło
Dzięki za odpowiedzi wszystkim. Skończyło się na wdrożeniu rozwiązania przy użyciu JQuery i Protect-Data wtyczki . Dzięki temu mogę automatycznie stosować monitorowanie do wszystkich kontrolek na stronie.
Jest jednak kilka zastrzeżeń, szczególnie w przypadku aplikacji ASP .Net:
Gdy użytkownik wybierze opcję anulowania, funkcja doPostBack zgłosi błąd JavaScript. Musiałem ręcznie umieścić próbny catch wokół wywołania .submit w doPostBack, aby je stłumić.
Na niektórych stronach użytkownik może wykonać akcję, która wykonuje ogłaszanie zwrotne na tej samej stronie, ale nie jest zapisem. Powoduje to zresetowanie logiki JavaScript, więc uważa, że nic się nie zmieniło po ogłoszeniu zwrotnym, gdy coś mogło się zmienić. Musiałem zaimplementować ukryte pole tekstowe, które jest wysyłane z powrotem ze stroną i służy do przechowywania prostej wartości logicznej wskazującej, czy dane są brudne. Problem ten utrzymuje się w ogłoszeniach zwrotnych.
Możesz chcieć, aby niektóre ogłoszenia zwrotne na stronie nie wywoływały okna dialogowego, na przykład przycisk Zapisz. W takim przypadku możesz użyć JQuery, aby dodać funkcję OnClick, która ustawia wartość window.onbeforeunload na null.
Mam nadzieję, że jest to pomocne dla każdego, kto musi wdrożyć coś podobnego.
źródło
Rozwiązanie ogólne Obsługa wielu formularzy na jednej stronie ( wystarczy skopiować i wkleić w projekcie )
$(document).ready(function() { $('form :input').change(function() { $(this).closest('form').addClass('form-dirty'); }); $(window).bind('beforeunload', function() { if($('form:not(.ignore-changes).form-dirty').length > 0) { return 'You have unsaved changes, are you sure you want to discard them?'; } }); $('form').bind('submit',function() { $(this).closest('form').removeClass('form-dirty'); return true; }); });
Uwaga: to rozwiązanie jest połączone z rozwiązaniami innych, aby stworzyć ogólne zintegrowane rozwiązanie.
Cechy:
źródło
_isDirty
zmienną opisaną przez Aarona Powella. Nie widziałem potrzeby dodawania i usuwania klas z HTML zamiast zmiennych w javascript.Poniższe rozwiązanie działa dla prototypu (testowane w FF, IE 6 i Safari). Używa ogólnego obserwatora formularza (który uruchamia formularz: zmieniany, gdy dowolne pola formularza zostały zmodyfikowane), którego możesz użyć również do innych rzeczy.
/* use this function to announce changes from your own scripts/event handlers. * Example: onClick="makeDirty($(this).up('form'));" */ function makeDirty(form) { form.fire("form:changed"); } function handleChange(form, event) { makeDirty(form); } /* generic form observer, ensure that form:changed is being fired whenever * a field is being changed in that particular for */ function setupFormChangeObserver(form) { var handler = handleChange.curry(form); form.getElements().each(function (element) { element.observe("change", handler); }); } /* installs a form protector to a form marked with class 'protectForm' */ function setupProtectForm() { var form = $$("form.protectForm").first(); /* abort if no form */ if (!form) return; setupFormChangeObserver(form); var dirty = false; form.observe("form:changed", function(event) { dirty = true; }); /* submitting the form makes the form clean again */ form.observe("submit", function(event) { dirty = false; }); /* unfortunatly a propper event handler doesn't appear to work with IE and Safari */ window.onbeforeunload = function(event) { if (dirty) { return "There are unsaved changes, they will be lost if you leave now."; } }; } document.observe("dom:loaded", setupProtectForm);
źródło
Oto proste rozwiązanie javascript / jquery. Uwzględnia "cofnięcie" przez użytkownika, jest zawarty w funkcji dla ułatwienia aplikacji i nie zapala się po przesłaniu. Po prostu wywołaj funkcję i podaj identyfikator swojego formularza.
Ta funkcja serializuje formularz raz po załadowaniu strony i ponownie przed opuszczeniem strony przez użytkownika. Jeśli dwa stany formularza są różne, zostanie wyświetlony monit.
Wypróbuj: http://jsfiddle.net/skibulk/Ydt7Y/
function formUnloadPrompt(formSelector) { var formA = $(formSelector).serialize(), formB, formSubmit = false; // Detect Form Submit $(formSelector).submit( function(){ formSubmit = true; }); // Handle Form Unload window.onbeforeunload = function(){ if (formSubmit) return; formB = $(formSelector).serialize(); if (formA != formB) return "Your changes have not been saved."; }; } $(function(){ formUnloadPrompt('form'); });
źródło
Niedawno współtworzyłem wtyczkę open source jQuery o nazwie dirtyForms .
Wtyczka jest zaprojektowana do pracy z dynamicznie dodawanym kodem HTML, obsługuje wiele formularzy, może obsługiwać praktycznie każdą strukturę okna dialogowego, wraca do przeglądarki przed dialogiem pobierania, ma podłączaną platformę pomocniczą, która obsługuje pobieranie statusu od niestandardowych edytorów (dołączona jest wtyczka tinyMCE) , działa w elementach iFrames, a stan „zabrudzenia” można dowolnie ustawiać lub resetować.
https://github.com/snikch/jquery.dirtyforms
źródło
Wykrywanie zmian w formularzach za pomocą jQuery jest bardzo proste:
var formInitVal = $('#formId').serialize(); // detect form init value after form is displayed // check for form changes if ($('#formId').serialize() != formInitVal) { // show confirmation alert }
źródło
Rozszerzyłem powyższą sugestię Slace'a, aby uwzględnić większość edytowalnych elementów, a także wykluczyć niektóre elementy (ze stylem CSS o nazwie „srSearch”), które powodują ustawienie flagi dirty.
<script type="text/javascript"> var _isDirty = false; $(document).ready(function () { // Set exclude CSS class on radio-button list elements $('table.srSearch input:radio').addClass("srSearch"); $("input[type='text'],input[type='radio'],select,textarea").not(".srSearch").change(function () { _isDirty = true; }); }); $(window).bind('beforeunload', function () { if (_isDirty) { return 'You have unsaved changes.'; } });
źródło
var unsaved = false; $(":input").change(function () { unsaved = true; }); function unloadPage() { if (unsaved) { alert("You have unsaved changes on this page. Do you want to leave this page and discard your changes or stay on this page?"); } }
window.onbeforeunload = unloadPage;
źródło
To jest dokładnie to, co wtyczka Fleegix.js
fleegix.form.diff
(http://js.fleegix.org/plugins/form/diff ) został utworzony dla. Serializuj początkowy stan formularza podczas ładowania przy użyciufleegix.form.toObject
(http://js.fleegix.org/ref#fleegix.form.toObject ) i zapisz go w zmiennej, a następnie porównaj z bieżącym stanem za pomocą funkcjifleegix.form.diff
on unload. Proste jak ciasto.źródło
Jedna metoda polega na użyciu tablic do przechowywania zmiennych, aby można było śledzić zmiany.
Oto bardzo prosta metoda wykrywania zmian , ale reszta nie jest tak elegancka.
Inna metoda, która jest dość prosta i niewielka, z Farfetched Blog :
<body onLoad="lookForChanges()" onBeforeUnload="return warnOfUnsavedChanges()"> <form> <select name=a multiple> <option value=1>1 <option value=2>2 <option value=3>3 </select> <input name=b value=123> <input type=submit> </form> <script> var changed = 0; function recordChange() { changed = 1; } function recordChangeIfChangeKey(myevent) { if (myevent.which && !myevent.ctrlKey && !myevent.ctrlKey) recordChange(myevent); } function ignoreChange() { changed = 0; } function lookForChanges() { var origfunc; for (i = 0; i < document.forms.length; i++) { for (j = 0; j < document.forms[i].elements.length; j++) { var formField=document.forms[i].elements[j]; var formFieldType=formField.type.toLowerCase(); if (formFieldType == 'checkbox' || formFieldType == 'radio') { addHandler(formField, 'click', recordChange); } else if (formFieldType == 'text' || formFieldType == 'textarea') { if (formField.attachEvent) { addHandler(formField, 'keypress', recordChange); } else { addHandler(formField, 'keypress', recordChangeIfChangeKey); } } else if (formFieldType == 'select-multiple' || formFieldType == 'select-one') { addHandler(formField, 'change', recordChange); } } addHandler(document.forms[i], 'submit', ignoreChange); } } function warnOfUnsavedChanges() { if (changed) { if ("event" in window) //ie event.returnValue = 'You have unsaved changes on this page, which will be discarded if you leave now. Click "Cancel" in order to save them first.'; else //netscape return false; } } function addHandler(target, eventName, handler) { if (target.attachEvent) { target.attachEvent('on'+eventName, handler); } else { target.addEventListener(eventName, handler, false); } } </script>
źródło
W IE document.ready nie będzie działać poprawnie, zaktualizuje wartości danych wejściowych.
więc musimy powiązać zdarzenie load wewnątrz funkcji document.ready, która będzie obsługiwać również przeglądarkę IE.
poniżej znajduje się kod, który należy umieścić w funkcji document.ready.
$(document).ready(function () { $(window).bind("load", function () { $("input, select").change(function () {}); }); });
źródło