WordHunt – plansza do gry przy użyciu Flex Layout

Dzisiaj pokażę jak wygląda plansza do gry. Przyznaję, że spodziewałem się że ten element zostanie skończony już dawno, ale wyszło jak wyszło. Zadanie to sprawiło mi więcej problemów niż się spodziewałem, ale jestem w miarę zadowolony z tego co otrzymałem.

Plansza i jej elementy

Jeżeli chodzi o styl, pozostawia ona jeszcze sporo do życzenia, ale pola na planszy zachowują się tak jak chciałem. Dostosowują się do wielkości ekranu, w pełni wypełniając całą dostępną przestrzeń.

Board_mobile

Plansza do gry 5×5 na małym urządzeniu

W przypadku, gdy plansza posiada więcej słów i pola nie będą się mieścić w całości, wyrazy zostaną ucięte. W przyszłości czcionka będzie zmieniać rozmiar w zależności od wielkości ekranu i długości słowa, dzięki czemu unikniemy takich sytuacji. Na troszkę większych urządzeniach np. tabletach wyświetlanie działa tak jak należy, nawet na największej możliwej planszy 8×8.

board2_tablet_8x8

Plansza do gry 8×8 na tablecie

Teraz, gdy plansza jest już w takim stanie, zaczynam dodawać pozostałe fragmenty. Na tą chwilę jest jeszcze menu boczne, gdzie będą dodatkowe informacje oraz opcje. Żeby nie marnować miejsca, jest ono ukryte. Gdy je otworzymy, menu pojawia się nad planszą.

board3_sidenav

Plansza do gry – menu boczne

Obecnie obsługuję jedynie przycisk pominięcia rundy, dzięki czemu kolejna drużyna może wykonać ruch. Jutro dodam obsługę „odsłonięcia” pola, czyli jedną z najważniejszych rzeczy do zrobienia.

Jak to wygląda pod maską?

Skoro już zademonstrowałem jak plansza wygląda, pokażę teraz jak to działa. Wygląd aplikacja zawdzięcza komponentom Angular Material Design, natomiast sam layout planszy działa na bazie Flex Layout. Obie biblioteki są w wersji beta, ale większych bugów nie znalazłem. Muszę jednak powiedzieć, że próg wejścia do tych technologi jest troszkę większy niż to, co spotyka nas przy pierwszym kontakcie z popularnym konkurentem, czyli poczciwym Bootstrapem. Nie ma jednak tragedii i coś nawet da się z tym zrobić.

Ekran gry podzielony jest na kilka elementów. Kod głównego komponentu, na którym umieszczone są wszystkie elementy wygląda następująco:

Kontener, w którym wyświetlane jest menu boczne, toolbar gdzie jest tytuł aplikacji, a będą także ikony drużyn oraz plansza do gry. Tutaj nie musiałem dużo majstrować przy stylach. Jedynie toolbar wymagał drobnych modyfikacji.

Ręcznie modyfikuję rozmiar górnego paska aplikacji, ponieważ zmieniał się on przy zmianie wielkości okna. Powodowało to, że plansza do gry odrobinę się przesuwała i wychodziła poza ekran.
W bazowej klasie ze stylami zdefiniowałem mixin, który wykorzystujemy w innych komponentach, tak jak powyżej. Dzięki temu dokładnie i w łatwy sposób sterujemy stylami w zależności od wielkości ekranu. Był on szczególnie potrzebny w przypadku planszy, ponieważ przesuwała się ona w dół o wysokość paska górnego.

Sam kod planszy jest dosyć krótki, ale powiedziałbym że intensywny. Mamy tutaj dwa layouty fxLayout: column i row. Kolumnowy sprawia, że każdy rząd zabiera odpowiednią ilość miejsca w pionie. Dzięki temu plansza wypełnia całą przestrzeń. Każdy rząd składa się z komponentu game-field, który wyświetla słowa i odpowiadać będzie za kliknięcie na element.

Wartość fxFlex, która definiuje nam jak dużo przestrzeni ma wykorzystać każdy blok, wyliczana jest w kodzie po załadowaniu danych gry. Na podstawie ilości rzędów i kolumn dodajemy odpowiednią wartość do każdego elementu. Wartość bazowa baseFlexValue, od której liczymy wielkość elementów sprawia, że otrzymamy małe odstępy pomiędzy polami, które będą zmieniały wielkość w zależności od wielkości ekranu. Gdyby było tam 100, nie mielibyśmy przerw. Można to oczywiście zaprogramować inaczej, np. poprzez margin pól gry, ale na razie zostawię to tak jak jest.

Jeżeli chodzi o stylowanie tego komponentu, trzeba było dodać obliczenie wielkości planszy z uwzględnieniem paska górnego. Bez tego plansza była przesunięta o jego wysokość.

Tutaj uwaga! calc nie przyjmuje zmiennych z SASSa, chyba że opakujemy je w nawiasy klamrowe poprzedzone hasztagiem. Trochę mi zajęło zanim na to wpadłem… Szczerze, to boję się tego ruszać, bo już raz coś zmieniłem, co sprawiło że zepsułem cały ekran gry i naprawiałem go dobrych kilka minut. Szybko nauczyłem się robić częste commity, o których zdarza mi się zapomnieć. Kodu nie jest wiele, ale jako że nie jestem mistrzem CSS, trochę mi zajęło znalezienie takiej konfiguracji.

