Jak warunkowo ominąć walidację po stronie klienta

Na podstawie tego świetnego odcinka railscasts na temat wielokrokowych formularzy zrobiłem właśnie taki form. I dokładnie tak jak Ryan, używam dwóch submitów, ‘back’ i ‘continue’, wygląda to tak:

= simple_form_for @poster do |f|
	= render "#{@poster.current_step}_step", f: f
	= f.submit "Back", name: "back_button" unless @poster.first_step?
	= f.submit "Continue"

oczywiście wszystko działa, ptaszki ćwierkają z radości, jest tylko jeden problem: kliknięcie przycisku “Back” by cofnąć się do poprzedniego kroku również wymaga by cała walidacja po stronie klienta przeszła pomyślnie. a to troche bez sensu żeby użytkownik musiał wypełniać pola przed tym jak poprawi błąd, którego skutkiem jest to że wyświetliły się zupełnie inne pola niż te o które mu chodziło. Jak zrobić taką magię żeby po kliknięciu w ten nieszczęsny “Back” walidacja client-side była omijana?

nie bardzo rozumiem jak to co napisałeś ma się do mojego pytania. To jest kod kontrolera czyli juz server side

Faktycznie, jest auto walidacja, spróbuj dodać do inputa atrybut required: false.

required: false odpada bo chce żeby walidacja była przeprowadzana przy kliknięciu przycisku “continue”, a tak to ją obetnę dla całości

Z tego, co patrzę na kod w ascii cast, to #save jest wykonywane tylko w przypadku ostatniego kroku. Jesteś pewien, że wszystko dobrze zrobiłeś?

metoda save nie ma tu nic do rzeczy, mi chodzi o walidację po stronie klienta.

Ok, racja, dopiero teraz zauważyłem, że jest tam if @order.valid?. W takim razie chyba nie do końca rozumiem problem - ogólnie wykonanie metod takich jak #save i #valid? odpala walidację. Zmień kod tak, żeby #valid? wywoływać tylko jeśli to krok do przodu i nie będziesz miał tego problemu.

Jemu chodzi o walidacje html5, którą dodaje prawdopodobnie rails_ujs, tzn. dodaje ‘required: true’ jeżeli w modelu jest walidacja presence na jakieś pole.

Może tak:

if params[:back_button]
  @order.previous_step
elsif @order.last_step?
  @order.save if @order.all_valid?
else
  if @order.valid?
    @order.next_step
  end
end
session[:order_step] = @order.current_step

czyli sprawdzać poprawność tylko w przypadku przejścia w przód? A na dobrą sprawę można by tylko zapamiętywać dane modelu a walidację robić na samym końcu - aczkolwiek przy takim podejściu i formularzu składającym się z wielu kroków, może być trochę głupio na samym końcu dostać błędy z kilku poprzednich formularzy :smile:

Ach ;]. Wybaczcie, jakieś klapki na oczach miałem… Rozwiązanie:

$ ->
  $('input[type=submit]').click (e) ->
    $('#myfield').attr('required', e.target.id == 'continue')

Zakładając oczywiście, że dasz odpowiednie id elementom. #myfield możesz zamienić na listę klas lub coś bardziej ogólnego jak #myform input[type=text], #myform input[type=email] etc.

@magnus proponujesz to samo co ja, a chodzi o walidację html5, która jest różna dla różnych przeglądarek.

Mała subtelność

e.target.id == ‘continue’ ? true : false
lub wręcz
e.target.id == ‘continue’

Wow, patrzę co napisałem i przecieram oczy ;] Oczywiście, że bez żadnego else then - jak to sprawdzałem, to napisałem tam stringi, później zdałem sobie sprawę, że z true/false też będzie poprawnie, ale już konstrukcji nie zmieniłem.

P.S. to jest też dowód na to, że nie powinno się pisać kodu po prawie nieprzespanej nocy i locie samolotem, trzeba się porządnie wyspać… ;]

Pamiętaj, że ten odcinek ma ponad 3 lata. Omawiana tam metoda nie jest zbyt dobra i przejrzysta. Polecam do takich celów https://github.com/pluginaweek/state_machine - można definiować sobie walidacje per stan (krok).

1 Like