Kaydedilmemiş Değişiklikleri Algılama


95

Bir ASP .Net uygulamasında "Kaydedilmemiş Değişiklikler" komut istemini uygulamaya koymam gerekiyor. Bir kullanıcı bir web formundaki kontrolleri değiştirir ve kaydetmeden önce uzaklaşmaya çalışırsa, değişiklikleri kaydedilmemiş olduğu konusunda uyaran bir istem görünmeli ve onlara iptal etme ve geçerli sayfada kalma seçeneği sunmalıdır. Kullanıcı herhangi bir kontrole dokunmadıysa komut istemi görüntülenmemelidir.

İdeal olarak bunu JavaScript'te uygulamak isterim, ancak kendi kodumu döndürme yoluna girmeden önce, bunu başarmak için mevcut çerçeveler veya önerilen tasarım modelleri var mı? İdeal olarak, minimum değişiklikle birden çok sayfada kolayca yeniden kullanılabilecek bir şey istiyorum.


Ben de aynı ilgimi çekiyorum, sadece asp.net için değil, ama orada genel bir çözüm olduğunu düşünüyorum.
Michael Neale

Aşağıda gönderdiğim çözüm düz HTML / Javscript olarak kullanılabilir. Basitçe "codebehind" öğesini HTML etiketlerinin kendisindeki özniteliklere değiştirin.
Wayne

Burada 5 yıl geciktiğimi biliyorum, ancak önceki çözümleri geliştirebileceğimi düşünüyorum ... Aşağıda cevabımı düzelttim ve güncelledim.
skibulk

Yanıtlar:


96

JQuery kullanarak:

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

onunload/ Methodları ile onbeforeunloadgerektiği gibi birleştirin .

Aşağıdaki açıklamalar, kodu çoğaltmadan tüm giriş alanlarına başvurur:

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

Kullanım $(":input"), tüm giriş, metin alanı, seçim ve düğme öğelerini ifade eder.