Podsumowanie

Plansza w wersji beta gotowa, teraz dodanie obsługi wykrywania pól oraz mapy dla kapitanów. Gdy to będzie gotowe, MVP projektu będzie skończone. Mam nadzieję że przed końcem konkursu!

Pierwszy pull request, czyli dokładamy swoją cegiełkę do projektów typu open source

Ostatnio napisałem kilka postów dotyczących wykorzystania biblioteki SignalR w moim projekcie. Opisywałem tam, jak dodać obsługę SignalR po stronie klienta napisanego we frameworku Angular. W tym celu wykorzystałem bibliotekę ng2-signalr. Podczas pracy z nią zauważyłem, że autor nie dodał jednej funkcjonalności, a dokładnie możliwości wyboru kanału transportu. Zagłębiłem się więc w kod źródłowy i zacząłem działać…

Mój pierwszy pull request

Każdy kto zna stronę GitHub, wie że służy ona do przetrzymywania kodu źródłowego projektów, ale nie tylko. Możemy tam za darmo hostować strony internetowe znajdujące się tam w repozytorium. Projekty mogą być publicznie widoczne dla każdego, lub prywatne (płatna opcja). Twórcy wielu bibliotek trzymają tam swój kod. Nie tylko możemy go przeglądać, ale także dokonywać w nich zmian, oczywiście za zgodą autora. Jak już wcześniej wspomniałem, sam znalazłem się w sytuacji, gdzie potrzebowałem drobnej modyfikacji. Zrobiłem ją na szybko w kopii biblioteki lokalnie, ale stwierdziłem, że nie potrwa to za długo żeby zrobić to jak należy. Nie będę rozpisywał się nad tym co dokładnie zrobiłem. Kto jest ciekawy, niech poczyta o tym tutaj.

Pull request na stronie GitHub.com

Niektórzy być może na co dzień pracują z pull requestami w pracy. Sam do takich osób nie należę, dlatego musiałem sprawdzić samemu jak to działa. A jest to bardzo proste. Zaczynamy oczywiście od wyboru projektu w którym chcemy dokonać zmiany. Następnie klikamy na przycisk Fork.

Fork

Dzięki temu dodana zostanie nasza kopia repozytorium. To w niej będziemy dokonywać zmian. Istnieje także drugi typ takiej pracy, gdzie jesteśmy współtwórcami repozytorium. Wtedy możemy dodać nasz branch i dodać pull request do zmian zawartych w nim. Tak jak tutaj pokazuje jest jednak łatwiej, ponieważ nie musimy posiadać żadnych uprawnień do repozytorium. Na naszym profilu GitHub widać że zrobiliśmy forka repozytorium.

fork_2

Następnym krokiem będzie dokonanie odpowiednich zmian w kodzie. Można to zrobić online z poziomu GitHuba. W przypadku poprawek dokumentacji może i tak zrobilibyśmy, ale w naszym przypadku zmian dokonywać będziemy lokalnie z wykorzystaniem naszego ulubionego IDE. Ten krok pominę, bo to co zmienimy zależy od projektu. Następnie robimy commit naszych zmian. Gdy już je zapiszemy, możemy wysłać pull request, czyli dajemy informację autorowi o tym, że dokonaliśmy poprawek i są gotowe do pobrania.

pr_1

Autor sprawdza co zrobiliśmy i akceptuje zmiany, lub zwraca nam je do poprawki. Jeżeli musimy poprawić część kodu, wystarczy później ją zapisać w naszym branchu. GitHub automatycznie zaktualizuje nasz pull request. Voila, gotowe. Nasze zmiany zostały przyjęte i są dostępne dla innych.

Po co mam robić cudzy projekt?

Niektórzy być może zadają sobie takie pytanie. Wymienię moim zdaniem kilka najbardziej oczywistych. Być może nie każdego to przekona, ale warto spróbować.

Dla spokoju w projekcie. Jeżeli korzystamy z biblioteki i musimy zrobić w niej jakąś poprawkę, najszybciej będzie zrobić kopię kodu źródłowego i dodać ją do naszego projektu. Nie musi to być poprawka zrobiona najlepiej jak się da. Wiadomo, ważne żeby działało. Wadą tego rozwiązania jest to, że jeżeli biblioteka otrzyma inne poprawki, które będziemy chcieli mieć w naszym projekcie, nasza poprawka będzie musiała zostać dodana na nowo. Pozornie łatwiejsza zmiana będzie niosła ze sobą więcej pracy w przyszłości. Naturalnie możemy liczyć na to, że ktoś inny dokona tych poprawek. Wiadomo jednak że najlepiej liczyć zawsze na siebie…

Dla satysfakcji. Tutaj bardzo obiektywna sprawa, ale mi bardzo wiele radości sprawia myśl, że mogę dodać zmiany z których inni będą korzystać. Wielu programistów ma to samo, ten moment gdy coś działa® i my to dodaliśmy. Mimo wielu lat, dalej jest to dla mnie coś bardzo przyjemnego, polecam.

Dla własnych korzyści. Dodanie w CV że aktywnie działamy podczas rozwoju kilku projektów typu open source to może być wielki plus w oczach pracodawcy. Ten punkt jest głównie dla programistów z mniejszym stażem, którzy w ten sposób mogą zapełnić swoje skromne CV. Łatwo pokazać w ten sposób że potrafimy coś zrobić i wiemy więcej niż uczą tylko na studiach. Doświadczeni programiści także mogą czerpać z tego korzyści, aktywny udział w dużych projektach i nie tylko może odróżnić nas od konkurencji na rynku.

