Pergunta

Eu tenho um requisito para implementar uma "Unsaved Changes" prompt em um aplicativo ASP .Net. Se um usuário modificar controles em um formulário web, e as tentativas para navegar para longe antes de poupança, um aviso deve aparecer alertando-os de que eles têm alterações não salvas, e dar-lhes a opção de cancelar e estadia na página atual. O prompt não deve exibir se o usuário não tenha tocado qualquer um dos controles.

Idealmente, eu gostaria de implementar isso em JavaScript, mas antes de eu ir para o caminho de rolar meu próprio código, existem quadros existentes ou padrões de projeto recomendadas para alcançar este objectivo? Idealmente, eu gostaria de algo que pode ser facilmente reutilizados em várias páginas com alterações mínimas.

Foi útil?

Solução

Usando jQuery:

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

Combine com métodos onunload / onbeforeunload conforme necessário.

A partir dos comentários, as seguintes referências todos os campos de entrada, sem duplicação de código:

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

Usando $(":input") refere-se a todas as entradas, textarea, selecione, e elementos de botão.

Outras dicas

Uma peça do quebra-cabeça:

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

E outra :

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

Enrole tudo, eo que você recebe?

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

Você provavelmente também quer alterar o registro do manipulador de eventos beforeunload para registro de eventos uso de LIBRARY_OF_CHOICE.

Na página .aspx, você precisa de uma função JavaScript para dizer se ou não a informação formulário é "sujo"

<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 no codebehind, adicione os gatilhos para os campos de entrada, bem como redefine sobre a apresentação / cancelar botões ....

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

A seguir tem função onbeforeunload do navegador e jQuery para capturar qualquer evento onchange. Ela também olha para qualquer enviar ou botões de reset para repor a bandeira ter ocorrido alterações que indiquem.

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;

Obrigado pelas respostas a todos. Acabei implementar uma solução usando JQuery eo Protect-Data plug-in. Isso me permite aplicar automaticamente monitorando a todos os controles em uma página.

Existem algumas ressalvas no entanto, especialmente quando se trata de uma aplicação ASP .Net:

  • Quando um usuário escolhe a opção cancelar, a função doPostBack irá lançar um erro de JavaScript. Eu tive que colocar manualmente um try-catch em torno da chamada .submit dentro doPostBack para suprimi-lo.

  • Em algumas páginas, um usuário pode executar uma ação que executa um postback para a mesma página, mas não é a defesa. Isso resulta em qualquer reajuste lógica JavaScript, por isso acha que nada mudou após o postback quando algo pode ter. Eu tive que implementar uma caixa de texto oculto que recebe de volta postado com a página, e é usado para manter um valor booleano simples indicando se os dados está sujo. Isso se persistiram em postagens.

  • Você pode querer algumas postagens na página para não provocar o diálogo, como um botão Salvar. Neste caso, você pode usar JQuery para adicionar uma função OnClick que conjuntos window.onbeforeunload como nulo.

Esperemos que este é útil para qualquer pessoa que tem que implementar algo similar.

A solução a seguir funciona para protótipo (testado no FF, IE 6 e Safari). Ele usa um observador genérico forma (que incêndios formar: mudou quando os campos do formulário ter sido modificado)., Que você pode usar para outras coisas também

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

Aqui está uma solução javascript / jquery que é simples. É responsável por "desfazer" pelo usuário, é encapsulado dentro de uma função para facilitar a aplicação, e não falha de ignição em enviar. Basta ligar para a função e passar o ID do seu formulário.

Esta função serializa o formulário uma vez quando a página é carregada, e novamente antes das folhas de usuários da página. Se os dois estados de formulário são diferentes, o prompt é exibido.

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

Solução Geral de Apoio múltiplas formas em uma determinada página ( Basta copiar e colar no seu projeto )

$(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:. Esta solução é combinada das soluções dos outros para criar uma solução integrada geral

características:

    copiar
  • Apenas e cole em seu aplicativo.
  • Suporta vários formulários.
  • Você pode estilo ou fazer ações formas sujas, uma vez que eles têm a classe "forma-suja".
  • Você pode excluir algumas formas de adicionar a classe 'ignorar-mudanças'.

Recentemente, contribuiu para uma fonte aberta jQuery plugin chamado dirtyForms .

O plugin é projetado para trabalhar com adicionados dinamicamente HTML, suporta múltiplas formas, pode suportar praticamente qualquer quadro de diálogo, cai de volta para o diálogo beforeunload navegador, tem um quadro ajudante pluggable para apoiar ficando estado sujo de editores personalizados (um plugin tinyMCE está incluído), trabalha dentro de iFrames eo status sujo pode ser definir ou redefinir à vontade.

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

Detect forma muda com o uso de jQuery é muito simples:

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

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

Eu expandiu sugestão de Slace acima, para incluir elementos mais editáveis ??e também excluindo certos elementos (com um estilo CSS chamada "srSearch" aqui) de causar a bandeira suja a ser definido.

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

      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;

Este é exatamente o que os Fleegix.js plugins fleegix.form.diff ( http://js.fleegix.org/plugins/form/diff ) foi criada. Serializar o estado inicial da forma em carga usando fleegix.form.toObject ( http://js.fleegix.org/ref#fleegix.form.toObject ) e save -lo em uma variável, e depois comparar com o estado atual usando fleegix.form.diff na descarga. Fácil como torta.

Uma método, usando matrizes para armazenar as variáveis ??de modo que as mudanças possam ser rastreados.

Aqui está um método muito simples para detectar mudanças , mas o resto não é tão elegante.

Outro método que é bastante simples e pequeno, de Blog improvável :

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

No IE document.ready não funcionará corretamente ele irá atualizar os valores de entrada.

por isso precisamos de evento de carregamento ligamento dentro da função document.ready que irá lidar com para o navegador IE também.

abaixo é o código que você deve colocar dentro da função document.ready.

 $(document).ready(function () {
   $(window).bind("load", function () { 
    $("input, select").change(function () {});
   });
});
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top