32
Oy verin çünkü bu çözümü kullandım ama biraz daha kolay bir yolu var. : $ (': İnput']. Change (function () {kullanırsanız, bu tüm giriş alanları için çalışır, diğer giriş türleri için çoğaltma yapmanıza gerek yoktur. $ (": İnput") tüm girişi, metin alanını seçer seçmek ve düğme unsurları.
CodeClimber

5
Bunun Firefox'ta sorunları olabileceğini düşünüyorum. Bir kullanıcı formu değiştirir ve ardından tarayıcı Yenile düğmesini tıklarsa, sayfa yeniden yüklenir ve Firefox, değişiklik olayını tetiklemeden formu kullanıcıların önceki girişleriyle doldurur. Şimdi form kirli olacak, ancak kullanıcı başka bir düzenleme yapmadıkça bayrak ayarlanmayacaktır.
Oskar Berggren

8
Sadece bir ipucu. Form kirliyse, yine de gerçekten değiştirilmiş veriler içerdiği anlamına gelmez. Daha iyi kullanılabilirlik için eski verileri yenisiyle karşılaştırmanız gerekir;)
Slava Fomin II

Akılda çekeriz changemetin girişi tetikleyiciler için ise / bırakarak odağı kaybetme kez inputher tuş için tetikleyici. ( Birden fazla olayı önlemek için genellikle changeJQuery'nin yanında kullanırım one().)
SpazzMarticus

46

Bulmacanın bir parçası:

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

Ve bir başkası :

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

Hepsini toparlayın ve ne elde edersiniz?

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

Ayrıca, beforeunloadetkinlik kaydını kullanmak için etkinlik işleyicisinin kaydını da değiştirmek isteyeceksiniz LIBRARY_OF_CHOICE.


Firefox 9.0.1'de herhangi bir mesaj almıyorum. "Sayfadan ayrılmak istediğinizden emin misiniz? Bazı veriler kaybolabilir" diyen varsayılan bir mesajım var (Bu, İngilizce olmayan tarayıcımın bana gösterdiği mesajın aşağı yukarı çevirisidir)
Romain Guidoux

1
Bu komut dosyası harika, ancak benim için formumu gönderdikten sonra da uyarı mesajı görüntüledi. Bir onClick ( onclick="window.onbeforeunload=null")
Joost

1
Kirli bir bit ayarlamak yerine defaultValue vb. Özelliklerini karşılaştırma yaklaşımını seviyorum. Bu, birisi bir alanı değiştirip sonra tekrar değiştirirse, formun kirli olarak bildirilmeyeceği anlamına gelir.
2014

Teşekkürler, bu harika. Lütfen bize jquery'ye nasıl kayıt olacağımızı söyler misiniz? Bu komut dosyası aynı html sayfasında olmadıkça çalışmıyor. Harici bir .js dosyasında varsa, çalışmıyor.
Shiva Naru

17

.Aspx sayfasında, form bilgilerinin "kirli" olup olmadığını anlamak için bir Javascript işlevine ihtiyacınız vardır.

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

ve kod arkasında, giriş alanlarına tetikleyicileri ve ayrıca gönderme / iptal düğmelerindeki sıfırlamaları ekleyin ...

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

9

Aşağıdakiler, herhangi bir onchange olayını yakalamak için tarayıcının onbeforeunload işlevini ve jquery'yi kullanır. Ayrıca BT, değişikliklerin gerçekleştiğini belirten işareti sıfırlamak için gönder veya sıfırla düğmelerini de arar.

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;

Merhaba colin sıfırlama bir onay kutusu yerleştirdiğimde işe yaramadı, başlangıçta kontrol ettim ve tekrar işaretini kaldırdım ancak uyarı tetikleniyor, kullanıcı onay kutusunun işaretini kaldırdığında nasıl sıfırlayabilirim
Geliştirici

8

Cevaplar için herkese teşekkürler. JQuery ve Protect-Data eklentisini kullanarak bir çözüm uygulamaya başladım . Bu, bir sayfadaki tüm kontrollere otomatik olarak izleme uygulamama izin veriyor.

Bununla birlikte, özellikle bir ASP .Net uygulamasıyla uğraşırken birkaç uyarı vardır:

  • Kullanıcı iptal seçeneğini seçtiğinde, doPostBack işlevi bir JavaScript hatası atar. Bunu bastırmak için doPostBack içindeki .submit çağrısının etrafına elle bir try-catch koymam gerekti.

  • Bazı sayfalarda, bir kullanıcı aynı sayfaya geri gönderme yapan, ancak kaydetme niteliğinde olmayan bir eylem gerçekleştirebilir. Bu, herhangi bir JavaScript mantığının sıfırlanmasıyla sonuçlanır, bu nedenle, bir şey olduğunda geri göndermeden sonra hiçbir şeyin değişmediğini düşünür. Sayfayla geri gönderilen ve verilerin kirli olup olmadığını gösteren basit bir boole değerini tutmak için kullanılan gizli bir metin kutusu uygulamak zorunda kaldım. Bu, geri göndermelerde kalıcı olur.

  • Sayfadaki bazı geri göndermelerin, Kaydet düğmesi gibi iletişim kutusunu tetiklememesini isteyebilirsiniz. Bu durumda, window.onbeforeunload değerini null olarak ayarlayan bir OnClick işlevi eklemek için JQuery'yi kullanabilirsiniz.

Umarım bu, benzer bir şey uygulamak zorunda olan herkes için yararlıdır.


7

Genel Çözüm Belirli bir sayfada birden çok formu desteklemek ( Projenize kopyalayıp yapıştırmanız yeterlidir )

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

Not: Bu çözüm, genel bir entegre çözüm oluşturmak için diğerlerinin çözümlerinden birleştirilmiştir.

Özellikleri:

  • Uygulamanıza kopyalayıp yapıştırmanız yeterlidir.
  • Çoklu Formları Destekler.
  • "Form-kirli" sınıfına sahip olduklarından, eylemleri kirli formlar haline getirebilir veya yapabilirsiniz.
  • 'Değişiklikleri yoksay' sınıfını ekleyerek bazı formları dışlayabilirsiniz.

Bu iyi çalışıyor, ancak eklenen sınıfları _isDirtyAaron Powell'ın tanımladığı gibi değişkenle değiştiriyorum . JavaScript'teki değişkenler yerine HTML'den sınıf ekleme ve çıkarma ihtiyacını görmedim.
Martin

1
Burada html sınıflarını kullanma fikri, genel bir application-wize js / html çözümü yapmak için değişkenleri kullanamayacağınızdan, her bir forma 'dirty' özelliğini eklemektir.
MhdSyrwan

1
Başka bir deyişle, bir değişken kullanmak yalnızca tek bir form için işe yarar, aksi takdirde çözümü çok daha karmaşık hale getirecek bir dizi kirli bayrağa ihtiyacınız olacaktır.
MhdSyrwan

1
Bu noktayı açıkladığınız için teşekkürler. İhtiyacım sadece tek bir form sayfasıydı.
Martin

6

Aşağıdaki çözüm prototip için çalışır (FF, IE 6 ve Safari'de test edilmiştir). Diğer şeyler için de kullanabileceğiniz genel bir form gözlemcisi kullanır (formu tetikler: formun herhangi bir alanı değiştirildiğinde değiştirilir).

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

6

İşte basit bir javascript / jquery çözümü. Kullanıcı tarafından "geri alma" yı hesaba katar, uygulama kolaylığı için bir işlevin içinde kapsüllenir ve gönderildiğinde tekleme olmaz. Sadece işlevi çağırın ve formunuzun kimliğini iletin.

Bu işlev, formu sayfa yüklendiğinde ve kullanıcı sayfadan ayrılmadan önce tekrar serileştirir. İki form durumu farklıysa, komut istemi gösterilir.

Deneyin: 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
Yaklaşımınızı denedim. Chrome ve Firefox için çalışıyor, ancak Safari'de Ipad Mini'de çalışmasını sağlayamıyor ..
Dimitry K

4

Son zamanlarda dirtyForms adlı açık kaynaklı bir jQuery eklentisine katkıda bulundum .

Eklenti, dinamik olarak eklenen HTML ile çalışmak üzere tasarlanmıştır, birden çok formu destekler, neredeyse tüm iletişim çerçevelerini destekleyebilir, yüklemeden önce tarayıcıya geri döner, özel düzenleyicilerden kirli durum almayı desteklemek için takılabilir bir yardımcı çerçeveye sahiptir (bir tinyMCE eklentisi dahildir) , iFrames içinde çalışır ve kirli durumu isteğe göre ayarlanabilir veya sıfırlanabilir.

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


4

JQuery kullanarak form değişikliklerini tespit etmek çok basittir:

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

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

2

Slace'in yukarıdaki önerisini, düzenlenebilir öğelerin çoğunu dahil etmek ve ayrıca bazı öğeleri (burada "srSearch" adlı bir CSS stiliyle) kirli bayrağın ayarlanmasına neden olmaktan hariç tutmak için genişlettim.

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


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;



0

Değişkenleri tutmak için dizileri kullanan bir yöntem , böylece değişiklikler izlenebilir.

İşte değişiklikleri tespit etmek için çok basit bir yöntem , ancak geri kalanı o kadar zarif değil.

Farfetched Blog'dan oldukça basit ve küçük olan başka bir yöntem :

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

0

IE belgesinde. Ready düzgün çalışmayacak, girdi değerlerini güncelleyecektir.

bu nedenle, IE tarayıcısı için de işleyecek olan document.ready işlevi içindeki load olayını bağlamamız gerekir.

aşağıda document.ready fonksiyonu içine koymanız gereken kod bulunmaktadır.

 $(document).ready(function () {
   $(window).bind("load", function () { 
    $("input, select").change(function () {});
   });
});
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.