Podsumowanie

Żałuję, że dopiero teraz udało mi się zrobić mój pierwszy wkład w projekty tego typu. Następnym razem, gdy będę korzystał z biblioteki w której będzie jakiś problem, poświęcę chwilę na sprawdzenie czy poprawka mojego problemu to duży problem. Może jednak dodanie jej własnoręcznie będzie lepsze niż szukanie obejścia problemu w internecie? Zachęcam Was do tego samego, w końcu na co dzień wszyscy korzystamy z bibliotek na które ktoś inny poświęcił swój czas. Może warto poświęcić trochę naszego?

Poznajmy Postman’a!

Podczas rozwijania API chcielibyśmy w łatwy sposób przetestować, czy działa ono jak należy i zwraca wartości w takiej formie, w jakiej powinno. Chciałem dzisiaj opisać pokrótce narzędzie, które nadaje się to tego celu idealnie. Znam je od dawna, ale dopiero niedawno poznałem większość z jego sporych możliwości. Mowa tutaj o Postman’ie. Jest to darmowa aplikacja, która pozwala na wykonywanie zapytań oraz przechowywanie ich historii. Zapraszam na krótkie wprowadzenie.

Logo narzędzia Postman

Instalacja

Tutaj sprawa jest bardzo prosta, jeżeli mamy przeglądarkę Chrome, wystarczy że dodamy rozszerzenie, które można pobrać tutaj. Drugą opcją jest zainstalowanie natywnej aplikacji na nasz system. Ją z kolei możemy pobrać w tym miejscu. Natywna aplikacja omija kilka ograniczeń wersji z dostępnej w przeglądarce. Następnie zakładamy konto i logujemy się do aplikacji. Możemy przejść dalej!

Podstawowe funkcje

Aplikacja moim zdaniem ma bardzo przyjemny dla oka interfejs, ale łatwo przeoczyć tutaj kilka ciekawych funkcji. Na początek zajmijmy się jednak kilkoma podstawowymi rzeczami. W menu bocznym aplikacji możemy dodawać kolekcje, które będą przechowywały nasze zapytania. Możemy tutaj także zobaczyć historię ostatnio wykonanych requestów.

Kolekcje w postmanie

Kolekcje oraz historia

Kolekcje przechowują nasze zapytania. Dodajmy więc nowy request. Wybieramy typ zapytania z listy oraz adres który będziemy odpytywać.

Nowe zapytanie

Możemy wybrać typ zabezpieczeń który jest zaimplementowany w API, dodać nagłówki oraz parametry naszego zapytania. Następnie klikamy na przycisk Send i gotowe. W odpowiedzi dostaniemy status naszego zapytania, czas wykonania, rozmiar odpowiedzi, body, ciasteczka, nagłówki. Nasze zapytanie możemy zapisać w kolekcji i szybko wykonać je ponownie później.

Animacja pokazująca wykonanie zaptania

Wykonanie zapytania

Environments

Bardzo przydatną opcją Postmana jest możliwość tworzenia profili środowiska. Jest to niesamowicie pomocne gdy chcemy w szybki sposób testować naszą aplikację lokalnie, a zaraz potem już na produkcji lub środowisku testowym. Dzięki temu nie musimy dodawać wielokrotnie tego samego zapytania lub ciągle edytować tych już istniejących.

Zarządzanie środowiskami

Definiujemy tutaj adres naszego API, istniejący token, nazwę użytkownika, hasło lub cokolwiek innego. Dodajemy takie opcje zarówno dla środowiska dev jak i produkcyjnego.

Edycja środowiska

Edycja środowiska

Następnie, tam gdzie nasze dane będą zależne od środowiska, zamiast podawać te wartości, wstawiamy zmienne środowiskowe. Zostaną one automatycznie zastąpione wartościami zdefiniowanymi w aktywnym środowisku. W tym celu używamy podwójnych nawiasów klamrowych, na przykład {{url}}.

Użycie zmiennych środowiskowych

Użycie zmiennych

Tesy oraz skrypty

Kolejną przydatną rzeczą są testy oraz skrypty wykonywane przed zapytaniem. Przed wykonaniem zapytania możemy np. ustawić obecną godzinę do zmiennej. Podobnie do skryptów działają testy.
Służą one do kilku celów. Pierwszym i oczywistym jest możliwość sprawdzenia czy wynik który otrzymaliśmy jest poprawy, posiada wszystkie pola, odpowiedni status etc. Podobnie jak w przypadku skryptów, możemy ustawić tutaj zmienną, ale tym razem mamy dostęp do tego, co zwróciło nasze zapytanie. Możemy więc wysłać zapytanie np. o token, a następnie przypisać go do zmiennej.

Taka automatyzacja może nam bardzo przyśpieszyć pracę. Nie będziemy musieli sprawdzać wszystkiego samemu, co może być problemem przy większej ilości zapytań.

Test zapytania

Testy zapytania

Wykonywanie wielu zapytań

