Cyfroteka.pl

klikaj i czytaj online

Cyfro
Czytomierz
00043 004101 15207724 na godz. na dobę w sumie
Web development. Receptury nowej generacji - książka
Web development. Receptury nowej generacji - książka
Autor: , , , , Liczba stron: 320
Wydawca: Helion Język publikacji: polski
ISBN: 978-83-246-5149-8 Data wydania:
Lektor:
Kategoria: ebooki >> komputery i informatyka >> webmasterstwo >> tworzenie stron www
Porównaj ceny (książka, ebook, audiobook).

Zostań wszechstronnym webmasterem!

Tworzenie nowoczesnych stron internetowych to cenna umiejętność. Niezależnie od tego, czy jesteś początkującym programistą WWW, czy masz wieloletnie doświadczenie w tej branży, poznanie gotowych rozwiązań zawartych w tej książce sprawi, że staniesz się bardziej wszechstronnym specjalistą.

Znajdziesz tu ponad 40 zwięźle opisanych, wypróbowanych rozwiązań problemów towarzyszących tworzeniu stron internetowych oraz poznasz nowe sposoby pracy, które pozwolą Ci jeszcze bardziej rozwinąć umiejętności. Możesz spodziewać się rzetelnego przeglądu wszystkich najnowszych technik programistycznych, od metod tworzenia atrakcyjnych wizualnie przycisków, przez analizę danych i testowanie kodu, po hosting witryn na serwerze.

Z tą książką nauczysz się projektować przykuwające wzrok przyciski za pomocą prostych stylów działających we wszystkich przeglądarkach, tworzyć animacje dla urządzeń przenośnych bez żadnych wtyczek, budować i testować wiadomości e-mail oraz konstruować elastyczne układy stron, odpowiednie dla komputerów stacjonarnych i przenośnych. Opis możliwości bibliotek jQuery, Knockout i Backbone.js pozwoli Ci na jeszcze efektywniejsze wykorzystanie możliwości współczesnych przeglądarek. Obecnie nie można ignorować rynku urządzeń mobilnych, dlatego dużo uwagi poświęcono tu dostosowaniu stron do wymogów urządzeń przenośnych. Książka ta jest obowiązkową pozycją w bibliotece każdego programisty WWW podążającego za trendami!

Poznaj przepisy na:

Najlepsze przepisy dla programistów stron WWW!

Znajdź podobne książki Ostatnio czytane w tej kategorii

Darmowy fragment publikacji:

Tytuł oryginału: Web Development Recipes Tłumaczenie: Łukasz Piwko ISBN: 978-83-246-5149-8 © Helion 2013. All rights reserved. Copyright © 2012 The Pragmatic Programmers, LLC. All rights reserved. No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form, or by any means, electronic, mechanical, photocopying, recording, or otherwise, without the prior consent of the publisher. Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in this book, and The Pragmatic Programmers, LLC was aware of a trademark claim, the designations have been printed in initial capital letters or in all capitals. The Pragmatic Starter Kit, The Pragmatic Programmer, Pragmatic Programming, Pragmatic Bookshelf, PragProg and the linking g device are trademarks of The Pragmatic Programmers, LLC. Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości lub fragmentu niniejszej publikacji w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii metodą kserograficzną, fotograficzną, a także kopiowanie książki na nośniku filmowym, magnetycznym lub innym powoduje naruszenie praw autorskich niniejszej publikacji. Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi bądź towarowymi ich właścicieli. Materiały graficzne na okładce zostały wykorzystane za zgodą iStockPhoto Inc. Wydawnictwo HELION dołożyło wszelkich starań, by zawarte w tej książce informacje były kompletne i rzetelne. Nie bierze jednak żadnej odpowiedzialności ani za ich wykorzystanie, ani za związane z tym ewentualne naruszenie praw patentowych lub autorskich. Wydawnictwo HELION nie ponosi również żadnej odpowiedzialności za ewentualne szkody wynikłe z wykorzystania informacji zawartych w książce. Wydawnictwo HELION ul. Kościuszki 1c, 44-100 GLIWICE tel. 32 231 22 19, 32 230 98 63 e-mail: helion@helion.pl WWW: http://helion.pl (księgarnia internetowa, katalog książek) Pliki z przykładami omawianymi w książce można znaleźć pod adresem: ftp://ftp.helion.pl/przyklady/twstnr.zip Drogi Czytelniku! Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres http://helion.pl/user/opinie/twstnr Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję. Printed in Poland. • Kup książkę • Poleć książkę • Oceń książkę • Księgarnia internetowa • Lubię to! » Nasza społeczność Spis treĂci PodziÚkowania ..............................................................................................7 WstÚp ...........................................................................................................11 Rozdziaï 1. ¥wiecideïka ..............................................................................17 Receptura 1. Stylizowanie przycisków i ïÈczy ...........................................................17 Receptura 2. Stylizowanie cytatów przy uĝyciu CSS ................................................21 Receptura 3. Tworzenie animacji przy uĝyciu transformacji CSS3 ............................28 Receptura 4. Tworzenie interaktywnych pokazów slajdów przy uĝyciu jQuery ............33 Receptura 5. Tworzenie i stylizowanie wewnÈtrztekstowych okienek pomocy ..............38 Rozdziaï 2. Interfejs uĝytkownika ............................................................47 Receptura 6. Tworzenie szablonu wiadomoĂci e-mail ................................................47 Receptura 7. WyĂwietlanie treĂci na kartach .............................................................58 Receptura 8. Rozwijanie i zwijanie treĂci z zachowaniem zasad dostÚpnoĂci ...............65 Receptura 9. Nawigacja po stronie internetowej przy uĝyciu klawiatury ......................71 Receptura 10. Tworzenie szablonów HTML przy uĝyciu systemu Mustache ..............79 Receptura 11. Dzielenie treĂci na strony ....................................................................84 Receptura 12. ZapamiÚtywanie stanu w Ajaksie ........................................................90 Receptura 13. Tworzenie interaktywnych interfejsów uĝytkownika przy uĝyciu biblioteki Knockout.js .......................................................95 Receptura 14. Organizacja kodu przy uĝyciu biblioteki Backbone.js ..........................105 Rozdziaï 3. Dane ........................................................................................123 Receptura 15. Wstawianie na stronÚ mapy Google ...................................................123 Receptura 16. Tworzenie wykresów i grafów przy uĝyciu Highcharts ........................129 Receptura 17. Tworzenie prostego formularza kontaktowego ....................................137 Receptura 18. Pobieranie danych z innych serwisów przy uĝyciu formatu JSONP .....144 6  Web development. Receptury nowej generacji Receptura 19. Tworzenie widĝetów do osadzenia w innych serwisach ........................147 Receptura 20. Budowanie witryny przy uĝyciu JavaScriptu i CouchDB .....................153 Rozdziaï 4. UrzÈdzenia przenoĂne ..........................................................163 Receptura 21. Dostosowywanie stron do wymogów urzÈdzeñ przenoĂnych .................163 Receptura 22. Menu rozwijane reagujÈce na dotyk ...................................................168 Receptura 23. Operacja „przeciÈgnij i upuĂÊ” w urzÈdzeniach przenoĂnych ...............171 Receptura 24. Tworzenie interfejsów przy uĝyciu biblioteki jQuery Mobile ................178 Receptura 25. Sprite’y w CSS ................................................................................187 Rozdziaï 5. Przepïyw pracy ......................................................................191 Receptura 26. Szybkie tworzenie interaktywnych prototypów stron ............................191 Receptura 27. Tworzenie prostego bloga przy uĝyciu biblioteki Jekyll ........................200 Receptura 28. Tworzenie modularnych arkuszy stylów przy uĝyciu Sass ....................207 Receptura 29. Bardziej przejrzysty kod JavaScript, czyli CoffeeScript ........................215 Receptura 30. ZarzÈdzanie plikami przy uĝyciu narzÚdzia Git ..................................222 Rozdziaï 6. Testowanie ............................................................................233 Receptura 31. Debugowanie JavaScriptu .................................................................233 Receptura 32. ¥ledzenie aktywnoĂci uĝytkowników przy uĝyciu map cieplnych ...........239 Receptura 33. Testowanie przeglÈdarek przy uĝyciu Selenium ..................................242 Receptura 34. Testowanie stron internetowych przy uĝyciu Selenium i Cucumber ......247 Receptura 35. Testowanie kodu JavaScript przy uĝyciu Jasmine ................................260 Rozdziaï 7. Hosting i wdraĝanie ..............................................................271 Receptura 36. Wspólna praca nad stronÈ poprzez Dropbox ......................................271 Receptura 37. Tworzenie maszyny wirtualnej ...........................................................275 Receptura 38. Zmienianie konfiguracji serwera WWW przy uĝyciu programu Vim ......279 Receptura 39. Zabezpieczanie serwera Apache za pomocÈ SSL i HTTPS ..............284 Receptura 40. Zabezpieczanie treĂci .......................................................................288 Receptura 41. Przepisywanie adresów URL w celu zachowania ïÈczy .......................292 Receptura 42. Automatyzacja procesu wdraĝania statycznych serwisów za pomocÈ Jammit i Rake .................................................................296 Dodatek A. Instalowanie jÚzyka Ruby ...................................................305 Dodatek B. Bibliografia ............................................................................309 Skorowidz ..................................................................................................311 Rozdziaï 2. • Interfejs uĝytkownika  79 Receptura 10. Tworzenie szablonów HTML przy uĝyciu systemu Mustache Problem Do utworzenia naprawdÚ wyjÈtkowego interfejsu potrzebne jest zastosowanie zarówno dynamicznych, jak i asynchronicznych technik programowania. DziÚki Ajaksowi i takim bibliotekom jak jQuery moĝemy modyfikowaÊ interfejs uĝytkow- nika bez zmieniania jego kodu HTML, poniewaĝ potrzebne elementy dodamy za pomocÈ JavaScriptu. Elementy te zazwyczaj dodaje siÚ, stosujÈc technikÚ konkatenacji ïañcuchów. Powstaïy w wyniku tego kod jest, niestety, zagmatwany i ïatwo w nim popeïniÊ bïÈd. Peïno w nim mieszajÈcych siÚ ze sobÈ pojedynczych i podwójnych cudzysïowów oraz niekoñczÈcych siÚ ïañcuchów wywoïañ metody append() z biblioteki jQuery. Skïadniki  jQuery  Mustache.js RozwiÈzanie Na szczÚĂcie, sÈ nowoczesne narzÚdzia, takie jak Mustache, dziÚki którym moĝemy pisaÊ prawdziwy kod HTML, renderowaÊ przy jego uĝyciu dane oraz wstawiaÊ go do dokumentów. Mustache to system szablonów HTML dostÚpny w kilku popularnych jÚzykach programowania. Implementacja JavaScript pozwala na pisanie widoków dla klienta przy uĝyciu czystego kodu HTML caïkowicie oddzielonego od kodu JavaScript. Moĝna w nim uĝywaÊ takĝe instrukcji logicz- nych i iteracji. Mustache pozwala uproĂciÊ tworzenie kodu HTML podczas generowania nowej treĂci. SkïadniÚ tego narzÚdzia poznamy na przykïadzie aplikacji JavaScript do zarzÈdzania produktami. Obecnie aplikacja ta umoĝliwia dodawanie nowych produktów do listy. Ponie- waĝ wszystkie ĝÈdania sÈ obsïugiwane przez JavaScript i Ajax, w przykïadzie tym uĝywamy naszego standardowego serwera roboczego. Gdy uĝytkownik wypeïni formularz dodawania nowego produktu, wysyïa do serwera ĝÈdanie zapisania 80  Web development. Receptury nowej generacji tego produktu i wyrenderowania nowego produktu na liĂcie. Aby dodaÊ produkt do listy, musimy zastosowaÊ konkatenacjÚ ïañcuchów, której kod jest zagmatwany i nieelegancki: mustache/submit.html var newProduct = $( li /li ); newProduct.append( span class= product-name + product.name + /span ); newProduct.append( em class= product-price + product.price + /em ); newProduct.append( div class= product-description + product.description + /div ); productsList.append(newProduct); Aby uĝyÊ systemu Mustache.js, wystarczy go tylko zaïadowaÊ na stronÚ. JednÈ z wersji tego pliku znajdziesz w pakiecie kodu towarzyszÈcym tej ksiÈĝce, ale moĝesz teĝ pobraÊ jego najnowszÈ wersjÚ z serwisu GitHub7. Renderowanie szablonu Aby przerobiÊ naszÈ istniejÈcÈ aplikacjÚ, przede wszystkim musimy dowiedzieÊ siÚ, jak wyrenderowaÊ szablon przy uĝyciu Mustache. Najprostszym sposobem na zrobienie tego jest uĝycie funkcji to_html(). Mustache.to_html(templateString, data); Funkcja ta przyjmuje dwa argumenty. Pierwszy jest ïañcuchem szablonowego kodu HTML, w którym ma odbywaÊ siÚ renderowanie, a drugi to dane, które majÈ zostaÊ do tego szablonu wstawione. Zmienna data jest obiektem, którego klucze w szablonie zostajÈ zamienione na lokalne zmienne. Spójrz na poniĝszy kod: var artist = {name: John Coltrane }; var rendered = Mustache.to_html( span class= artist name {{ name }} /span , artist); $( body ).append(rendered); Zmienna rendered zawiera nasz ostateczny kod HTML zwrócony przez metodÚ to_html(). Aby umieĂciÊ wïasnoĂÊ name w naszym kodzie HTML, uĝyliĂmy znaczników Mustache ograniczonych podwójnymi klamrami. W klamrach tych umieszcza siÚ nazwy wïasnoĂci. Ostatni wiersz kodu dodaje wyrenderowany kod HTML do elementu body . Jest to najprostsza technika renderowania szablonu przy uĝyciu Mustache. W naszej aplikacji bÚdzie wiÚcej kodu zwiÈzanego z wysyïaniem ĝÈdania do serwera w celu pobrania danych, ale proces tworzenia szablonu pozostanie bez zmian. 7 https://github.com/janl/mustache.js Rozdziaï 2. • Interfejs uĝytkownika  81 Podmienianie istniejÈcego systemu Skoro wiemy juĝ, jak siÚ renderuje szablony, moĝemy z naszego programu usunÈÊ stary kod konkatenacji. Przyjrzymy mu siÚ, aby zobaczyÊ, co moĝna usunÈÊ, a co trzeba podmieniÊ. mustache/submit.html function renderNewProduct() { var productsList = $( #products-list ); var newProductForm = $( #new-product-form ); var product = {}; product.name = newProductForm.find( input[name*=name] ).val(); product.price = newProductForm.find( input[name*=price] ).val(); product.description = newProductForm.find( textarea[name*=description] ).val(); var newProduct = $( li /li ); newProduct.append( span class= product-name + product.name + /span ); newProduct.append( em class= product-price + product.price + /em ); newProduct.append( div class= product-description + product.description + /div ); productsList.append(newProduct); productsList.find( input[type=text], textarea ).each(function(input) { input.attr( value , ); }); } Ten skomplikowany kod bardzo trudno jest rozszyfrowaÊ, a jeszcze trudniej go modyfikowaÊ. Dlatego zamiast metody append() jQuery do budowy struktury HTML uĝyjemy systemu Mustache. Moĝemy napisaÊ prawdziwy kod HTML, a nastÚpnie wyrenderowaÊ dane przy uĝyciu Mustache! Pierwszym krokiem w kierunku pozbycia siÚ plÈtaniny JavaScriptu jest zbudowanie szablonu. Póěniej wyrenderujemy go wraz z danymi produktów w jednym prostym procesie. JeĂli utworzymy element script z typem treĂci text/template, to bÚdziemy mogli w nim umieĂciÊ kod HTML Mustache i pobieraÊ go stamtÈd do szablonu. Nadamy mu identyfikator, aby móc siÚ do niego odwoïywaÊ z kodu jQuery. script type= text/template id= product-template !-- Szablon HTML -- /script NastÚpnie napiszemy kod HTML naszego szablonu. Mamy juĝ produkt w postaci obiektu, a wiÚc jego wïasnoĂci w szablonie moĝemy uĝyÊ jako nazw zmiennych: 82  Web development. Receptury nowej generacji JaĂ pyta: Czy moĝna uĝywaÊ zewnÚtrznych szablonów? Szablony wewnÚtrzne sÈ porÚczne, ale my chcemy oddzieliÊ logikÚ szablo- nowÈ od widoków serwera. Na serwerze naleĝaïoby utworzyÊ folder do przechowywania wszystkich plików widoków. NastÚpnie, gdy trzeba wyrenderowaÊ jeden z szablonów, pobieramy go za pomocÈ jQery i ĝÈdania GET. $.get( http://mojastrona.com/js_views/zewnetrzny_szablon.html , function(template) { Mustache.to_html(template, data).appendTo( body ); } ); W ten sposób moĝna serwowaÊ widoki serwera osobno od widoków klienta. script type= text/template id= product-template li span class= product-name {{ name }} /span em class= product-price {{ price }} /em div class= product-description {{ description }} /div /li /script MajÈc gotowy szablon, moĝemy wróciÊ do poprzedniego kodu, aby zmieniÊ sposób wstawiania kodu HTML. Moĝemy pobraÊ odwoïanie do szablonu przy uĝyciu jQuery i za pomocÈ metody html() pobraÊ treĂÊ wewnÚtrznÈ. Póěniej trzeba jeszcze tylko przesïaÊ kod HTML i dane do Mustache. var newProduct = Mustache.to_html( $( #product-template ).html(), product); Wynik tego powinien byÊ juĝ zadowalajÈcy, chociaĝ gdy nie ma ĝadnego opisu, pole opisu nie powinno byÊ widoczne. Innymi sïowy, jeĂli nie ma opisu, nie powinniĂmy renderowaÊ jego elementu div . Na szczÚĂcie, w Mustache moĝna uĝywaÊ instrukcji warunkowych. Przy ich uĝyciu sprawdzimy, czy opis istnieje, i jeĂli tak, to wyrenderujemy dla niego element div . {{#description}} div class= product-description {{ description }} /div {{/description}} Przy uĝyciu tego samego operatora Mustache wykona iteracjÚ po tablicy. System sprawdzi, czy dana wïasnoĂÊ jest tablicÈ, i jeĂli tak, to automatycznie zastosuje iteracjÚ. Rozdziaï 2. • Interfejs uĝytkownika  83 Stosowanie iteracji Poniewaĝ udaïo nam siÚ wymieniÊ znacznÈ czÚĂÊ istniejÈcego kodu budujÈcego nowy produkt, postanowiliĂmy, ĝe wiÚkszÈ czÚĂÊ logiki aplikacji napiszemy w JavaScripcie. Zamienimy stronÚ indeksowÈ, na której wyĂwietlane sÈ produkty wraz z uwagami, na kod JavaScript, który bÚdzie robiï to samo. Utworzymy tablicÚ produktów na jednej z wïasnoĂci obiektu danych i kaĝdy produkt w tej tablicy bÚdzie miaï wïasnoĂÊ notes. WïasnoĂÊ notes bÚdzie tablicÈ, po której iteracja bÚdzie siÚ odbywaÊ wewnÈtrz szablonu. Najpierw pobierzemy i wyrenderujemy produkty. Przyjmujemy zaïoĝenie, ĝe ser- wer zwraca tablicÚ w formacie JSON wyglÈdajÈcÈ tak: $.getJSON( /products.json , function(products) { var data = {products: products}; var rendered = Mustache.to_html($( #products-template ).html(), data); $( body ).append(rendered); }); Teraz musimy zbudowaÊ szablon do wyrenderowania produktów. W Mustache iteracjÚ po tablicy wykonuje siÚ, przekazujÈc tÚ tablicÚ operatorowi #, np. {{#zmienna}}. WewnÈtrz iteracji wïasnoĂci, które wywoïujemy, znajdujÈ siÚ w kontekĂcie obiektów w tablicy. mustache/index.html script type= text/template id= products-template {{#products}} li span class= product-name {{ name }} /span em class= product-price {{ price }} /em div class= product-description {{ description }} /div ul class= product-notes {{#notes}} li {{ text }} /li {{/notes}} /ul /li {{/products}} /script Teraz nasza strona indeksowa moĝe byÊ w caïoĂci generowana w przeglÈdarce przy uĝyciu szablonów i Mustache. Szablony JavaScript sÈ doskonaïym narzÚdziem pozwalajÈcym dobrze zorgani- zowaÊ kod aplikacji JavaScript. NauczyïeĂ siÚ renderowaÊ szablony, uĝywaÊ instrukcji warunkowych oraz stosowaÊ iteracjÚ. Mustache.js jest prostym narzÚ- dziem pozwalajÈcym pozbyÊ siÚ konkatenacji ïañcuchów i budowaÊ strukturÚ HTML w czytelny i zgodny z semantykÈ sposób. 84  Web development. Receptury nowej generacji Kontynuacja Szablony Mustache pozwalajÈ zachowaÊ przejrzystoĂÊ nie tylko kodu dziaïajÈcego po stronie klienta, ale równieĝ serwerowego. IstniejÈ implementacje tego systemu w jÚzykach Ruby, Java, Python, ColdFusion i wielu innych. WiÚcej informacji na ten temat moĝna znaleěÊ na stronie Mustache8. Mustache moĝna zatem uĝywaÊ jako systemu szablonów zarówno przy budowie frontu, jak i zaplecza aplikacji. GdybyĂmy na przykïad mieli szablon Mustache reprezentujÈcy wiersz tabeli HTML i uĝyli go wewnÈtrz pÚtli budujÈcej tabelÚ przy wczytywaniu strony, to tego samego szablonu moglibyĂmy teĝ uĝyÊ w celu dodania wiersza do tej tabeli po udanym wykonaniu ĝÈdania Ajax. Zobacz równieĝ  Receptura 11.: „Dzielenie treĂci na strony”  Receptura 13.: „Tworzenie interaktywnych interfejsów uĝytkownika przy uĝyciu biblioteki Knockout.js”  Receptura 14.: „Organizacja kodu przy uĝyciu biblioteki Backbone.js”  Receptura 20.: „Budowanie witryny przy uĝyciu JavaScriptu i CouchDB” Receptura 11. Dzielenie treĂci na strony Problem Aby zaoszczÚdziÊ uĝytkownikom nadmiaru treĂci na jednej stronie, a przy okazji nie przeciÈĝyÊ serwerów, naleĝy ustaliÊ limit iloĂci danych, jaka moĝe zostaÊ wyĂwietlona na jednej stronie. NajczÚĂciej w tym celu dodaje siÚ mechanizm dzielenia stron. Jego dziaïanie polega na tym, ĝe wyĂwietlana jest tylko czÚĂÊ zawartoĂci strony i uĝytkownik moĝe w razie potrzeby przejrzeÊ takĝe pozostaïe czÚĂci. PoczÈtkowo wyĂwietlana jest tylko maïa czÈstka tego, co moĝna obejrzeÊ. W toku ewolucji stron internetowych ich twórcy spostrzegli, ĝe uĝytkownicy wiÚkszoĂÊ czasu spÚdzajÈ na przeglÈdaniu treĂci w sposób liniowy. NajchÚtniej 8 http://mustache.github.com/ Rozdziaï 2. • Interfejs uĝytkownika  85 przeglÈdaliby caïe listy danych, aĝ do znalezienia szukanych informacji albo osiÈgniÚcia koñca zbioru. Naszym zadaniem jest umoĝliwienie im takiego prze- glÈdania i jednoczeĂnie unikniÚcie przeciÈĝenia serwera. Skïadniki  jQuery  Mustache.js9  QEDServer RozwiÈzanie Paginacja to dobry sposób na zapanowanie nad zasobami, który dodatkowo uïa- twia korzystanie ze strony uĝytkownikowi. Zamiast zmuszaÊ uĝytkownika do wybrania nastÚpnej strony wyników i wczytywaÊ caïy interfejs od nowa, nastÚpnÈ stronÚ wczytujemy w tle i dodajemy jÈ do bieĝÈcej strony, podczas gdy uĝytkow- nik zbliĝa siÚ do jej koñca. Chcemy umieĂciÊ na stronie listÚ naszych produktów, ale jest ich za duĝo, aby wyĂwietliÊ je wszystkie naraz. Dlatego zastosujemy paginacjÚ z ograniczeniem polegajÈcym na wyĂwietlaniu maksymalnie 10 produktów naraz. ¿eby jeszcze bardziej uïatwiÊ ĝycie uĝytkownikom, pozbÚdziemy siÚ przycisku NastÚpna strona i zamiast tego bÚdziemy automatycznie wczytywaÊ kolejne strony, gdy uznamy, ĝe naleĝy to zrobiÊ. Od strony uĝytkownika bÚdzie to wyglÈdaïo tak, jakby caïa lista byïa dostÚpna na stronie od samego poczÈtku. Do budowy prototypu uĝyjemy QEDServera i jego katalogu produktów. Caïy kod ěródïowy umieĂcimy w folderze public w przestrzeni roboczej QEDServera. Uruchom QEDServer i utwórz nowy plik o nazwie products.html w folderze public utworzonym przez QEDServer. JeĂli nie wiesz, czym jest QEDServer, zajrzyj do „WstÚpu”, w którym znajduje siÚ objaĂnienie. Aby utrzymaÊ porzÈdek w kodzie, uĝyjemy biblioteki szablonów Mustache, o której byïa mowa w recepturze 10., „Tworzenie szablonów HTML przy uĝyciu systemu Mustache”. Pobierz tÚ bibliotekÚ i umieĂÊ jÈ takĝe w folderze public. Zaczniemy od utworzenia prostego szkieletu strony index.html w HTML5. DoïÈczymy do niej biblioteki jQuery, Mustache oraz plik endless_pagination.js, który bÚdzie zawieraï nasz kod paginacji. 9 http://github.com/documentcloud/underscore/blob/master/underscore.js 86  Web development. Receptury nowej generacji endlesspagination/products.html !DOCTYPE html html head meta charset= utf-8 title Produkty AwesomeCo /title link rel= stylesheet href= endless_pagination.css script type= text/javascript src= http://ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.min.js /script script type= text/javascript src= mustache.js /script script src= endless_pagination.js /script /head body div id= wrap header h1 Produkty /h1 /header /div /body /html W strukturze tej strony umieĂciliĂmy kontener na treĂÊ i obraz wirujÈcego kóïka widoczny na rysunku 2.8. Animacja ta ma na celu zasygnalizowaÊ uĝytkownikowi, ĝe trwa wczytywanie nastÚpnej strony, które powinno siÚ odbywaÊ w tle. endlesspagination/products.html div id= content /div img src= spinner.gif id= next_page_spinner API QEDServer jest tak skonfigurowane, ĝe zwraca stronicowane wyniki i reaguje na ĝÈdania JSON. Moĝemy siÚ o tym przekonaÊ, otwierajÈc adres http://localhost: 8080/Products.json?page=2. WiedzÈc, jakie informacje otrzymujemy od serwera, moĝemy rozpoczÈÊ budowÚ kodu, który bÚdzie aktualizowaï interfejs. W tym celu napiszemy funkcjÚ pobiera- jÈcÈ tablicÚ w formacie JSON, znakujÈcÈ jÈ przy uĝyciu szablonu Mustache i wynik tego dziaïania dodajÈcÈ na koñcu strony. Caïy ten kod umieĂcimy w pliku o nazwie endless_pagination.js. Zaczniemy od napisania funkcji pomocniczych. Na pierwszy ogieñ pójdzie funkcja renderujÈca odpowiedě w formacie JSON do HTML. endlesspagination/endless_pagination.js function loadData(data) { $( #content ).append(Mustache.to_html( {{#products}} \ div class= product \ a href= /products/{{id}} {{name}} /a \ br \ span class= description {{description}} /span \ /div {{/products}} , { products: data })); } Rozdziaï 2. • Interfejs uĝytkownika  87 Rysunek 2.8. Widok dolnej czÚĂci strony W procesie iteracji szablon utworzy dla kaĝdego produktu element div , w któ- rym treĂÊ jest nazwÈ produktu w postaci ïÈcza. NastÚpnie nowe elementy zostanÈ dodane na koñcu listy produktów i pojawiÈ siÚ na stronie. NastÚpnie, jako ĝe po dotarciu przez uĝytkownika na koniec strony chcemy wczytaÊ kolejnÈ stronÚ, musimy znaleěÊ sposób na okreĂlenie, czym jest ta nastÚpna strona. W tym celu moĝemy przechowywaÊ bieĝÈcÈ stronÚ jako zmiennÈ globalnÈ, a nastÚpnie gdy bÚdziemy gotowi — utworzyÊ URL dla nastÚpnej strony. endlesspagination/endless_pagination.js var currentPage = 0; function nextPageWithJSON() { currentPage += 1; var newURL = http://localhost:8080/products.json?page= + currentPage; var splitHref = document.URL.split( ? ); var parameters = splitHref[1]; 88  Web development. Receptury nowej generacji if (parameters) { parameters = parameters.replace(/[? ]page=[^ ]*/, ); newURL += + parameters; } return newURL; } Funkcja nextPageWithJSON() zwiÚksza wartoĂÊ zmiennej currentPage i dodaje jÈ do bieĝÈcego adresu jako wartoĂÊ parametru page=. ZapamiÚtujemy teĝ wszystkie inne parametry znajdujÈce siÚ w bieĝÈcym URL. JednoczeĂnie upewniamy siÚ, ĝe stary parametr page, jeĂli istnieje, zostanie nadpisany. DziÚki temu otrzymamy wïaĂciwÈ odpowiedě od serwera. Funkcje wyĂwietlajÈce nowÈ treĂÊ i okreĂlajÈce adres nastÚpnej strony sÈ juĝ gotowe. Czas w takim razie napisaÊ funkcjÚ pobierajÈcÈ treĂÊ z serwera. W istocie funkcja ta bÚdzie po prostu ĝÈdaniem Ajax wysyïanym do serwera. Musimy tylko zaimplementowaÊ w niej podstawowy mechanizm zapobiegajÈcy wysyïaniu nie- potrzebnych ĝÈdañ. Utworzymy zmiennÈ globalnÈ o nazwie loadingPage(), którÈ zainicjujemy wartoĂciÈ 0. Przed wykonaniem ĝÈdania bÚdziemy jÈ zwiÚkszaÊ, a po jego zakoñczeniu — zmniejszaÊ z powrotem. Jest to coĂ w rodzaju muteksu, czyli mechanizmu blokujÈcego. Bez tej blokady do serwera wysyïane byïyby dziesiÈtki ĝÈdañ, a serwer by na nie skwapliwie odpowiadaï, mimo ĝe nie o to nam chodziïo. endlesspagination/endless_pagination.js var loadingPage = 0; function getNextPage() { if (loadingPage != 0) return; loadingPage++; $.getJSON(nextPageWithJSON(), {}, updateContent). complete(function() { loadingPage-- }); } function updateContent(response) { loadData(response); } Po otrzymaniu odpowiedzi na ĝÈdanie Ajax przekazujemy jÈ do funkcji loadData(), której definicjÚ przedstawiono wczeĂniej. Po dodaniu nowej treĂci przez funkcjÚ loadData() aktualizujemy adres URL przechowywany w zmiennej nextPage. Jeste- Ămy gotowi na wykonanie kolejnego ĝÈdania Ajax. MajÈc funkcjÚ ĝÈdajÈcÈ nastÚpnej strony, teraz musimy zajÈÊ siÚ sprawdzaniem, czy uĝytkownik w ogóle jest gotowy na wczytanie kolejnej strony. Normalnie wyĂwietlilibyĂmy po prostu ïÈcze NastÚpna strona, ale nam chodzi o coĂ innego. Potrzebujemy funkcji, która bÚdzie zwracaÊ true, gdy dolna krawÚdě okna prze- glÈdarki znajdzie siÚ w okreĂlonej odlegïoĂci od doïu strony. Rozdziaï 2. • Interfejs uĝytkownika  89 endlesspagination/endless_pagination.js function readyForNextPage() { if (!$( #next_page_spinner ).is( :visible )) return; var threshold = 200; var bottomPosition = $(window).scrollTop() + $(window).height(); var distanceFromBottom = $(document).height() - bottomPosition; return distanceFromBottom = threshold; } Na koniec dodajemy procedurÚ obsïugi zdarzenia przewijania kóïkiem myszy, która wywoïuje funkcjÚ observeScroll(). Gdy uĝytkownik przewinie stronÚ za pomocÈ kóïka myszy, nastÈpi wywoïanie funkcji pomocniczej readyForNextPage(). Gdy funkcja ta zwróci true, wywoïamy funkcjÚ getNextPage(), aby wykonaÊ ĝÈda- nie Ajax. endlesspagination/endless_pagination.js function observeScroll(event) { if (readyForNextPage()) getNextPage(); } $(document).scroll(observeScroll); CzÚĂÊ dotyczÈcÈ „nieskoñczonego wyĂwietlania treĂci” mamy za sobÈ, ale przecieĝ kiedyĂ ta nasza treĂÊ jednak siÚ skoñczy. Po wyĂwietleniu ostatniego produktu wirujÈce kóïko powinno zostaÊ ukryte, poniewaĝ jeĂli bÚdzie widoczne, uĝytkow- nik pomyĂli, ĝe albo coĂ jest nie tak z jego ïÈczem internetowym, albo z naszÈ stronÈ. Aby usunÈÊ wirujÈce kóïko, dodamy test, który bÚdzie powodowaï jego ukrycie, gdy serwer zwróci pustÈ listÚ. endlesspagination/endless_pagination.js function loadData(data) { $( #content ).append(Mustache.to_html( {{#products}} \ div class= product \ a href= /products/{{id}} {{name}} /a \ br \ span class= description {{description}} /span \ /div {{/products}} , { products: data })); if (data.length == 0) $( #next_page_spinner ).hide(); } To wszystko. Gdy dotrzemy do koñca listy, wirujÈce kóïko zniknie. Kontynuacja Opisana tu technika jest doskonaïÈ metodÈ wyĂwietlania dïugich list danych w sposób zgodny z oczekiwaniami uĝytkowników. DziÚki podziaïowi rozwiÈzania na funkcje ïatwo je bÚdzie przystosowaÊ do róĝnych projektów. Moĝna zmieniÊ 90  Web development. Receptury nowej generacji FunkcjonalnoĂÊ przeglÈdarki IE 8 W przeglÈdarce IE 8 ten kod nie dziaïa. PrzeglÈdarka ta wymaga, aby nagïówki ĝÈdañ JSON byïy w bardzo specyficznym formacie, np. strona kodowa UTF-8 musi zostaÊ wysïana jako utf8. Bez poprawnych nagïówków ĝÈdanie Ajax nie powiedzie siÚ i na stronie bÚdzie wyĂwietlone tylko wiru- jÈce kóïko. Naleĝy o tym pamiÚtaÊ podczas pracy z formatem JSON na ser- werze i w przeglÈdarce IE. wartoĂÊ zmiennej treshold, aby treĂÊ byïa wczytywana wczeĂniej lub póěniej, lub zmodyfikowaÊ funkcjÚ loadData(), aby zwracaïa odpowiedě w formacie HTML lub XML zamiast JSON. A najlepsze jest to, ĝe moĝemy byÊ spokojni o dostÚp- noĂÊ naszej strony takĝe wówczas, gdy z jakiegoĂ powodu biblioteka jQuery nie bÚdzie obsïugiwana. Moĝesz to sprawdziÊ, wyïÈczajÈc JavaScript w swojej prze- glÈdarce. W nastÚpnej recepturze pokaĝemy Ci, jak ulepszyÊ ten kod poprzez dodanie obsïugi zmian adresu URL i przycisku Wstecz. Zobacz równieĝ  Receptura 12.: „ZapamiÚtywanie stanu w Ajaksie”  Receptura 10.: „Tworzenie szablonów HTML przy uĝyciu systemu Mustache” Receptura 12. ZapamiÚtywanie stanu w Ajaksie Problem JednÈ z najwiÚkszych zalet internetu jest moĝliwoĂÊ dzielenia siÚ odnoĂnikami z innymi luděmi. Jednak wraz z nadejĂciem ery Ajaksa nie wszystko jest takie proste. KlikajÈc ïÈcze Ajax, nie mamy gwarancji, ĝe spowoduje to zmianÚ adresu URL w pasku przeglÈdarki. To nie tylko utrudnia wymianÚ odnoĂnikami z innymi luděmi, ale równieĝ powoduje wadliwe dziaïanie przycisku Wstecz przeglÈdarki. Strony zawierajÈce takie ïÈcza nie sÈ dobrymi obywatelami internetu, poniewaĝ gdy siÚ je wyïÈczy, nie da siÚ wróciÊ bezpoĂrednio do poprzedniego stanu. Rozdziaï 2. • Interfejs uĝytkownika  91 Niestety, skrypt paginacji, który napisaliĂmy w recepturze 11., „Dzielenie treĂci na strony”, równieĝ nie naleĝy do wzorowych obywateli. Gdy uĝytkownik prze- wija stronÚ i przechodzi do kolejnych porcji informacji, adres URL caïy czas pozostaje taki sam. A przecieĝ kaĝde wczytanie oznacza nowy stan, w którym prezentowane sÈ inne dane niĝ bezpoĂrednio po wczytaniu strony. Gdyby na przykïad spodobaï siÚ nam produkt ze strony piÈtej i wysïalibyĂmy znajomemu odnoĂnik do niej, znajomy ten mógïby nie znaleěÊ tego, o czym mu pisaliĂmy, poniewaĝ zobaczyïby innÈ listÚ niĝ my. To nie wszystko. Gdy uĝytkownik kliknie przycisk Wstecz przeglÈdarki na stro- nie zbudowanej w caïoĂci na bazie Ajaksa, to nie przejdzie tam, gdzie by chciaï, tylko na poprzedniÈ stronÚ, z której trafiï do nas. Zdziwiony kliknie przycisk Dalej i pogubi siÚ caïkowicie. Na szczÚĂcie, znamy rozwiÈzanie tych problemów. Skïadniki  jQuery  Mustache.js10  QEDServer RozwiÈzanie W tej recepturze dokoñczymy pracÚ rozpoczÚtÈ w recepturze 11., „Dzielenie treĂci na strony”. Mimo iĝ zastosowana tam metoda ogólnie dziaïa, to jednak ma tÚ wadÚ, ĝe uniemoĝliwia odwiedzajÈcym dzielenie siÚ linkami do stron. Aby speïniÊ wymogi dobrego projektowania stron i nie utrudniaÊ ĝycia uĝytkowni- kom, musimy sprawiÊ, aby nasza lista produktów Ăledziïa swój stan. Innymi sïowy, gdy zmieni siÚ strona, na którÈ patrzymy, wraz z niÈ powinien zmieniaÊ siÚ adres URL. W specyfikacji HTML5 wprowadzono funkcjÚ JavaScript o nazwie pushState(), która w wiÚkszoĂci przeglÈdarek pozwala na zmianÚ adresu URL bez opuszczania strony. To doskonaïa wiadomoĂÊ dla programistów stron inter- netowych! DziÚki temu moĝemy napisaÊ stronÚ w caïoĂci opartÈ na Ajaksie, której przeglÈdanie nigdy nie wymaga wykonywania caïego cyklu ĝÈdañ i przeïa- dowañ. Oznacza to, ĝe nie musimy juĝ wczytywaÊ takich zasobów, jak nagïówek i stopka dokumentu, wysyïaÊ w nieskoñczonoĂÊ ĝÈdañ plików graficznych, arkuszy stylów i skryptów JavaScript za kaĝdym razem, gdy przechodzimy na nowÈ stronÚ 10 http://github.com/documentcloud/underscore/blob/master/underscore.js 92  Web development. Receptury nowej generacji w obrÚbie witryny. A uĝytkownicy mogÈ bez problemu przesyïaÊ linki znajomym i nigdy siÚ nie pogubiÈ, w którym miejscu w serwisie aktualnie siÚ znajdujÈ. Naj- lepsze jest to, ĝe przycisk Wstecz przeglÈdarki równieĝ bÚdzie dziaïaï poprawnie. Funkcja pushState() Funkcja pushState() jest jeszcze dopracowywana. WiÚkszoĂÊ starych przeglÈda- rek jej nie obsïuguje, ale istniejÈ rozwiÈzania awaryjne wykorzystujÈce czÚĂÊ adresu URL za znakiem #. RozwiÈzania te moĝe nie sÈ eleganckie, ale dziaïajÈ. Poza tym nie chodzi tylko o piÚkne adresy URL. Internet ma bardzo dobrÈ pamiÚÊ dïugotrwaïÈ. Stworzono go nie tylko do zabawy i pogaduszek, lecz równieĝ po to, aby moĝna byïo nawet po latach znaleěÊ stare strony, do których kiedyĂ utwo- rzyïo siÚ ïÈcze, a które zostaïy przeniesione na nowy serwer (pod warunkiem ĝe ich twórcy sÈ dobrymi obywatelami internetu i stosujÈ poprawne przekierowa- nia HTTP 301). JeĂli bÚdziemy uĝywaÊ znaku # w adresach URL jako tym- czasowego rozwiÈzania dla waĝnych informacji, to moĝe siÚ okazaÊ, ĝe nigdy siÚ ich nie pozbÚdziemy11. Poniewaĝ znaki # z adresów URL nigdy nie sÈ wysyïane do serwera, nasza aplikacja musiaïaby dalej przekierowywaÊ ruch po tym, gdy funkcja pushState() stanie siÚ standardem. Uzbrojeni w tÚ nowÈ wiedzÚ, zobaczmy, co trzeba zrobiÊ, aby nasza niekoñczÈca siÚ strona z produktami uzyskaïa ĂwiadomoĂÊ swojego stanu. Parametry, które trzeba ĂledziÊ Poniewaĝ nie wiemy, na którÈ stronÚ uĝytkownik wejdzie za pierwszym razem, bÚdziemy ĂledziÊ zarówno stronÚ startowÈ, jak i bieĝÈcÈ. JeĂli uĝytkownik wejdzie od razu na trzeciÈ stronÚ, to chcemy, aby przy kolejnych wizytach mógï na niÈ wróciÊ. JeĂli odwiedzajÈcy skorzysta z kóïka myszy na stronie trzeciej i wczyta kilka kolejnych stron, np. do strony siódmej, to równieĝ chcemy o tym wiedzieÊ. Potrzebujemy sposobu na zapamiÚtanie strony startowej i koñcowej, aby w przy- padku odĂwieĝenia uĝytkownik nie musiaï przewijaÊ wszystkiego od poczÈtku. Musimy teĝ znaleěÊ sposób na wysyïanie startowej i koñcowej strony z klienta. Najbardziej oczywistym rozwiÈzaniem w tym przypadku wydaje siÚ dodanie tych parametrów do adresu URL w ĝÈdaniu GET. Przy pierwszym wczytaniu strony ustawimy parametr page adresu na bieĝÈcÈ stronÚ i przyjmiemy zaïoĝenie, ĝe uĝytkownik chce obejrzeÊ tylko tÚ stronÚ. Gdy klient przekaĝe dodatkowo para- metr start_page, bÚdziemy wiedzieÊ, ĝe uĝytkownik chce obejrzeÊ kilka stron, od 11 http://danwebb.net/2011/5/28/it-is-about-the-hashbangs Rozdziaï 2. • Interfejs uĝytkownika  93 start_page do page. WracajÈc do poprzedniego przykïadu, gdybyĂmy byli na stronie siódmej, ale zaczÚli przeglÈdanie od strony trzeciej, to nasz adres URL wyglÈdaïby nastÚpujÈco http://localhost:8080/products?start_page=3 page=7. Te parametry powinny nam wystarczyÊ do odtworzenia listy produktów i po- kazania uĝytkownikowi takiej samej strony za kaĝdym razem. statefulpagination/stateful_pagination.js function getParameterByName(name) { var match = RegExp( [? ] + name + =([^ ]*) ) .exec(window.location.search); return match decodeURIComponent(match[1].replace(/\+/g, )); } var currentPage = 0; var startPage = 0; $(function() { startPage = parseInt(getParameterByName( start_page )); if (isNaN(startPage)) { startPage = parseInt(getParameterByName( page )); } if (isNaN(startPage)) { startPage = 1; } currentPage = startPage - 1; if (getParameterByName( page )) { endPage = parseInt(getParameterByName( page )); for (i = currentPage; i endPage; i++) { getNextPage(true); } } observeScroll(); }); Skrypt ten sprawdza parametry start_page i page, a nastÚpnie wysyïa ĝÈdanie odpowiednich stron do serwera. UĝyliĂmy funkcji bardzo podobnej do getNext ´Page() z poprzedniej receptury, tylko z obsïugÈ wielu ĝÈdañ naraz. W odróĝ- nieniu od sytuacji, gdy uĝytkownik korzysta z kóïka myszy i chcemy zapobiec nakïadaniu siÚ ĝÈdañ, w tym przypadku nie przeszkadza nam to, poniewaĝ wiemy dokïadnie, których stron ma dotyczyÊ ĝÈdanie. Podobnie jak ĂledziliĂmy wczeĂniej wartoĂÊ zmiennej currentPage, teraz bÚdziemy ĂledziÊ startPage. Parametr ten bÚdziemy pobieraÊ z adresu URL, dziÚki czemu bÚdziemy mogli wykonywaÊ ĝÈdania stron, które nie byïy jeszcze wczytywane. Liczba ta nie bÚdzie siÚ zmieniaÊ, ale musi byÊ dodawana do adresu URL i byÊ w nim przy kaĝdym ĝÈdaniu nowej strony. 94  Web development. Receptury nowej generacji Aktualizowanie adresu URL w przeglÈdarce Do aktualizacji adresu URL w przeglÈdarce napiszemy funkcjÚ o nazwie update ´BrowserUrl(), wywoïujÈcÈ funkcjÚ pushState() oraz ustawiajÈcÈ parametry start_ ´page i page. Naleĝy przy okazji pamiÚtaÊ, ĝe nie wszystkie przeglÈdarki obsïu- gujÈ funkcjÚ pushState(), i przed jej wywoïaniem sprawdzaÊ, czy jest zdefinio- wana. W tych przeglÈdarkach nasze rozwiÈzanie po prostu nie bÚdzie dziaïaÊ, ale powinniĂmy przygotowywaÊ nasze aplikacje z myĂlÈ o przyszïoĂci. statefulpagination/stateful_pagination.js function updateBrowserUrl() { if (window.history.pushState == undefined) return; var newURL = ?start_page= + startPage + page= + currentPage; window.history.pushState({}, , newURL); } Funkcja pushState() przyjmuje trzy parametry. Pierwszy jest obiekt stanu, który jest w istocie obiektem w formacie JSON. Argument ten moglibyĂmy wykorzystaÊ do przechowywania informacji dotyczÈcych stanu, poniewaĝ w wyniku przewija- nia od serwera otrzymujemy dane wïaĂnie w formacie JSON. Poniewaĝ jednak nasze dane sÈ proste i ïatwe do pobrania z serwera, wydaje siÚ, ĝe nie jest to warte zachodu. Drugi argument to tytuï nowego stanu. Nie jest on na razie szeroko obsïugiwany przez przeglÈdarki, ale w naszym przypadku to nie problem, bo i tak byĂmy tego nie potrzebowali. W zwiÈzku z tym przekazujemy w nim pusty ïañcuch. W koñcu dochodzimy do najwaĝniejszego elementu funkcji pushState(). Trzeci parametr okreĂla, co ma siÚ zmieniÊ w adresie. Moĝe to byÊ zarówno bezwzglÚdna Ăcieĝka, jak i zestaw parametrów, które majÈ zostaÊ zmienione na koñcu adresu. Ze wzglÚdów bezpieczeñstwa nie moĝna zmieniaÊ domeny, ale wszystko, co znajduje siÚ za niÈ — tak. Poniewaĝ nas interesuje tylko zmienianie parametrów adresu URL, na poczÈtku trzeciego parametru funkcji pushState() wpisaliĂmy znak ?. NastÚpnie wpisaliĂmy ustawienia parametrów start_page i page. JeĂli parametry te bÚdÈ znajdowaÊ siÚ w adresie, funkcja sama je zaktualizuje. statefulpagination/stateful_pagination.js function updateContent(response) { loadData(response); updateBrowserUrl(); } Na koniec, aby nasz mechanizm paginacji zaczÈï rozpoznawaÊ swój stan, doda- liĂmy wywoïanie funkcji updateBrowserUrl() do funkcji updateContent(). Od tej pory uĝytkownicy mogÈ bez przeszkód uĝywaÊ przycisku Wstecz, aby wyjĂÊ ze strony, i przycisku Dalej, aby na niÈ wróciÊ do tego samego miejsca. Takĝe odĂwieĝenie Rozdziaï 2. • Interfejs uĝytkownika  95 strony niczego nie zepsuje. Co jednak najwaĝniejsze, teraz odwiedzajÈcy mogÈ wysyïaÊ znajomym linki do naszych stron. DziÚki ciÚĝkiej pracy programistów przeglÈdarek internetowych udaïo nam siÚ sprawiÊ, aby nasza strona indeksowa byïa dobrym obywatelem internetu. Kontynuacja DodajÈc kolejne skrypty JavaScript i Ajax do swoich stron, powinniĂmy mieÊ ĂwiadomoĂÊ dziaïania uĝywanych interfejsów. Metoda pushState() HTML5 i API History pozwalajÈ nam przywróciÊ normalne dziaïanie kontrolek, do któ- rych uĝytkownicy sÈ przyzwyczajeni. Warstwy abstrakcji, takie jak History.js12, jeszcze to uïatwiajÈ, poniewaĝ dostarczajÈ eleganckich rozwiÈzañ awaryjnych dla starych przeglÈdarek. Opisane przez nas rozwiÈzania zaczynajÈ teĝ byÊ implementowane w bibliotekach JavaScript, jak np. Backbone.js, co oznacza, ĝe moĝemy spodziewaÊ siÚ jeszcze lepszej obsïugi przycisku Wstecz nawet na najbardziej skomplikowanych jedno- stronicowych aplikacjach. Zobacz równieĝ  Receptura 10.: „Tworzenie szablonów HTML przy uĝyciu systemu Mustache”  Receptura 14.: „Organizacja kodu przy uĝyciu biblioteki Backbone.js” Receptura 13. Tworzenie interaktywnych interfejsów uĝytkownika przy uĝyciu biblioteki Knockout.js Problem ProgramujÈc nowoczesne aplikacje sieciowe, staramy siÚ, aby w reakcji na dzia- ïania uĝytkownika aktualizowana byïa jak najmniejsza czÚĂÊ interfejsu. Odwoïania do serwera sÈ zawsze czasochïonne, a odĂwieĝanie caïej strony moĝe spowodowaÊ zniechÚcenie uĝytkownika. 12 http://plugins.jquery.com/plugin-tags/pushstate 96  Web development. Receptury nowej generacji Niestety, kod JavaScript uĝywany do implementacji tych mechanizmów czÚsto szybko wymyka siÚ spod kontroli. Na poczÈtku obserwowanych jest tylko kilka zdarzeñ, ale z czasem dodajemy kolejne funkcje zwrotne do aktualizowania róĝ- nych obszarów strony i utrzymanie tego wszystkiego w ryzach staje siÚ bardzo kïopotliwe. Knockout to prosta, a zarazem bardzo funkcjonalna biblioteka, pozwalajÈca wiÈ- zaÊ obiekty z interfejsem i automatycznie aktualizowaÊ jednÈ czÚĂÊ interfejsu, podczas gdy zmieniana jest inna czÚĂÊ, bez potrzeby uĝywania wielu zagnieĝdĝo- nych procedur obsïugi zdarzeñ. Skïadniki  Knockout.js13 RozwiÈzanie Knockout.js uĝywa modeli widoków, które zawierajÈ logikÚ widoku zwiÈzanÈ ze zmianami interfejsu. WïasnoĂci tych modeli moĝna wiÈzaÊ z elementami interfejsu. Chcemy, aby uĝytkownicy naszej strony mogli zmieniaÊ liczbÚ elementów w koszyku i od razu otrzymaÊ zaktualizowanÈ naleĝnÈ za nie sumÚ. Do budowy ekranu aktualizacji naszego koszyka moĝemy wykorzystaÊ modele widoków Knock- out. W koszyku kaĝdy produkt bÚdzie przedstawiony w postaci wiersza tabeli. W kaĝdym wierszu bÚdzie siÚ znajdowaÊ pole na liczbÚ egzemplarzy danego produktu oraz przycisk pozwalajÈcy usunÈÊ ten produkt z koszyka. Gdy zmieni siÚ liczba egzemplarzy któregoĂ z produktów, aplikacja natychmiast obliczy nowÈ sumÚ czÈstkowÈ oraz sumÚ za wszystkie towary. Gdy skoñczymy pracÚ, finalny efekt bÚdzie wyglÈdaï jak na rysunku 2.9. Rysunek 2.9. Interfejs koszyka 13 http://knockoutjs.com Rozdziaï 2. • Interfejs uĝytkownika  97 Podstawy Knockout Modele widoków Knockout to zwykïe obiekty JavaScript z wïasnoĂciami i meto- dami. Oto prosty obiekt Person z metodami dla imienia, nazwiska oraz imienia i nazwiska. knockout/binding.html var Person = function(){ this.firstname = ko.observable( Jan ); this.lastname = ko.observable( Kowalski ); this.fullname = ko.dependentObservable(function(){ return( this.firstname() + + this.lastname() ); }, this); }; ko.applyBindings( new Person ); Metody i logikÚ tego obiektu z elementami interfejsu wiÈĝemy przy uĝyciu atry- butu data- jÚzyka HTML5. knockout/binding.html p ImiÚ: input type= text data-bind= value: firstname /p p Nazwisko: input type= text data-bind= value: lastname /p p ImiÚ i nazwisko: span aria-live= polite data-bind= text: fullname /span /p Gdy zmienimy imiÚ albo nazwisko w jednym z pól, pod spodem zostanie wyĂwie- tlone zaktualizowane imiÚ i nazwisko. Poniewaĝ aktualizacja odbywa siÚ dyna- micznie, moĝe sprawiaÊ problemy osobom niewidomym, które korzystajÈ z czyt- ników ekranu. RozwiÈzaniem tego problemu jest uĝycie atrybutu aria-live, infor- mujÈcego czytniki, ĝe ta czÚĂÊ strony dynamicznie siÚ zmienia. To byï bardzo prosty przykïad, wiÚc teraz pokopiemy trochÚ gïÚbiej i utworzymy jeden wiersz danych naszego koszyka, w którym po zmianie liczby produktów bÚdzie odpowiednio zmieniaïa siÚ suma naleĝna. Póěniej na tej bazie zbudujemy caïy koszyk. Zaczniemy od utworzenia modelu danych. Pojedynczy wiersz bÚdzie reprezentowaÊ obiekt JavaScript o nazwie LineItem majÈcy wïasnoĂci name i price. Utwórz stronÚ HTML i doïÈcz do niej bibliotekÚ Knockout.js w sekcji nagïówkowej: knockout/item.html !DOCTYPE html html head title Aktualizacja liczby produktów /title script type= text/javascript src= knockout-1.3.0.js /script /head 98  body /body Web development. Receptury nowej generacji /html Na dole strony, nad znacznikiem /body , wstaw element script i wpisz w nim nastÚpujÈcy kod: knockout/item.html var LineItem = function(product_name, product_price){ this.name = product_name; this.price = product_price; }; W JavaScripcie funkcje sÈ konstruktorami obiektów, a wiÚc moĝna ich uĝywaÊ do naĂladowania klas. W tym przypadku konstruktor egzemplarza LineItem przyj- muje nazwÚ i cenÚ. Teraz musimy poinformowaÊ Knockout, ĝe chcemy uĝyÊ tej klasy LineItem jako naszego modelu widoku, aby jej wïasnoĂci byïy widoczne dla kodu HTML. W tym celu do skryptu dodajemy poniĝszy kod. knockout/item.html var item = new LineItem( Macbook Pro 15 , 1699.00); ko.applyBindings(item); Tworzymy nowy egzemplarz obiektu LineItem, ustawiajÈc w nim nazwÚ i cenÚ produktu, i wywoïujemy na nim metodÚ Knockout applyBindings(). Póěniej to zmienimy, ale na razie wystarczy nam zakodowanie danych na staïe. MajÈc obiekt, moĝemy zbudowaÊ interfejs i pobraÊ z tego obiektu dane. Koszyk zbudujemy na bazie tabeli HTML z elementami strukturalnymi thead i tbody . knockout/item.html div role= application table thead tr th Produkt /th th Cena /th th Liczba /th th Suma /th /tr /thead tbody tr aria-live= polite td data-bind= text: name /td td data-bind= text: price /td /tr /tbody /table /div Rozdziaï 2. • Interfejs uĝytkownika  99 Poniewaĝ wiersze tabeli sÈ aktualizowane danymi wprowadzanymi przez uĝytkow- nika, wierszom tym nadaliĂmy atrybut aria-live, aby czytniki ekranu wiedziaïy, ĝe naleĝy siÚ w nich spodziewaÊ zmian. Caïy koszyk dodatkowo umieĂciliĂmy w elemencie div z atrybutem HTML5-ARIA application, który informuje czytniki, ĝe jest to aplikacja interaktywna. WiÚcej informacji na temat tych atry- butów moĝna przeczytaÊ w specyfikacji HTML514. SzczególnÈ uwagÚ naleĝy zwróciÊ na poniĝsze dwa wiersze kodu: knockout/item.html td data-bind= text: name /td td data-bind= text: price /td Teraz nasz egzemplarz LineInstance jest widoczny globalnie na caïej stronie, a wiÚc tak samo widoczne sÈ jego wïasnoĂci name i price. Te dwa wiersze kodu ozna- czajÈ, ĝe tekst (text) tych elementów chcemy pobieraÊ z wïasnoĂci o okreĂlonych nazwach. Gdy teraz wczytamy naszÈ stronÚ w przeglÈdarce, to zauwaĝymy, ĝe zaczyna nabieraÊ ksztaïtu oraz ĝe pola nazwy i ceny produktu sÈ wypeïnione! Teraz dodamy pole, w którym uĝytkownik bÚdzie mógï zmieniÊ liczbÚ produktów w zamówieniu. knockout/item.html td input type= text name= quantity data-bind= value: quantity, valueUpdate: keyup /td W bibliotece Knockout do odwoïywania siÚ do pól danych w postaci elementów HTML sïuĝy parametr text, ale elementy formularzy HTML takie jak input majÈ atrybut value. Dlatego tym razem zwiÈzaliĂmy atrybut value z wïasnoĂciÈ quantity. WïasnoĂÊ quantity sïuĝy nie tylko do wyĂwietlania danych, lecz równieĝ do ich ustawiania. A gdy ustawimy dane, musimy teĝ uruchomiÊ zdarzenia. Do tego celu uĝywamy funkcji Knockout ko.observable() jako wartoĂci wïasnoĂci quantity naszej klasy. this.quantity = ko.observable(1); Funkcji ko.observable() przekazaliĂmy domyĂlnÈ wartoĂÊ, aby po wyĂwietleniu strony po raz pierwszy pole tekstowe coĂ juĝ zawieraïo. Teraz moĝemy juĝ wpisaÊ liczbÚ, ale chcielibyĂmy jeszcze dodatkowo wyĂwietliÊ sumÚ czÈstkowÈ dla kaĝdego wiersza. Dodamy do tabeli kolumnÚ na tÚ kwotÚ: 14 http://www.w3.org/TR/html5-author/wai-aria.html 100  Web development. Receptury nowej generacji knockout/item.html td data-bind= text: subtotal /td Podobnie jak w przypadku kolumn nazwy i ceny, tekst komórki ustawiamy na wartoĂÊ wïasnoĂci subtotal modelu widoku. To doprowadziïo nas do jednej z najwaĝniejszych czÚĂci biblioteki Knockout.js, metody dependentObservable(). WïasnoĂÊ quantity zdefiniowaliĂmy jako obser- wowalnÈ, co oznacza, ĝe gdy pole zmieni wartoĂÊ, zmiana ta bÚdzie zauwaĝona przez inne elementy. Deklarujemy metodÚ dependentObservable(), która bÚdzie wykonywaÊ kod w reakcji na zmianÚ wartoĂci obserwowanego pola, oraz przypi- sujemy tÚ metodÚ do wïasnoĂci naszego obiektu, aby moĝna jÈ byïo zwiÈzaÊ z naszym interfejsem uĝytkownika. this.subtotal = ko.dependentObservable(function() { return( this.price * parseInt( 0 +this.quantity(), 10) ); // label id= code.subtotal }, this); Ale skÈd metoda dependentObservable() wie, które pola obserwowaÊ? PrzeglÈda obserwowalne wïasnoĂci, które wymieniamy w definiowanej funkcji. Poniewaĝ mnoĝymy cenÚ i liczbÚ, Knockout Ăledzi obie te wïasnoĂci i wykonuje kod, gdy któ- rakolwiek z nich siÚ zmieni. Metoda dependentObservable() przyjmuje takĝe drugi parametr, który okreĂla kontekst dla wïasnoĂci. Ma to zwiÈzek ze sposobem dziaïania funkcji i obiektów w JavaScripcie. WiÚcej na ten temat moĝna przeczytaÊ w dokumentacji biblioteki Knockout.js. To wszystko, jeĂli chodzi o pojedynczy wiersz tabeli. Gdy zmienimy liczbÚ pro- duktów w zamówieniu, cena zostanie natychmiast zaktualizowana. Teraz roz- budujemy uzyskanÈ strukturÚ, aby utworzyÊ koszyk na wiele produktów, wyĂwie- tlajÈcy dodatkowo sumy czÈstkowe i ogólnÈ sumÚ naleĝnoĂci. WiÈzania przepïywu sterowania WiÈzanie obiektów z elementami HTML jest bardzo wygodne, ale w koszyku rzadko kiedy jest tylko jeden produkt, a duplikowanie caïego tego kodu byïoby bardzo ĝmudne, nie mówiÈc juĝ o dodatkowych komplikacjach zwiÈzanych z wiÚk- szÈ liczbÈ obiektów LineItem. Musimy coĂ zmieniÊ. Zamiast obiektu LineItem w roli modelu widoku, do reprezentowania koszyka uĝyjemy innego obiektu. Nadamy mu nazwÚ Cart i bÚdziemy w nim przechowy- waÊ wszystkie obiekty LineItem. WiedzÈc, jak dziaïa metoda dependentObservables(), Rozdziaï 2. • Interfejs uĝytkownika  101 moĝemy w obiekcie Cart utworzyÊ wïasnoĂÊ obliczajÈcÈ sumÚ naleĝnoĂci, gdy zmieni siÚ którykolwiek z elementów koszyka. A co z kodem HTML dla pojedynczego produktu? Duplikowania kodu moĝemy uniknÈÊ dziÚki uĝyciu tzw. wiÈzania przepïywu sterowania (ang. control flow binding) i nakazujÈc Knockout wyrenderowanie kodu HTML dla kaĝdego produktu w koszyku. Najpierw zdefiniujemy tablicÚ elementów, których uĝyjemy do napeïnienia koszyka. knockout/update_cart.html var products = [ {name: Macbook Pro 15 inch , price: 1699.00}, {name: PrzejĂciówka Mini Display Port na VGA , price: 29.00}, {name: Magic Trackpad , price: 69.00}, {name: Klawiatura bezprzewodowa Apple , price: 69.00} ]; W realnej aplikacji dane te pobieralibyĂmy z usïugi sieciowej lub wywoïania Ajax albo generowalibyĂmy je na serwerze podczas serwowania strony. Teraz utworzymy obiekt Cart do przechowywania produktów. Zdefiniujemy go w taki sam sposób, jak obiekt LineItem. knockout/update_cart.html var Cart = function(items){ this.items = ko.observableArray(); for(var i in items){ var item = new LineItem(items[i].name, items[i].price); this.items.push(item); } } Musimy zmieniÊ wiÈzanie z LineItem na klasÚ Cart. knockout/update_cart.html var cartViewModel = new Cart(products); ko.applyBindings(cartViewModel); Produkty sÈ zapisywane w koszyku za pomocÈ metody observableArray(), która dziaïa tak samo jak observable(), ale ma wïaĂciwoĂci tablicy. Gdy utworzyliĂmy nowy egzemplarz naszego koszyka, przekazaliĂmy do niego tablicÚ danych. Nasz obiekt iteruje po elementach danych i tworzy nowe egzemplarze LineItem, które sÈ zapisywane w tablicy produktów. Poniewaĝ tablica ta jest obserwowalna, interfejs zmieni siÚ po kaĝdej zmianie zawartoĂci tej tablicy. OczywiĂcie, teraz mamy wiÚcej niĝ jeden produkt, a wiÚc musimy zmodyfikowaÊ nasz interfejs. 102  Web development. Receptury nowej generacji JaĂ pyta: Jak wyglÈda sprawa dostÚpnoĂci w przypadku biblioteki Knockout? Interfejsy w duĝym stopniu oparte na JavaScripcie czÚsto bardzo sïabo wypadajÈ pod wzglÚdem dostÚpnoĂci, jednak uĝycie tego jÚzyka samo w sobie o niczym jeszcze nie Ăwiadczy. W tej recepturze uĝyliĂmy ról i atrybutów HTML5 ARIA, aby pomóc czyt- nikom ekranu w zrozumieniu dziaïania naszej aplikacji. Jednak kwestie dostÚpnoĂci dotyczÈ nie tylko czytników. W dostÚpnoĂci chodzi ogólnie o umoĝliwienie dostÚpu do treĂci jak najszerszemu gronu odbiorców. Knockout to rozwiÈzanie napisane w JavaScripcie, a wiÚc dziaïa tylko wów- czas, gdy obsïuga tego jÚzyka jest wïÈczona. Trzeba to braÊ pod uwagÚ. Najlepiej jest najpierw napisaÊ aplikacjÚ, która jest uĝyteczna bez Java- Scriptu, a nastÚpnie za pomocÈ biblioteki Knockout dodaÊ róĝne ulepszenia. W naszym przykïadzie zawartoĂÊ koszyka jest renderowana za pomocÈ biblioteki Knockout, ale gdybyĂmy uĝyli którejĂ z technologii serwerowych, moglibyĂmy renderowaÊ kod koszyka i stosowaÊ wiÈzania Knockout do wyrenderowanego kodu HTML. DostÚpnoĂÊ aplikacji zaleĝy przede wszyst- kim od sposobu jej zaimplementowania, a nie od konkretnej uĝytej do jej budowy biblioteki. NastÚpnie zmodyfikujemy naszÈ stronÚ HTML i nakaĝemy Knockout utworzyÊ wiersz tabeli dla kaĝdego produktu za pomocÈ wywoïania data-bind na elemencie tbody . knockout/update_cart.html h tbody data-bind= foreach: items tr aria=live= polite td data-bind= text: name /td td data-bind= text: price /td td input type= text name= quantity data-bind= value: quantity /td td data-bind= text: subtotal /td /tr /tbody NakazaliĂmy bibliotece Knockout wyrenderowaÊ zawartoĂÊ elementu tbody dla kaĝdego elementu tablicy items. Nie musimy nic wiÚcej zmieniaÊ. Teraz na naszej stronie moĝe byÊ wyĂwietlanych wiele wierszy tabeli i dla kaĝ- dego z nich bÚdzie wyĂwietlana suma czÈstkowa. Zajmiemy siÚ obliczaniem caï- kowitej kwoty do zapïaty i usuwaniem elementów. Rozdziaï 2. • Interfejs uĝytkownika  103 Kwota do zapïaty Sposób dziaïania metody Knockout dependentObservable() poznaliĂmy juĝ przy okazji obliczania sumy czÈstkowej dla kaĝdego produktu. DodajÈc jÈ do obiektu Cart, moĝemy jej uĝyÊ takĝe do obliczania sumy caïkowitej. this.total = ko.dependentObservable(function(){ var total = 0; for (item in this.items()){ total += this.items()[item].subtotal(); } return total; }, this); Kod ten zostanie uruchomiony za kaĝdym razem, gdy zmieni siÚ którykolwiek z produktów. Aby wyĂwietliÊ sumÚ caïkowitÈ, musimy oczywiĂcie jeszcze dodaÊ jeden wiersz do tabeli. Poniewaĝ ma to byÊ suma caïkowita naleĝnoĂci za wszyst- kie produkty, wiersz ten umieĂcimy poza elementem tbody , w elemencie tfoot umieszczonym bezpoĂrednio pod zamykajÈcym znacznikiem /thead . Umiesz- czenie stopki nad treĂciÈ tabeli pomaga niektórym przeglÈdarkom i technologiom pomocniczym w szybszym rozpracowaniu struktury tabeli. knockout/update_cart.html tfoot tr td colspan= 4 NaleĝnoĂÊ /td td aria-live= polite data-bind= text: total() /td /tr /tfoot Gdy odĂwieĝymy stronÚ i zmienimy liczbÚ przy którymĂ z produktów, nastÈpi automatyczna aktualizacja sumy czÈstkowej i caïkowitej. Teraz przejdziemy do przycisku usuwania produktów. Usuwanie produktów Na zakoñczenie musimy jeszcze dodaÊ przycisk Usuñ obok kaĝdego produktu, sïuĝÈcy do jego usuniÚcia z koszyka. DziÚki caïej wykonanej do tej pory pracy zadanie to jest juĝ bardzo ïatwe. Najpierw dodamy przycisk do tabeli. td button data-bind= click: function() { cartViewModel.remove(this) } Usuñ /button /td Tym razem zamiast wiÈzaÊ dane z interfejsem, wiÈĝemy zdarzenie i funkcjÚ. Przekazujemy this do metody remove() wywoïywanej na rzecz egzemplarza cartViewModel. Przycisk ten jednak nie dziaïa, poniewaĝ jeszcze nie zdefiniowa- liĂmy metody remove(). Jej definicja znajduje siÚ poniĝej: 104  Web development. Receptury nowej generacji ¿yj w zgodzie z serwerem! Coraz wiÚkszÈ popularnoĂÊ zdobywajÈ koszyki na zakupy, których aktu- alizacja odbywa siÚ w caïoĂci wyïÈcznie po stronie klienta. Czasami po pro- stu niemoĝliwe jest wysyïanie ĝÈdañ Ajax za kaĝdym razem, gdy uĝytkow- nik zmieni coĂ w interfejsie. StosujÈc to podejĂcie, musisz zadbaÊ o synchronizacjÚ danych w koszyku na kliencie z danymi na serwerze. Przecieĝ nie chciaïbyĂ, aby ktoĂ zmieniaï ceny produktów za Ciebie! Gdy uĝytkownik przechodzi do kasy, naleĝy przesïaÊ zaktualizowane war- toĂci na serwer i tam obliczyÊ sumy przed sfinalizowaniem transakcji. knockout/update_cart.html this.remove = function(item){ this.items.remove(item); } To wszystko! Poniewaĝ tablica items jest obserwowalna (observableArray), aktuali- zowany jest caïy interfejs, wraz sumÈ caïkowitÈ! Kontynuacja Biblioteka Knockout jest doskonaïym narzÚdziem do tworzenia dynamicznych jednostronicowych interfejsów, a dziÚki temu, ĝe nie jest zwiÈzana z ĝadnym frameworkiem sieciowym, moĝemy jej uĝywaÊ, gdzie tylko chcemy. Co waĝniejsze, modele widoków uĝywane w tej bibliotece sÈ zwykïym kodem JavaScript, dziÚki czemu moĝna jej uĝywaÊ do implementowania wielu czÚsto potrzebnych funkcji interfejsu uĝytkownika. Na przykïad przy uĝyciu Ajaksa z ïatwoĂciÈ moĝna by byïo utworzyÊ funkcjÚ wyszukiwania bieĝÈcego, zbudowaÊ kontrolki do edycji danych przesyïajÈce dane na serwer w celu ich zachowania, a nawet aktualizowaÊ zawartoĂÊ jednego menu rozwijanego na podstawie wartoĂci wybranej w innym. Zobacz równieĝ  Receptura 14.: „Organizacja kodu przy uĝyciu biblioteki Backbone.js” Rozdziaï 2. • Interfejs uĝytkownika  105 Receptura 14. Organizacja kodu przy uĝyciu biblioteki Backbone.js Problem W odpowiedzi na rosnÈce wymagania uĝytkowników w kwestii niezawodnoĂci i interaktywnoĂci aplikacji dziaïajÈcych po stronie klienta, programiĂci ciÈgle tworzÈ nowe niesamowite biblioteki JavaScript. Jednak im bardziej skompliko- wana jest dana aplikacja, tym bardziej jej kod wyglÈda jak pobojowisko peïne porozrzucanych bez ïadu i skïadu rozmaitych bibliotek, wiÈzañ zdarzeñ, wywo- ïañ Ajax jQuery i funkcji przetwarzajÈcych dane w formacie JSON. Potrzebna jest nam metoda tworzenia aplikacji dziaïajÈcych po stronie klienta w taki sam sposób, jak od lat tworzymy aplikacje serwerowe. Krótko mówiÈc, potrzebujemy jakiegoĂ frameworku. MajÈc solidny framework JavaScript, utrzy- mamy porzÈdek w programie, zredukujemy powtarzalnoĂÊ kodu oraz zastosujemy standard zrozumiaïy takĝe dla innych programistów. Poniewaĝ Backbone to skomplikowana biblioteka, ta receptura jest znacznie dïuĝsza i bardziej skomplikowana od innych. Skïadniki  Backbone.js15  Underscore.js16  JSON2.js17  Mustache18  jQuery  QEDServer 15 http://documentcloud.github.com/backbone 16 http://documentcloud.github.com/underscore/ 17 https://github.com/douglascrockford/JSON-js 18 http://mustache.github.com/ 106  Web development. Receptury nowej generacji RozwiÈzanie To zadanie moĝemy wykonaÊ przy uĝyciu wielu róĝnych frameworków, ale Backbone.js jest jednym z najpopularniejszych, dziÚki swojej elastycznoĂci, niezawodnoĂci i ogólnie wysokiej jakoĂci kodu. W chwili pisania tych sïów byï jeszcze wzglÚdnie nowy, a miaï juĝ wielu uĝytkowników. Przy uĝyciu Backbone moĝemy wiÈzaÊ zdarzenia w podobny sposób, jak to robiliĂmy przy uĝyciu Knockout w recepturze 13., „Tworzenie interaktywnych interfejsów uĝytkownika przy uĝyciu biblioteki Knockout.js”, ale teraz otrzymujemy modele wspóïpracujÈce z serwerem oraz system trasowania ĝÈdañ, za pomocÈ którego moĝna ĂledziÊ zmiany w adresach URL. DziÚki Backbone otrzymujemy bardziej niezawodny zrÈb apli- kacji, który moĝe doskonale sprawdziÊ siÚ w przypadku skomplikowanych aplikacji serwerowych, ale stanowiÊ przerost formy nad treĂciÈ w przypadku prostszych programów. Uĝyjemy Backbone do poprawienia wraĝliwoĂci interfejsu naszego sklepu inter- netowego, tzn. sprawimy, ĝe bÚdzie ĝywiej reagowaï na dziaïania uĝytkownika. Z naszych logów i badañ zachowañ uĝytkowników wynika, ĝe odĂwieĝanie strony trwa zbyt dïugo, a wiele rzeczy, które wykonuje siÚ za poĂrednictwem serwera, moĝna by byïo zrobiÊ na kliencie. Nasz kierownik zasugerowaï, abyĂmy caïy interfejs zarzÈdzania produktami zmieĂcili na pojedynczej stronie, na której bÚdzie moĝna dodawaÊ i usuwaÊ produkty bez odĂwieĝania strony. Zanim rozpoczniemy budowÚ naszego interfejsu, bliĝej poznamy Backbone i dowiemy siÚ, jak za pomocÈ tej biblioteki moĝemy rozwiÈzaÊ nasz problem. Podstawy Backbone Backbone to dziaïajÈca po stronie klienta implementacja wzorca model-widok- -kontroler, na powstanie której duĝy wpïyw miaïy serwerowe frameworki, takie jak ASP.NET MVC i Ruby on Rails. Backbone ma kilka komponentów poma- gajÈcych dobrze zorganizowaÊ kod zwiÈzany z komunikacjÈ z serwerem. Modele reprezentujÈ dane i mogÈ wspóïpracowaÊ z naszym zapleczem za poĂred- nictwem Ajaksa. Ponadto sÈ doskonaïym miejscem na wpisanie logiki biznesowej i kodu sprawdzajÈcego poprawnoĂÊ danych. Widoki w Backbone nieco róĝniÈ siÚ od widoków w innych frameworkach. SÈ nie tyle warstwÈ prezentacji, co raczej „kontrolerami widoku”. W interfejsie typowej aplikacji dziaïajÈcej po stronie klienta moĝe byÊ wiele zdarzeñ. Kod wywoïywany przez te zdarzenia jest przechowywany wïaĂnie w tych widokach. NastÚpnie mogÈ one renderowaÊ szablony i modyfikowaÊ nasz interfejs uĝytkownika. Rozdziaï 2. • Interfejs uĝytkownika  107 Routery ĂledzÈ zmiany adresu URL i mogÈ wiÈzaÊ ze sobÈ modele i widoki. Gdy chcemy pokazaÊ w interfejsie róĝne „strony” lub karty, do obsïugi ĝÈdañ i w celu wyĂwietlania róĝnych widoków moĝemy uĝyÊ routerów. W Backbone obsïugujÈ one takĝe przycisk Wstecz przeglÈdarki. W koñcu w Backbone dostÚpne sÈ kolekcje, dziÚki którym moĝemy ïatwo pobie- raÊ zbiory egzemplarzy modeli, z którymi chcemy pracowaÊ. Na rysunku 2.10 pokazano, jak poszczególne elementy Backbone ze sobÈ wspóïpracujÈ oraz w jaki sposób uĝyjemy ich do budowy naszego interfejsu do zarzÈdzania produktami. Rysunek 2.10. Skïadniki Backbone DomyĂlnie modele Backbone komunikujÈ siÚ z serwerowÈ aplikacjÈ RESTful przy uĝyciu metody ajax() z biblioteki jQuery i formatu JSON. Zaplecze musi akceptowaÊ ĝÈdania GET, POST, PUT i DELETE oraz rozpoznawaÊ dane w formacie JSON w treĂci tych ĝÈdañ. SÈ to jednak tylko ustawienia domyĂlne, które moĝna zmodyfikowaÊ. W dokumentacji Backbone znajdujÈ siÚ informacje na temat tego, jak zmodyfikowaÊ kod dziaïajÈcy po stronie klienta tak, aby wspóïpracowaï z róĝnymi rodzajami zapleczy. Nasze zaplecze bÚdzie obsïu
Pobierz darmowy fragment (pdf)

Gdzie kupić całą publikację:

Web development. Receptury nowej generacji
Autor:
, , , ,

Opinie na temat publikacji:


Inne popularne pozycje z tej kategorii:


Czytaj również:


Prowadzisz stronę lub blog? Wstaw link do fragmentu tej książki i współpracuj z Cyfroteką: