2008-10-01 17 views
79

Ho un requisito per implementare un prompt "Modifiche non salvate" in un'applicazione ASP .Net. Se un utente modifica i controlli su un modulo Web e tenta di allontanarsi prima di salvare, dovrebbe apparire un messaggio che avverte che hanno delle modifiche non salvate e che gli consente di annullare e rimanere sulla pagina corrente. Il prompt non dovrebbe essere visualizzato se l'utente non ha toccato nessuno dei controlli.Rilevamento modifiche non salvate

Idealmente mi piacerebbe implementarlo in JavaScript, ma prima di intraprendere il percorso del rolling del mio codice, ci sono quadri esistenti o schemi di progettazione consigliati per raggiungere questo obiettivo? Idealmente mi piacerebbe qualcosa che possa essere facilmente riutilizzato su più pagine con modifiche minime.

+0

Sono interessato allo stesso, non solo per asp.net, ma penso che ci sia una soluzione generale là fuori. –

+0

La soluzione che ho postato di seguito può essere utilizzata in semplice HTML/Javscript. Basta cambiare il "codebehind" agli attributi nei tag HTML stessi. – Wayne

+0

So che ho 5 anni di ritardo qui, ma penso di poter migliorare le soluzioni precedenti ... Ho appena corretto e aggiornato la mia risposta di seguito. – skibulk

risposta

83

utilizzando jQuery:

var _isDirty = false; 
$("input[type='text']").change(function(){ 
    _isDirty = true; 
}); 
// replicate for other input types and selects 

combinano con onunload/onbeforeunload metodi come richiesto.

Dai commenti, i seguenti riferimenti tutti i campi di input, senza codice duplicazione:

$(':input').change(function() { 

Uso $(":input") si riferisce a tutti gli elementi di input, textarea, selezionare, e dei pulsanti.

+30

Voto in più perché ho usato questa soluzione ma c'è un modo leggermente più semplice. Se si utilizza: $ (': input'). Change (function() {che funziona per tutti i campi di input, non è necessario replicare per altri tipi di input. $ (": Input") seleziona tutto input, textarea , selezionare e gli elementi del pulsante – CodeClimber

+2

Penso che questo potrebbe avere problemi in Firefox.Se un utente modifica il modulo, quindi fa clic sul pulsante Aggiorna browser, la pagina verrà ricaricata e Firefox compilerà il modulo con le voci precedenti degli utenti - senza sparare il cambiamento-evento.Ora il modulo sarà sporco, ma il flag non verrà impostato a meno che l'utente non effettui un'altra modifica. –

+5

Solo un suggerimento. Se il modulo è sporco, ciò non significa che contenga dati realmente modificati. Per una migliore usabilità devi confrontare effettivamente i vecchi dati con quello nuovo;) –

17

Nella pagina aspx, è necessario una funzione JavaScript per dire se il Modulo è "sporco"

<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()"> 

e nel codebehind, aggiungere i trigger per i campi di input, così come si azzera relativo alla trasmissione pulsanti/annullare ....

btnSubmit.Attributes.Add("onclick", "isDirty = 0;"); 
btnCancel.Attributes.Add("onclick", "isDirty = 0;"); 
txtName.Attributes.Add("onchange", "setDirty();"); 
txtAddress.Attributes.Add("onchange", "setDirty();"); 
//etc.. 
0

One method, utilizzando gli array per contenere le variabili in modo modifiche possono essere monitorati.

Ecco un metodo molto semplice per detect changes, ma il resto non è elegante.

Un altro metodo che è abbastanza semplice e piccolo, da 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> 
41

Un pezzo del puzzle:

/** 
* 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; 
} 

And another:

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."; 
    } 
}; 

avvolgere tutto in su, e cosa ottieni?

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."); 

sarà probabilmente anche voler modificare la registrazione del gestore beforeunload eventi di utilizzare registrazione dell'evento LIBRARY_OF_CHOICE s'.

+0

In Firefox 9.0.1 non ricevo alcun messaggio. Ho un messaggio predefinito che dice "Sei sicuro di voler lasciare la pagina? Alcuni dati potrebbero andare persi" (Questo è più o meno la traduzione del messaggio che il mio browser non inglese mostra a me) –

+0

Funziona per le caselle di elenco (tag Li)? – Phil

+0

Questo script è fantastico, ma per me ha anche visualizzato il messaggio di avviso dopo aver inviato il mio modulo. Ho risolto questo problema disaggregando l'evento in un onClick ('onclick =" window.onbeforeunload = null "') – Joost

7

Grazie per le risposte a tutti. Ho finito per implementare una soluzione utilizzando JQuery e il plug-in Protect-Data. Questo mi consente di applicare automaticamente il monitoraggio a tutti i controlli su una pagina.

Tuttavia, ci sono alcuni avvertimenti, soprattutto quando si tratta di un ASP.applicazione netto:

  • Quando un utente sceglie l'opzione annullare, la funzione doPostBack getterà un errore JavaScript. Ho dovuto inserire manualmente un try-catch attorno alla chiamata .submit all'interno di doPostBack per sopprimerlo.

  • In alcune pagine, un utente può eseguire un'azione che esegue un postback alla stessa pagina, ma non un salvataggio è. Ciò si traduce in qualsiasi ripristino della logica JavaScript, quindi pensa che nulla è cambiato dopo il postback quando qualcosa può avere. Ho dovuto implementare una casella di testo nascosto che viene pubblicato di nuovo con la pagina, e viene usato per contenere un semplice valore booleano che indica se il dato è sporco. Questo viene mantenuto attraverso i postback.

  • Si consiglia alcune postback nella pagina per non innescare la finestra di dialogo, ad esempio un pulsante Salva. In questo caso, è possibile utilizzare JQuery per aggiungere una funzione che imposta OnClick window.onbeforeunload a null.

Speriamo che questo sia utile per chiunque altro debba implementare qualcosa di simile.

6

la seguente soluzione funziona per il prototipo (testato in FF, IE 6 e Safari). Esso utilizza una forma generica osservatore (che spara forma: modificate quando sono stati modificati tutti i campi del modulo), che può essere utilizzato per altre cose pure.

/* 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); 
9

Quanto segue utilizza la funzione onbeforeunload del browser e jquery per acquisire qualsiasi evento onchange. L'IT cerca anche i pulsanti di invio o di ripristino per reimpostare il contrassegno che indica che le modifiche si sono verificate.

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; 
+0

Il ripristino di Hi colin non ha funzionato quando ho inserito una casella di controllo, inizialmente l'ho controllato e deselezionato di nuovo ma l'avviso è stato attivato, come posso resettare quando l'utente deselezionata la casella di controllo – Dotnet

2

ho ampliato su suggerimento di Slace sopra, per includere elementi più modificabili e anche escludendo alcuni elementi (con uno stile CSS chiamato "srSearch" qui) di causare la bandiera sporca da impostare.

<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.'; 
      } 
     });   

4

Recentemente ho contribuito a un plugin jQuery open source chiamato dirtyForms.

Il plug-in è progettato per funzionare con HTML aggiunto dinamicamente, supporta più moduli, può supportare praticamente qualsiasi framework di dialogo, torna alla finestra di dialogo prima del caricamento del browser, ha un framework di supporto pluggable per supportare lo stato di dirty da editor personalizzati (un piccolo MCE plugin è incluso), funziona all'interno di iFrame e lo stato dirty può essere impostato o resettato a piacimento.

https://github.com/snikch/jquery.dirtyforms

5

Ecco una soluzione javascript/jquery che è semplice. Rappresenta "annullamento" da parte dell'utente, è incapsulato all'interno di una funzione per facilità di applicazione e non viene inficiato da invio. Basta chiamare la funzione e passare l'ID del modulo.

Questa funzione serializza il modulo una volta quando viene caricata la pagina e nuovamente prima che l'utente lasci la pagina. Se i due stati del modulo sono diversi, viene visualizzato il prompt.

Provalo: 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'); 
}); 
+2

ho provato il tuo approccio. Funziona per Chrome e Firefox, ma non può farlo funzionare su Safari su Ipad Mini .. –

2
 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;

4

rilevare i cambiamenti di forma con l'utilizzo di jQuery è molto semplice:

var formInitVal = $('#formId').serialize(); // detect form init value after form is displayed 

// check for form changes 
if ($('#formId').serialize() != formInitVal) { 
    // show confirmation alert 
} 
6

soluzione generale Sostenere molteplici forme in una determinata pagina (Basta copiare e incollare nel vostro progetto)

$(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; 
    }); 
}); 

Nota: Questa soluzione è combinata dalle soluzioni di altri per creare una soluzione integrata generale.

Caratteristiche:

  • Basta copiare e incollare nel vostro app.
  • Supporta moduli multipli.
  • È possibile applicare stili o eseguire azioni su moduli sporchi, poiché hanno la classe "form-dirty".
  • È possibile escludere alcuni moduli aggiungendo la classe 'ignore-changes'.
+0

Funziona bene, ma sostituisco le classi aggiunte con la variabile '_isDirty' come descritto da Aaron Powell. Non ho visto la necessità di aggiungere e rimuovere classi dall'HTML invece delle variabili nel javascript. – Martin

+0

L'idea di utilizzare le classi html qui è di allegare l'attributo 'dirty' ad ogni modulo dato che non si possono semplicemente usare le variabili per creare una soluzione generica application-wize js/html. – MhdSyrwan

+0

In altre parole, l'utilizzo di una variabile funzionerà solo per un singolo modulo, altrimenti sarà necessario un array di flag dirty che renderà la soluzione molto più complicata. – MhdSyrwan

0

In IE document.ready non funzionerà correttamente aggiornerà i valori di input.

quindi è necessario associare un evento di caricamento all'interno della funzione document.ready che gestirà anche il browser IE.

di seguito è il codice che si deve inserire all'interno della funzione document.ready.

$(document).ready(function() { 
    $(window).bind("load", function() { 
    $("input, select").change(function() {}); 
    }); 
}); 
Problemi correlati