Ostatnią rzeczą którą chciałem pokazać jest wykonywanie wielu zapytań jednocześnie, co umożliwia nam Collection Runner. Przy jego pomocy możemy wykorzystać to, o czym pisałem w poprzednim akapicie. Przykładowo, jako pierwsze zapytanie w kolekcji dodajemy takie, które odpyta nasze API o token uwierzytelniający. Zapytanie te zapisze nasz token do zmiennej,
z której później będą korzystały inne zapytania. W ten sposób możemy automatyzować wykonywanie i testowanie bardzo dużej ilości zapytań.

Podsumowanie

Mam nadzieje że tym, którzy nie znali jeszcze tego narzędzia pokazałem, jak w łatwy sposób z niego korzystać i ułatwić sobie życie. Po więcej informacji dotyczących Postmana odsyłam na jego oficjalną stronę, gdzie można znaleźć więcej przykładów i dokładny opis funkcji o których tutaj nie wspomniałem.

Zmienne środowiskowe w Angular

Często, gdy pracujemy nad naszą aplikacją musimy korzystać z innych adresów dla naszego API na produkcji, a innego lokalnie. Między innymi w takich przypadkach przychodzą nam z pomocą zmienne środowiskowe aplikacji. Dzisiaj chciałem pokazać jak w szybki sposób dodać możliwość obsługi zmiennych środowiskowych w naszej aplikacji wykorzystującej Angulara. Myślałem że będzie to coś, co jest dostępne out of the box, jednak nie zawsze tak jest. Jeżeli korzystacie z Angular-CLI, macie dostęp do nich w katalogu environments. Ja pokażę jak łatwo coś takiego zaimplementować w sytuacji, gdy nie mamy takiego udogodnienia. Mój przykład opiera się o aplikację, która powstała z szablonu dostępnego w dotnet CLI o którym pisałem w poprzednich postach. Moja aplikacja wykorzystuje Webpacka.

Implementacja serwisu z konfiguracją

Moje rozwiązanie nie jest idealne, ponieważ obsługuje jedynie dwie wersje aplikacji: lokalną czyli developerską, oraz produkcyjną. Jeżeli chcielibyśmy mieć możliwość obsługi innego środowiska, należy dodać kilka dodatkowych elementów, np. task w Gulpie, który podmieniałby nasze konfiguracje. Ja nie będę się tutaj rozpisywał na ten temat. Rozwiązanie składa się z kilku elementów. Na początek dodajemy plik app.config.ts, który przechowuje nasze zmienne.

Plik ten zawiera dwie klasy, które implementują interfejs IConfig. Dzięki temu nigdy nie przeoczymy żadnej zmiennej podczas dodawania konfiguracji. Poniżej przykład takiego intefejsu.

Następnie dodajemy serwis, który będzie przekazywany wewnątrz aplikacji.

Tutaj, w zależności od tego, czy uruchomiliśmy aplikację lokalnie, czy na produkcji, tworzymy instancję klasy zawierającej nasze konfiguracje. Funkcja isDevMode sprawdza na jakim środowisku jesteśmy. W moim przypadku to Webpack uruchamia aplikację w odpowiednim trybie (podczas publishu na produkcję dodawana jest opcja env.prod, o tym więcej w dokumentacji dotnet CLI). W serwisie definiujemy tyle pól, ile potrzebujemy. Ja użyłem tutaj składni Typescipt’u, która pozwala na dodawanie getterów. Możemy taką klasę podzielić na mniejsze, jeżeli nie chcemy mieć jednej dużej klasy z całą konfiguracją. Taki serwis możemy wstrzykiwać w inne serwisy lub komponenty naszej aplikacji, oraz podmieniać ją na inną podczas testów.

Podsumowanie

Jest to rozwiązanie, które można jeszcze rozwinąć, ale obecnie spełnia moje wszystkie wymagania. Dodatkowo jest bardzo proste w implementacji i działa automatycznie, bez konieczności wykonywania innych operacji podczas rozwoju aplikacji, takich jak uruchamianie skryptów. Mam nadzieję, że komuś się przyda!

Routing w Angular2 – pierwsze kroki

Dzisiaj chce przedstawić jak w szybki sposób dodać routing do naszej aplikacji z frameworkiem Angular. Podczas tego procesu można natknąć się na kilka problemów, o których tutaj wspomnę. Do dzieła!

Routing krok po kroku

Moje przykłady podaję na projekcie, który powstał przy użyciu dotnet CLI. Po zainstalowaniu .NET Core SDK, w konsoli wpisujemy dotnet new --install Microsoft.AspNetCore.SpaTemplates::*. Daje nam to dostęp do wielu szablonów projektów. Następnie wpisujemy dotnet new angular i mamy gotowy projekt z którym można pracować! Po więcej informacji na ten temat odsyłam na bloga jednego z autorów tych szablonów, Steve’a Sandersona. Opisał to wszystko ładnie w tym poście.

Wracając do routingu, na początek definiujemy moduł, w którym powiemy aplikacji jak ma się zachowywać, gdy użytkownik będzie chciał wejść w podany przez niego adres na naszej stronie. Później zaimportujemy go do głównego modułu, który jest uruchamiany jako pierwszy. Moduł routingu składa się z kilku części. Na początek importujemy potrzebne komponenty:

Dekorator NgModule potrzebny jest do zdefiniowania modułu. Routes oraz RouterModule potrzebne są już do napisania kodu, który pozwoli nam na nawigację wewnątrz aplikacji. Następnie importowaliśmy komponenty, które sami dodaliśmy i chcemy wyświetlić. Następnie pora na przekazanie aplikacji jak ma się zachowywać, czyli definicję ścieżek.

Import RouterModule.forRoot(routes) załatwi nam sprawę routingu.

Dzięki takiej definicji, po otwarciu aplikacji, od razu zobaczymy komponent CatComponent. Został on jawnie zaimportowany na starcie aplikacji. W tym przykładzie pokażę także jeszcze jedną ważną sprawę na którą trzeba zwrócić uwagę, w momencie gdy dodajemy routing do naszej aplikacji. CatComponent został pobrany od razu gdy weszliśmy na stronę aplikacji, mogło to wpłynąć na czas otwarcia aplikacji. Takie podejście nazywamy EagerLoading. Jeżeli byłby on duży, mogłoby to w znaczący sposób wpłynąć na to, jak użytkownik będzie odbierał naszą aplikację. Z kolei DogModule zostanie załadowany dopiero wtedy, gdy będziemy chcieli wejść do niego. Będzie wtedy można zauważyć małe opóźnienie przed otwarciem tej strony. Ta metoda z kolei nazywana jest LazyLoading. Dzięki temu, że komponent załadowany będzie później, cała strona będzie ładować się szybciej. Coś za coś, idziemy tutaj na kompromis. Jednak nie musi tak być. Wcześniej w imporcie komponentów z @angular/router zaimportowałem jeszcze klasę PreloadAllModules. Jeżeli definicję modułu zmienimy na następującą:

Moduł DogModule , oraz wszystkie inne które tak dodamy do routingu pobrane zostaną w tle po uruchomieniu aplikacji. Dzięki temu zyskujemy zarówno szybkość ładowania całej aplikacji, jak i responsywność podczas przechodzenia pomiędzy stronami. Ważna uwaga: żeby LazyLoading działał w projekcie wykorzystującym Webpacka, musimy zainstalować paczkę angular-router-loader, a następnie użyć ją w pliku webpack.config.js podczas konfiguracji plików .ts.

Kolejnym istotnym punktem jest stała routableComponents którą eksportujemy razem z modułem. Dzięki temu że tutaj dodajemy moduły, nie trzeba będzie ich na nowo importować w głównym module.Należy pamiętać, że każdy komponent który jest używany musi być dodany do declarations.

Teraz pozostaje tylko dodać moduł DogModule:

Oraz moduł routingu DogRoutingModule:

Dla takich małych modułów może się wydawać, że to spory narzut, jeżeli chodzi o ilość pracy. Ale jeżeli nasze komponenty są bardziej rozbudowane i mają swoje ścieżki podrzędne, dzięki temu nasz główny komponent nie rozrośnie się do niebotycznych rozmiarów. Dodajemy także separację do logiki naszej aplikacji, co wpływa na przejrzystość kodu.

Na koniec dodajemy nasz MainComponent, czyli główną stronę aplikacji:

Oraz szablon dla tego komponentu:

W szablonie możemy zobaczyć dwa ważne elementy: routerLink oraz router-outlet. routerLink to odpowiednik ng-href z pierwszego Angulara, czyli po prostu doda nam odpowiedni adres dla odnośnika na podstawie podanej nazwy ścieżki. router-outlet to najważniejszy element tutaj, czyli miejsce gdzie wstawione zostaną nasze komponenty, w poprzedniej wersji ng-view. Dodatkowo należy pamiętać także o dodaniu do znacznika head wartość <base href="/">, bez tego routing nie zadziała.

Podsumowanie

Przedstawiłem tutaj jedynie część możliwości jakie daje nam Angular jeżeli chodzi o routing. W kolejnych postach przedstawię bardziej skomplikowane przypadki: przekazywanie parametrów w adresie scieżki, RouteGuards, czyli mechanizm pozwalający na np. blokowanie wejścia do niektórych fragmentów aplikacji oraz wykorzystanie więcej niż jednego router-outlet.

Wprowadzenie do Sass, czyli jak ułatwić sobie życie z CSS

Trochę to trwało, ale wygląda na to, że powoli zaczynam nabierać tempa z rozwojem projektu. Odrobina wolnego czasu pozwoliła mi na ogarnięcie technologii z których będę korzystał (a gdy chcemy korzystać z nowości, czasem może zająć to dosyć długo). Ostatnio projektowałem wygląd aplikacji za pomocą mockup’ów. Nastała więc pora tchnąc trochę życia w projekt i napisać trochę kodu. Przyznam, że słaby ze mnie designer, więc nie będzie to takie proste. Jeżeli mowa o wyglądzie aplikacji webowej, raczej na pewno pojawi się temat CSS’ów. Nie jest to moja najmocniejsza strona, więc postanowiłem sobie trochę ułatwić ten temat. W tym celu sięgnąłem po Sass.

Logo Sass

Co to jest Sass?

Sass, czyli Syntactically Awesome Style Sheets, to preprocesor języka CSS. Dodaje do niego funkcje, które nie są dostępne w podstawowej wersji CSS. Kod Sass jest kompilowany do zwykłego CSSa. W wyniku tej operacji otrzymujemy więc plik zawierający czysty CSS. Można zadać sobie pytanie: po co go używać, skoro ostatecznie uzyskujemy to samo? Jest takie powiedzenie, mówiące że Nie chodzi o cel, tylko o podróż. No i właśnie, korzystając z Sass podróż ta nie jest tak męcząca… Osobiście z CSS nie miałem aż tak dużo do czynienia. Praca ta polegała głównie na drobnych poprawkach, układaniu elementów etc. Motywy były już zwykle gotowe lub korzystałem z gotowych szablonów. Gdy dużo pracy poświęcamy upiększaniu naszej aplikacji za pomocą CSS, możemy jednak natrafić na wiele drobnych lub bardziej poważnych niedogodności.

Problemy z CSS

Pomine tutaj problemy, których użycie Sassa nie rozwiąże. Każdy, kto pracował z tą technologią miał czasem wrażenie, że żyje sobie własnym życiem. W moim przypadku wynika to zwykle z niewiedzy, ale nie zawsze.
Animowany obrazek satyryczny pokazujące typowe problemy z językiem CSS. Scena z kreskówki Family Guy.
A wracając do problemów, które Sass jest w stanie pomóc rozwiązać, to są to między innymi:

  • Problem z kolorem motywów, gdy w naszych plikach CSS trzeba zmienić jakiś kolor, albo po prostu w wielu miejscach użyć tej samej barwy. Musimy kopiować lub pamiętać o jaki kolor chodzi
  • Duplikacje kodu, gdy wiele elementów będzie miało ten sam styl i musimy powielać go wielokrotnie
  • Nie mamy możliwości zagnieżdżaniastylów, co może powodować problem gdy musimy zmieniać nazwę jakiejś klasy lub identyfikatora. Dodatkowo wpływa to także na przejrzystość kodu
  • Brak możliwości wykonywania obliczeń w naszych stylach, czasem chcemy żeby niektóre wartości były od siebie zależne. Musimy robić to ręcznie, czasem w wielu miejscach
  • Import stylów, który jest obecny, ale powoduje wykonanie kolejnego zapytania do serwera po importowany styl

Sass na ratunek

Tutaj z pomocą przychodzi nam Sass. Daje on gotowe rozwiązania na powyższe problemy. Opowiem o nich w skrócie poniżej. Jak wcześniej wspomniałem, Sass to preprocesor języka CSS, przeglądarki nie wiedzą co z nim zrobić. Najpierw trzeba go przekompilować. Nie będę tego tutaj dokładnie opisywał, ponieważ to jak to zrobimy zależy od technologi w jakiej będziemy pracowali. Na początek polecam poćwiczyć online np. na CodePen. Wystarczy, że wybierzemy jako preprocesor SCSS i możemy zaczynać. Tutaj kolejna uwaga: do wyboru mamy dwie składnie Sassa: klasyczną (Sass), oraz nową (SCSS). W poniższych przykładach używam składni SCSS.

Zmienne

Problemy związanie między innymi z kolorami rozwiązuje dodanie zmiennych:

Dzięki nim jeżeli w wielu miejscach wykorzystujemy te same wartości, w przypadku zmiany możemy je łatwo podmienić. Na zmiennych można wykonywać także operacje. Tutaj dla przykładu nagłówek będzie większy od bazowego tekstu o 10px.

Zagnieżdżanie reguł

Zagnieżdżanie pozwala nam na usunięcie kodu który byłby powielany, oraz dodaje mu przejrzystości.

Dzięki temu zabiegowi na pierwszy rzut oka widać, które elementy są ze sobą powiązane. Dodatkowo elementy font zostały pogrupowane razem, co też wpływa na ich czytelność.

Import

Dyrektywa @import pozwana na modularyzację naszego kodu. Dzięki temu możemy dodać do projektu plik .scss, który zawierać będzie tylko deklarację zmiennych z kolorami. W ten sposób łatwo dodawać różne motywy do naszej aplikacji. Poniżej przykładowy plik z deklaracją zmiennych przechowujących kolory.

Następnie wykorzystujemy import pliku ze zmiennymi.

Dzięki temu że importujemy plik .scss, jako efekt tej operacji otrzymamy jeden plik zawierający całą zawartość obu tych plików. Jeżeli importowalibyśmy plik .css, podczas importu musielibyśmy pobrać plik z serwera, przez co musielibyśmy wysłać kolejne żądanie na serwer.

Dziedziczenie reguł

Kolejna dyrektywa którą chcę przedstawić to @extend. Dzięki niej możemy łatwiej definiować elementy posiadające te same style, różniące się tylko kilkoma szczegółami.

Jako element wyjściowy tej operacji otrzymamy następujący kod CSS:

Jedyne zmiana to dodanie do deklaracji bazowej klasy naszej rozszerzonej implementacji, a na końcu dodanie pozostałej części. Zmiana niewielka, ale taki zapis łatwo pokazuje, że elementy te są ze sobą powiązane w logiczny sposób (pełnią podobną rolę). W przypadku gdy będziemy implementować wiele podobnych elementów, taki zapis jest moim zdaniem o wiele bardziej czytelny.

Powtarzalne reguły

Bardzo potężne narzędzie do jakiego daje nam dostęp Sass to dyrektywa @mixin. Dzięki niej nie będziemy musieli powielać wielokrotnie tego samego kody dla różnych, niepowiązanych ze sobą elementów, które mają jednak wspólne cechy. Przykładowo możliwość dodania zaokrąglonych krawędzi. Można to zaimplementować w następujący sposób:

Taki kod sprawia, że możemy wykorzystać tą samą funkcję (funkcja nie do końca można to nazwać, ale pokazuje o co tutaj chodzi) do zaokrąglania krawędzi. Dodatkowo posiada ona parametr, który może mieć wartość domyślną, dzięki czemu wiele elementów które w różny sposób chcemy zaokrąglić może z niej skorzystać.

