Tutorial: dashboard web 2.0 dla leniwych…

Usuwanie i dodawanie elementów


Kolejną kwestią jest usuwanie elementów. Jeśli nie używasz na co dzień selektorów jQuery – może Cię kusić, aby linkom odpowiedzialnym za usuwanie elementów dodawać atrybut onclick, a w nim wywołanie funkcji, która sprawi, że całość odbędzie się ajaxem. Nie jest to złe podejście, ale jeśli trochę poznasz selektory jQuery – będziesz wiedział, że to rozwiązanie nie jest sexy ;)

Zróbmy to od razu porządnie, przy pomocy tylko kilku linii kodu.
W widokach index.ctp (dla items i things) znajdź fragment odpowiadający za linki. Dodajmy dla linku “delete” klasę “tab-update”,
dzięku temu łatwo je złapiemy w przeglądarce:

<td class="actions">
	<?php echo $this->Html->link(__('View', true), array('action' => 'view', $item['Item']['id'])); ?>
	<?php echo $this->Html->link(__('Edit', true), array('action' => 'edit', $item['Item']['id'])); ?>
	<?php echo $this->Html->link(
	    __('Delete', true),
	    array('action' => 'delete', $item['Item']['id']),
	    array("class"=> "tab-update"),
	    sprintf(__('Are you sure you want to delete # %s?', true),
		    $item['Item']['id'])
	); ?>
</td>

Metoda tutaj zastosowana nie będzie współgrała z confirm message cake’a (czwarty parametr metody link()), więc proponuję to na razie usunąć, bo chwilowo nie mam pomysłu jak to naprawić (spodziewam się w finalnej wersji 1.3 rozwiązania tej kwestii, więc pozwalam sobie teraz nie zaprzątać nią głowy).

Używamy podobnego triku jak przy pagination, a właściwie dopisujemy jedynie nowy selektor “.tab-update” do funkjcji, którą już mamy:

   jQuery('#tabs a[href*=/sort:],#tabs a[href*=/page:],.tab-update').live('click', function(){
       jQuery('#tabs>div:visible').load(jQuery(this).attr('href'));
          return false;
   });

Problem z przekerowaniami

Problem z przekierowaniami


Jednak pojawia się problem z redirectami. To dlatego, że dispatcher nie wie jaki to controller, żeby to naprawić wystarczy w metodzie delete zamienić

// /app/controllers/items_controller.php
$this->redirect(array('action'=>'index'));

na

// /app/controllers/items_controller.php
$this->redirect(array("controller"=> "items", 'action' => 'index'));

jeśli chcesz mieć bardziej estetyczny kod, to zamiast explicite podawać “items” możesz zmienić na $this->name,

Uważny obserwator (czyli Twój klient) zauważy, że już nie widać informacji, że element został usunięty. Zostawmy sobie tą drobnostkę na koniec.

Teraz warto by poprawić działanie przycisku “Add Item”. Teraz przenosi nas do nowego widoku, a potem do /items/index. W przypadku pierwszej zakładki nie ma problemu – wystarczyłoby zmienić przekierowanie na adres /items/all. Ale w kwestii kolejnych zakładek – natkniemy się na problem włączenia odpowiedniej zakładki. Dlatego tu również pomocny będzie Ajax. Do tego wykorzystamy fajny plugin do jQuery – Boxy.

Myślę, że teraz jest najwyższa pora aby pozbyć się powtórzeń w widokach add i edit. Szkoda życia na edycję dwóch niemal identycznych widoków. Aby to zrobić – rzuć okiem tutaj. W dalszej części kod będzie po przeprowadzeniu takiego refaktoringu.

Od razu zrobimy to tak aby wszystkie linki, które chcemy, wywoływały się ajaxem w oknie dialogowym. Dodaj klasę “display-in-box” dla linków edit i add (w widokach index.ctp items i things), a następnie w widoku /items/all dodaj taki kod:

// /app/views/items/all.ctp
$("a.display-in-box").live(
        "click",
        function(){

           Boxy.load(
                 this.href,
                 {
                    title: (this.title)? this.title: this.href,
                    modal: true,
                 }
           );
           return false;
        }
);

Przypisze on wszystkim linkom klast “display-in-box” zachowanie polegające na tym, że po kliknięciu otworzy okno dialogowe Boxy, załaduje do niego ajaxem zawartość z pod adresu na który ten link wskazywał (href), ustawi tytuł boxu taki jak title linku (lub w wypadku braku – wywołany adres) oraz zapobiegnie przeładowaniu strony po kliknięciu linku.

Dodawanie elementu w oknie dialogowym

Dodawanie elementu w oknie dialogowym


Wygląda to teraz ładnie, działa poprawnie, ale po dodaniu, lub edycji elementu lądujemy nie tam gdzie się spodziewamy.

Pierwszym problemem jest to, że kliknięcie submit skutkuje przeładowaniem strony (i w następstwie redirectem nie tam gdzie chcemy). Przekonajmy formularz, żeby wysłał się również ajaxem :)

