W poprzedniej części spowodowaliśmy by generowały się atrybuty walidacyjne dla elementów dynamicznie generowanych. Jednakże mimo ich poprawnego dodawania do elementów, walidacja po stronie klienta nie działała dla dynamicznie dodanych elementów – przy tworzeniu byli to autorzy książki, zaś przy edycji nowi jeszcze nie dodani autorzy.

Rozwiązanie byłoby dość banalne, gdyby mi choć raz zadziałało, tak jak wspominał Procent u siebie na blogu, powinno wystarczyć wykonanie następującej linijki:

// parse wszystkich elementów zawartych w danym selector:
$.validator.unobtrusive.parse(document);

/* 
  * parse pojedynczego elementu, drugi parametr określa czy dodać element 
  * do walidatora - parse robi to na samym końcu, zaś parseElement wymaga 
  * zdefiniowania, jeżeli wartość jestr true, to element nie zostanie dodany do
  * walidatora, zaś wartosć false spowoduje iż zostanie dodany
  */
$.validator.unobtrusive.parseElement(elemSelector, false);

Kod sparsuje nasz element (lub elementy w danym selector) i następnie doda go do pluginu walidacyjnego od jQuery. Niestety, dla mnie ten kod nigdy nie zadziałał. Nawet i teraz nie zadziała, a można to łatwo sprawdzić. W metodzie od JS odpowiedzialnej za dodanie elementu należy dodać następującą linijkę:

$.validator.unobtrusive.parse($element);

Lub nawet można się pokusić i dodać taką:

$.validator.unobtrusive.parse(document);

Na Create/Save walidacja po stronie klienta nie odpali się, zaś to co otrzymamy będzie odpowiedzą z serwera – można to poznać po ostatnim punkcie podsumowania błędów (Please fix all validation errors before clicking create/save once again).

Dlaczego tak się dzieje?

Plugin unobtrusive validation, po załadowaniu się strony forsuje jej sparsowanie – i dobrze. Jednak plugin validate działa rozsądnie. Raz sparsowany dokument zapisuje i jeżeli wykona się polecenie:

$('form[id=my]').validate();

To zostanie nam zwrócony już istniejący obiekt walidatora z informacją o wszystkich polach, jak i typach walidacji, które powinny zostać uruchomione w trakcie submit formularza.

Nie ważne to czy do metody validate() przekażemy opcję czy też nie, dokładnie ten kod to obrazuje:

$.extend($.fn, {
    // http://docs.jquery.com/Plugins/Validation/validate
    validate: function( options ) {
        // if nothing is selected, return nothing; can't chain anyway
        if (!this.length) {
            options && options.debug && window.console && console.warn( "nothing selected, can't validate, returning nothing" );
            return;
        }

        // check if a validator for this form was already created
        var validator = $.data(this[0], 'validator');
        if ( validator ) {
            return validator;
        }

        // cut
    },
    // cut
});

Wyraźnie widać, że jeżeli zapisana wartość pod kluczem validator dla naszego elementu (w tym przypadku formularza) to zamiast wykorzystać przekazane opcje, zostanie nam zwrócony obiekt już utworzony.

Wyjść z sytuacji jest kilka:

  1. Usunięcie zapisanego obiektu o kluczu validator co dodanie elementu;
  2. Łączenie opcji za pomocą sprytnego kodu JS;
  3. Nierobienie nic i przeciążenie zdarzenia submit, tak by wykonać raz (nie licząc pierwszego wymuszonego przez unobtrusive validation) parsowanie;

Opcja pierwsza jest banalnie prosta, najpierw zamieniamy kod by zamiast parsować tylko nasz element to by parsować cały dokument lub określony formularz, a tuż przed tą linijką dodajemy linijkę kasującą ustawienia:

$.removeData($('form')[0], 'validator');
$.validator.unobtrusive.parse(document);

Opcja druga jest trochę bardziej skomplikowana i można zobaczyć jej przykładowe rozwiązanie tutaj – uwaga, selector nie działa tam poprawnie dla zagnieżdżonych elementów ze względu na nawiasy klamrowe.

Trzecia opcja jest najtrudniejsza ale to ze względu na jak działa plugin validate w jQuery i o tym będzie następnym razem.

Oczywiście poprawiony kod można znaleźć na github.