Funkcje

Kolejna dyrektywa w arsenale Sass to @function. Dzięki niej możemy wydzielić fragment logiki, który będziemy mogli wielokrotnie użyć.

Dzięki temu oszczędzimy czas na operacje, które będziemy wykonywać wielokrotnie.

Podsumowanie

Nie przedstawiłem wszystkich zalet, jakie niesie ze sobą używanie Sass w naszym projekcie. Jeżeli chcecie dowiedzieć się więcej na ten temat, zapraszam do przejrzenia dokumentacji. Wydaje mi się że warto spróbować, jeżeli ktoś nie miał jeszcze styczności z tego typu technologiami. Na rynku jest oczywiście wiele innych rozwiązań, które pełnią podobną fukncję: LESS, Stylus, PostCSS. Niezależnie od naszego wyboru, wydaje mi się, że zawsze będzie to krok na przód.

Wprowadzenie do Angular 2

Dzisiaj chciałem opisać w kilku punktach najważniejsze cechy frameworka Angular dla użytkowników, którzy nie mieli z nim jeszcze styczności. Na wstępnie trzeba zaznaczyć, że twórcy odeszli od nazwy Angular 2 (głównie z powodu wporwadzenia Semantic Versioning). Od teraz wersje 1.x.x nazywamy AngularJS, a do wydań oznaczonych jako 2.x.x i dalej będziemy używali krótkiej nazwy Angular. Można o tym poczytać tutaj.

Czym jest Angular?

Logo frameworka Angular

Angular to framework służący do rozwijania aplikacji webowych oraz na platformy mobilne. Nie jest to kolejna wersja swojego poprzednika, został napisany całkowicie od nowa. Wersje te nie są ze sobą kompatybilne. Na pierwszy rzut oka widać, że dwójka bardzo różni się od swojego pierwowzoru. Poniżej postaram się przedstawić te różnice i pokazać, że pomimo tego że są to całkiem inne narzędzia, developerzy korzystający z jedynki będą mogli wykorzystać część swojego doświadczenia podczas pracy z nowym wydaniem.

Wybór języka

Twórcy Angulara dają nam wybór w kwestii języka, którym będziemy się posługiwać podczas pisania naszych aplikacji. Do wyboru mamy następujące opcje:

  • ES5, czyli po prostu JavaScript. Ten sam, który był najczęściej wykorzystywany podczas rozwoju aplikacji w starym AngularJS.
  • ES6/ES2015, jest to rozszerzenie standardowego języka JavaScript, dodające nowe funkcje. Nie wszystkie przeglądarki go wspierają, dlatego podczas rozwoju aplikacji wykorzystujących ES6 należy korzystać z kompilatorów np. Babel.
  • TypeScript wprowadza do JavaScript’u jeszcze więcej możliwośći np. typy, dzięki czemu łatwiej wyłapywać błędy podczas pisania aplikacji. Jest rozszerzeniem ES6, i tak samo jak on, wymaga kompilatora. Więcej na jego temat można poczytać w tym miejscu.
  • Dart jest to język programowania stworzony przez firmę Google. Po więcej informacji na temat tego języka zapraszam tutaj.

W dalszej części tego artykuły będę posługiwał się przykładami napisanymi w TypeScripcie.

Moduły

Obie wersje Angulara przedstawiają koncepcję modułu, jako swego rodzaju punkt wejścia dla naszej aplikacji. W pierwszej wersji definiowaliśmy go w następujący sposób:

W nawiasach kwadratowych podawaliśmy zależności z których chcieliśmy korzystać w naszej aplikacji. Następnie musieliśmy oznaczyć w HTMLu gdzie nasza aplikacji ma się znajdować:

Teraz definiujemy go w następujący sposób:

Moduł tym razem służy do oznaczenia z jakich elementów nasza aplikacja będzie się składała. Do tego celu służy dekorator @NgModule, który używamy w połączeniu z klasą modułu. Wskazujemy, z jakich modułów dostarczanych z zewnątrz będziemy korzystać (imports) np. RouterModule lub BrowserModule. Deklarujemy (declarations) z jakich komponentów będziemy korzystać w naszej aplikacji. Może się ona składać z setek lub tysięcy małych komponentów. Definiujemy tutaj także komponent wejściowy przy użyciu słowa bootstrap. Komponent ten zostaje uruchomiony jako pierwszy. W górnej części tego pliku znajdują się importy, czyli mechanizm TypeScriptu, który pozwala na użycie innych bibliotek w naszej aplikacji. Modułom dokładniej przyjrzymy się w kolejnych postach.

Kontrolery i Komponenty

Angular odszedł od koncepcji kontrolerów, używanych w pierwszej wersji. Kontrolery były definiowane w widoku:

Następnie dodawany był kod, który przedstawiał jego logikę.

Obecnie w widoku używamy tylko nazwy naszego komponentu np.:

Sam komponent zaś definiuje jego logikę oraz wygląd:

Importujemy tutaj dekorator komponentu, który pozwala nam na jego definicje. Wskazujemy selector, który będzie wskazywał na nasz komponent w HTMLu, jego wygląd poprzez template (można oczywiście dodać ten szablon w osobnym pliku), oraz logikę komponentu w klasie.

Dyrektywy