jQuery("body").ajaxComplete(
  function( event, xhr, options) {
      if(Boxy.isModalVisible()){
	$(".boxy-wrapper form").live(
	    "submit",
	    function() {
	      var form = this;
	      jQuery.ajax({
		  type: "post",
		  url: this.action,
		  data: jQuery(this).serialize(),
		  success: function(data, status, xhr) {
		    if(data != ":ajaxRedirect:") {
			  Boxy.get(form).setContent(data);
		    }else{
			  Boxy.get(form).hideAndUnload();
		    }
		  },
	      });
	      return false;
	    }
	);
      }
  }
);

Ten fragment wymaga konkretnego wyjasnienia. W linii 1 podpinamy naszą funkcję (linia 2) pod zdarzenie ajaxComplete, a to znaczy, że przy każdym odpaleniu jakiegokolwiek ajaxa na naszej stronie przez jQuery zostanie ona uruchomiona (z odpowiednimi parametrami jak w linni 2).
W linni 3 sprawdzamy czy istnieje jakiekolwiek okno dialogowe otwarte na tej stronie (modal to typ okna, które pojawia się z takim szarym tłem przykrywającym resztę strony). Jeśli tak, to w lini 4 łapiemy wszystkie (w naszym wypadku zawsze jeden) formularze, które znajdują się w oknie dialogowym i podpinamy się w nich pod zdarzenie “submit” (4i5). Teraz przy każdej próbie wysłania naszego formularza odpali się nasza funkcja (linia 6).
Przy pomocy jQuery odpalamy wywołanie ajaxowe, przekazując odpowiednie parametry:
sposób przesłania danych (8), endpoint wywołania (9), dane do wysłania (10) (zostawiamy brudna robotę jQuery), a następnie definiujemy zachowanie w przypadku, gdy wywołanie ajaxa się powiedzie (11).

Zanim opiszę działanie następnego bloku – trochę teorii. Wywołanie ajaxa się powiedzie zawsze, kiedy uda sie wysłać zapytanie pod wskazany adres, a w odpowiedzi dostaniemy jakiś ciąg tekstowy. Metody zapisywania elementów w cakePHP korzystają z mechanizmów przekierowań, więc niezaleźnie od tego, czy nasz zapis zakończy się poprawnie, czy wyskoczą błędy walidacji – zapytanie z punktu widzenia Javascript zakończy się sukcesem. Dlatego należy zastosować mały hack, żeby przesłać klientowi informacje “Zapis się udał, zamknij okno”. Proponuję podejście nadpisania działania funkcji redirect w AppController:

// /app/app_controller.php
function redirect($url, $status = null, $exit = true) {
    if(!$this->RequestHandler->isAjax()){
      parent::redirect($url, $status, $exit);
    }else{
      exit(":ajaxRedirect:");
    }
}

która w przypadku zwykłych wywołań działa normalnie, a przypadku wywołań ajaxowych – zwraca ustalony ciąg znaków, będący umówionym hasłem ;)

Gdy już mamy ten fragment łatwiej zrozumieć co się dzieje w liniach 12-16. Gdy zwrócone dane nie są umówionym hasłem – ładujemy je do naszego otwartego okna dialogowego (13) (zobacz to w działaniu dodając walidację do modelu i wysyłając dane nie spełniające wymagań walidacji), a gdy hasło się zgadza – wiemy, że zapis się powiódł i zamykamy okno (15).
Linia 19 zabezpiecza nas przed wysłaniem formularza ponownie przez przeglądarkę.

Walidacja w oknie dialogowym

Walidacja w oknie dialogowym

Niestety, z powodu nowej metody AppController::redirect – usuwanie elementów się popsuło:

Popsute usuwanie

Popsute usuwanie


zajmiemy się tym później. Teraz mamy na tapecie inną kwestię: po prawidłowym zapisaniu elementu nie widzimy żadnego efektu na stronie.

Share Button

6 thoughts on “Tutorial: dashboard web 2.0 dla leniwych…

  1. I’m sure You do ;) I want to make It in English, thanks to You I have little more motivation to do so. So please stay tuned ;)

  2. Podzieliłem tutorial na strony dla Waszej wygody. Pozdrawiam.

  3. Ciekawy wpis, wydaje się być przejrzysty… pod koniec pewne rzeczy się komplikują, co znaczy że można się czegoś nowego nauczyć :)! ogólnie bardzo ciekawy tutorial, ale… już nie mogę się doczekać bardziej efektownych dodatków, które nie tylko są praktyczne ale i cieszą oko efektami :)

  4. Ja ogólnie rzecz biorąc pałam mniejszym uczuciem do fajerwerków – interesują mnie narzędzia usprawniające pracę. Ale napisz o czym chciałbym poczytać i zobaczymy co da się zrobić :)

  5. Ja również nie uważam, że strona bez “fajerwerków” to żadna strona, ale wiesz… czym ludzi nęcą w Sylwestra?
    Odpowiednio skonstruowany skrypt zachowuje swoją funkcjonalność a przy okazji nacieszy oko co zwiększa zainteresowanie :)

Leave a Reply

Your email address will not be published. Required fields are marked *