Dyrektywy dostępne w Angularze uległy znacznym zmianom, ale dalej są obecne. Popularne w pierwszej wersji dyrektywy to na przykład ng-repeat oraz ng-if. W nowej wersji zostały one zastąpione przez *ngFor oraz *ngIf. Nazywamy je Structural directives, ponieważ wpływają na strukturę naszej strony (powielają elementy, usuwają etc). Są poprzedzane znakiem *. Przykłady dla AngularJS:

A teraz nowa wersja:

Różnice nie są wielkie, oprócz samej składni, dla *ngFor dochodzi także słowo kluczowe let, które definiuje zmienną lokalną.
Oprócz tego, w Angularze usunięte zostają inne dyrektywy, takie jak ng-src, ng-style, ng-href. Podobnie jest z dyrektywami, które służyły do obsługi zdarzeń np. ng-click czy ng-focus. Zamiast tego, możemy podpiąć się bezpośrednio do atrybutów lub zdarzeń w HTML, na przykład:

Do właściwości podpinamy się poprzez nawiasy kwadratowe [], a do zdarzeń nawiasy okrągłe (). Daje nam to dużo możliwości, ponieważ nie musimy już polegać na tym, czy interesująca nas dyrektywa istnieje. Zamiast tego korzystamy po prostu z atrybutów oraz zdarzeń w HTMLu.

Data binding

Tutaj także jest wiele podobieństw, chociaż ponownie składnia trochę się różni. O ile dalej możemy korzystać z nawiasów klamrowych {{...}} aby wyświetlić dane lub tworzyć wyrażenia, to two-way oraz one-way binding znany z poprzednika wygląda trochę inaczej. Działa tutaj ten sam mechanizm, który pokazałem w dyrektywach. One-way binding, w którym zmiany wychodzą z widoku do kodu, odbywa się poprzez użycie nawiasów okrągłych (). Tutaj przykładem mogą być eventy, gdzie zdarzenie wysyła nam informacje do naszej klasy. Wysyłanie danych z klasy do widoku odbywa się poprzez nawiasy kwadratowe [] (wykorzystując atrybuty, klasy, style, właściwości) lub klamrowe {} (mechanizm ten nazywa się Interpolation). Two-way binding, czyli przesył danych w obie strony, odbywa się zarówno przy użyciu nawiasów kwadratowych i okrągłych [(...)]. Składnia ta wydaje się dziwna, ale jest w 100% spójna z tym co powiedzieliśmy wcześniej: dane zarówno wysyłane są z widoku do kodu, oraz z kodu do widoku.

Zamiast nawiasów mamy możliwość korzystania z prefixów bind- lub on- dla komunikacji w jedną stronę, lub bindon- w obie strony. Przykłady:

Serwisy

W pierwszym Angularze mieliśmy do wyboru kilka metod, które pozwalały na dodawanie serwisów do aplikacji. Dzięki nim byliśmy w stanie wyciągnąć w jedno miejsce logikę, która powtarzała się w wielu fragmentach aplikacji lub pozwalały na komunikację pomiędzy różnymi jej elementami. Czasem pojawiał się problem: którą metodę wybrać? Do wyboru były popularne Factory, Service czy Provider, a także Constants oraz Values. Ten element został całkowicie zmieniony w nowej wersji. Zamiast wybierać dostępne mechanizmy, po prostu dodajemy klasę, która ma w sobie określoną funkcjonalność, a następnie używamy jej w dowolnym miejscu.

Należy jedynie dodać dekorator @Injectable, który pozwoli na wstrzyknięcie naszego serwisu np. do komponentu. O tym opowiem w kolejnym akapicie.

Dependency Injection

Dependency Injection to wzorzec architektoniczny, polegający na wstrzykiwaniu zależności do naszego kodu. Jeżeli chcemy odseparować z komponentu logikę np. pobierania użytkowników z bazy danych, a w samym komponencie zająć się jedynie ich wyświetlaniem, możemy zrobić to w łatwy sposób. Pobieranie danych wyciągamy do Serwisu, czyli innej klasy, a w komponencie wskazujemy na to, że potrzebujemy dostać obiekty do wyświetlenia. W module dodajemy komponent i serwis:

W komponencie wskazujemy, że potrzebujemy serwisu (używamy tutaj CatsService z poprzedniego akapitu), który zwróci nam nasze obiekty do wyświetlenia:

W ten sposób nasz komponent będzie w stanie wyświetlić listę naszych kotów, bez wiedzy skąd trzeba je pobrać!

Podsumowanie

Oczywiście, to co tutaj przedstawiłem to tylko część dobrodziejstw które oferuje nam Angular. Mamy jeszcze Routing, który został znacznie uproszczony, filtry, które teraz nazywają się Pipes oraz wiele innych. Wydaje mi się, że pomimo wielu różnic które pojawiły się w nowej wersji (wiele z nich moim zdaniem dobrych), kilka konceptów znanych z pierwszego wydania tego frameworka można zastosować w nowych aplikacjach, które będą już korzystały z najnowszej edycji. Dzięki temu próg wejścia w tą technologię jest dla weteranów AngularJS znacznie mniejszy.

Na dzisiaj to tyle. Tematy, które nie zostały tutaj poruszone na pewno pojawią się w przyszłości. Postaram się także rozwinąć bardziej szczegółowo to, o czym już mówiłem. Zapraszam do komentowania!