Hejka!

Bardzo się cieszymy, że tu jesteś :)

Mamy nadzieję, że czujesz się swobodnie i nie boisz się wyzwań 💪🏻

Witaj na warsztatach girls.js!

 

Nie przejmuj się, jeśli w trakcie warsztatów czegoś nie wiesz lub nie rozumiesz, jesteś tu po to, żeby się nauczyć!

Pamiętaj, że każda już pracująca programistka była kiedyś dokładnie na takim etapie, na jakim teraz jesteś Ty, w tym Twoja mentorka/mentor!

Dlatego nie bój się zadawać pytań w dowolnym momencie warsztatów :) 

❤️

Powodzenia!

Podczas dzisiejszych warsztatów stworzymy aplikację, która będzie prostym klonem Twittera ;) 

W jej ramach będziemy pobierać definicje Tweetów z serwera, wyświetlać je oraz uwzględnimy możliwość dodawania własnych.

Na początek kilka spraw organizacyjnych

W ten sposób będą oznaczone
tematy do przedyskutowania w grupie

Tak oznaczone są kolejne kroki
do wykonania

Gwiazdką oznaczone są większe zagadnienia, które mogą być dla Ciebie nowością i być może warto się przy nich odrobinę dłużej zatrzymać

* Mogą się też pojawić wyjaśnienia lub linki do zewnętrznych źródeł

Na początek

Narzędzia Programisty!

(ang. DevTools)

Prawie każda przeglądarka internetowa ma w sobie zaawansowane narzędzia, które pomogą nam zrozumieć z czego składa się strona i w jaki sposób ona działa.

Opcja zbadaj (ang. inspect) pozwala nam podejrzeć szczegóły.

Podświetlenie wybranego elementu na stronie

Klasy, style, i możliwość ich zmiany

Dostępna jest również konsola

Czas na eksperymenty!

Pobierz i otwórz pliki projektu.

 

 

 

 

 

Bez obaw, nieważne co zrobisz, po odświeżeniu strony wszystko wróci do pierwotnego stanu!

Otwórz DevTools'y na stronie index.html

i spróbuj trochę tam namieszać :)

  • zaczniemy od wyświetlania Tweetów, o których informacje już posiadamy,
  • w następnej części zajmiemy się pobieraniem nowych danych z serwera,
  • na sam koniec dodamy również obsługę wysyłania własnych postów.

Przejdźmy do naszego zadania!

Najpierw rozbijmy je na poszczególne etapy:

Tak będzie wyglądał nasz efekt końcowy!

 W pliku index.html możesz znaleźć szablon aplikacji wraz z pierwszym postem.

Przeanalizuj go.

Naszym pierwszym zadaniem będzie wyświetlenie kolejnych postów.

 Możesz zmienić jego treść i autora :)

Zapisz plik i odśwież stronę! 

Metodą kopiuj-wklej dodaj w pliku kolejny post.

Udało się? 🎉 Powinnaś zobaczyć na stronie swojego nowo stworzonego posta! 

Właśnie tak wygląda tworzenie statycznych stron internetowych. Wszystkie dane i treści są zapisane "na stałe" w pliku html.

Nasza aplikacja będzie trochę inna, będzie żywa! Dane będą pochodzić z serwera :)

Zastanówmy się więc, jak powinien przebiegać proces dodawania nowego posta. Możesz sugerować się tym, co robiłaś wcześniej kopiując i wklejając element w pliku.

    Stwórzmy prosty algorytm na dodawanie posta. Będziemy się nim kierować podczas pisania kodu.

Możesz sobie spisać kroki na kartce!

Nasz algorytm zapewne wygląda mniej więcej tak:

1. Znajdź istniejący element-rodzic,

wewnątrz którego zostanie utworzony nasz nowy post.

2. Stwórz nowy element-post.

3. Dodaj dane do posta.

4. Umieść posta wewnątrz elementu-rodzica.

 

Nie bardzo wiesz jak się do tego zabrać?

Przejdź do następnego slajdu!

    Otwórz DevTools'y i korzystając z nich     spróbuj dodać nowego posta na stronie kierując się algorytmem.

Na początku musimy znaleźć miejsce,
w którym chcemy umiejscowić naszego nowego posta
.

Wcześniej zapewne umieściłaś go wewnątrz elementu

 

 

To jest nasz rodzic!

<div id="posts">...</div>

Zastanówmy się teraz, jak znaleźć ten element używając JavaScript.

  W tym celu możemy skorzystać z metody document.querySelector.

Spróbuj używając konsoli w DevTools wyszukać ten element.

document.querySelector('twoj_selektor')

*twoj_selektor - taki sam selektor jak ten, którego byś użyła w css

Jest strukturą odzwierciedlającą zawartość strony internetowej. Wewnątrz niej znajdują się wszystkie elementy, które widzimy, i do których możemy się odwołać w JavaScript

Dla ułatwienia dalszej pracy, zapiszmy element rodzica w zmiennej.

Przypisz do zmiennej parent wcześniej "złapany" element.

Czy wiesz jak to zrobić?

Zmienna

Dla przypomnienia:

zmienną możemy określić jako "pojemnik na dowolną wartość". Może to być m.in. ciąg znaków, liczba, boolean (prawda/fałsz) i wiele innych!

Zmienne w Javascript tworzymy w następujący sposób:

let parent = document.querySelector('#posts');
let nazwa_zmiennej = wartość_zmiennej;

Twój wyjściowy kod powinien wyglądać mniej więcej tak:

Powinna zostać wypisana jej wartość. Udało się? Super! 🎉

  Wpisz w konsoli nazwę swojej nowo utworzonej zmiennej.

Kolejnym krokiem będzie stworzenie pojedynczego posta jako nowego elementu.

W tym celu wykorzystamy metodę createElement, która pozwoli nam na dynamiczne utworzenie elementu DOM. Jej użycie wygląda w następujący sposób:
 

document.createElement('typ_elementu')

*typ_elementu - może to być każdy element html: p, div, span itp.

Ponownie używając DevTools, stwórz element korzystając z wyżej wymienionej metody i przypisz go do zmiennej post.

Następnie chcemy dodać do elementu klasę. Wiesz jaką?

Dodanie klasy do elementu możemy przeprowadzić na dwa sposoby:
 

   1. Poprzez modyfikację pola elementu className

        tj.

 

   2. Wykorzystując metodę add na obiekcie classList,
        tj. 

element.className = "Twoja_klasa"
element.classList.add("Twoja_klasa")

*element - Twój nowo stworzony element posta

Często w programowaniu zdarza się tak, że dany efekt możemy osiągnąć stosując kilka różnych rozwiązań.

Dodawanie klasy do elementu jest tego dobrym przykładem, a wybór rozwiązania często zależy od preferencji programistki. 

Posłuży nam do tego metoda appendChild.

Następnym krokiem naszego algorytmu jest wrzucenie posta wewnątrz elementu rodzica.

parent_element.appendChild(child_element)

W naszej strukturze HTML powinien pojawić się nowo stworzony element:

A Twój kod powinien wyglądać
mniej więcej tak:

let parent = document.querySelector("#posts");
let post = document.createElement("div");
post.className = "post";
parent.appendChild(post);

Otwórz plik helpers.js - mamy w nim pustą funkcję addPostToHtml.

Spróbuj przenieść ten kod do Twojego projektu w edytorze.

Teraz zajmiemy się zawartością posta.


W pliku app.js znajduje się wywołanie funkcji addPostToHtml z przekazaniem jako argumentu obiektu z definicją posta - zawiera ona autora, datę dodania i jego treść.

Obiekt jest jedną z podstawowych struktur danych w Javascript. Możemy go sobie wyobrazić jako "pudełko", które pozwala nam zamykać dane i funkcje w obrębie jednej przestrzeni.

Takie uporządkowanie danych ułatwia pisanie kodu oraz wykonywanie operacji
w Javascript.

{
    author: "Maurycy Vivaldi",
    text: "Codziennie pojawia się ponad 500 milinów nowych Tweetów ",
    date: "25.02.2019, 06:26:01"
}

Widzisz podobieństwo? :)

Tak naprawdę już do tej pory wykorzystywaliśmy różne obiekty istniejące
w Javascript. Jednym z nich jest document posiadający metodę querySelector,
a odwołanie do niej wygląda tak:

 

document.querySelector(Twoj_selektor)

Wpisz w konsoli document i zobacz jakie są jego pola i metody

Znalazłaś znaną Ci już metodę querySelector?

OK, wróćmy do funkcji addPostToHtml. Póki co tworzy ona element posta i dodaje go na stronę.

Zacznijmy dodawać do niej kolejne kawałki kodu tak, aby uzyskać podobny efekt jak w istniejącym już poście na stronie.

Przyjrzyjmy się raz jeszcze strukturze pojedynczego posta.

Z jakich elementów

składa się gotowy post?

Pierwszym elementem naszego posta jest obrazek - aby dynamicznie utworzyć obrazek możemy posłużyć się znaną już nam metodą createElement.

document.createElement('img');

Super, powinnaś mieć już stworzony element! 🎉

Czy przypisałaś go do zmiennej?

Jednak wciąż czegoś nam brakuje - jak powiedzieć stronie, że w naszym elemencie chcemy mieć jeden konkretny obrazek?

Źródło obrazka definiuje jego atrybut ‘src’ - można do niego przekazać m.in. ścieżkę do pliku, który mamy lokalnie na komputerze, lub adres URL obrazka z internetu.

Aby zdefiniować źródło obrazka z poziomu JavaScript wystarczy przypisać do pola src
adres obrazka

element_obrazka.src = "sciezka_do_pliku";

Pozostało nam już tylko "wrzucenie" obrazka do posta wykorzystując metodę appendChild.

Spróbuj dodać obrazek
do Twojego posta

Twój kod powinen wyglądać mniej więcej tak:

let img = document.createElement("img");
img.src = "src/img/chinese-duck.png";
parent.appendChild(img);

W kolejnych krokach pozostaje nam dodanie w analogiczny sposób autora, tekstu i daty - do wykorzystamy pole ‘innerText’ tworzonych elementów - dzięki niemu możemy dynamicznie zarządzać tekstem zawartym w elemencie.

author.innerText = 'autor_posta'

       Czym w powyższym kodzie jest author?

Dodaj elementy odpowiedzialne za wyświetlanie treści posta oraz jego autora.

Algorytm wygląda dokładnie tak samo, jak przy tworzeniu wcześniejszych elementów. Do dzieła!

Zrobione? 😊 Twoja funkcja addPostToHTML powinna wyglądać mniej więcej tak:

function addPostToHTML(post) {
    let container = document.createElement("div");
    container.className = "post";
    let img = document.createElement("img");
    img.src = "src/img/chinese-duck.png";

    let author = document.createElement("div");
    author.className = "author";
    author.innerText = post.author;
    let text = document.createElement("div");
    text.className = "text";
    text.innerText = post.text;
    let date = document.createElement("div");
    date.className = "date";
    date.innerText = post.date;

    container.appendChild(img);
    container.appendChild(author);
    container.appendChild(text);
    container.appendChild(date);

    document.querySelector("#posts").appendChild(container);
}

OK, na tym etapie powinniśmy już mieć metodę, którą będziemy wykorzystywać
w przyszłości do dodawania kolejnych postów!

Wyświetlmy teraz więcej postów!

Przejdźmy do pliku app.js - znajdziemy w nim wykomentowaną tablicę definicji postów. Dobra nowina jest taka, że nie musimy ręcznie dodawać posta jednego po drugim.

       Czy znasz sposób, dzięki któremu można wykonać kilka operacji pod rząd bez konieczności pisania bardzo podobnego kodu kilkukrotnie?

Możemy użyć pętli forEach po tablicy i dodać posty wykorzystując napisaną już wcześniej funkcję addPostToHTML!

Do iterowania po tablicy posłuży nam metoda forEach, do której możemy przekazać funkcję, którą chcemy wywołać na każdym z jej elementów.

[1,2,3].forEach(numer => console.log('Oto numer ' + numer))

// Oto numer 1
// Oto numer 2
// Oto numer 3

Może zauważyłaś, że przekazywane na poprzednich slajdach funkcje wyglądają odrobinę inaczej. Są to tzw. funkcje strzałkowe (ang. arrow function).

Poniższe dwa przykłady działają w dokładnie taki sam sposób:

function (a) { 
    return a + 2;
}
(a) => a + 2

Wykorzystując metodę forEach,
spróbuj napisać kod, który wyświetli wszystkie posty na stronie

Wykorzystaj zdefiniowaną już wcześniej tablicę obiektów.  

Twój kod powinien wyglądać tak:

posts.forEach(post => addPostToHTML(post));

Po odświeżeniu na stronie powinny pokazać się nowe posty! 🎉

To koniec części pierwszej - w następnej zacznie się zabawa!

Pobierzemy definicje postów z serwera. Ale to już po przerwie na kawę! 🎉

Gotowa do dalszej pracy? :)

W tej części dowiesz się jak działa komunikacja pomiędzy frontendem a backendem.

Jeśli jeszcze tego nie zrobiłaś - weź sobie teraz coś do notowania, może się przydać!

 

 

 

 

 

 

Jak już zapewne wiesz, frontend jest warstwą wizualną strony lub aplikacji internetowej.

Backend z kolei działa "za kurtyną".
Jest on odpowiedzialny za przetwarzanie informacji, które później są wyświetlane
na frontendzie.

     Zastanów się przez chwilę jak działa "kelner" z obrazka.

Co jest potrzebne, żeby połączyć dwie warstwy ze sobą?

Żeby dostać z poziomu JavaScript (frontend) dane z backendu potrzebujemy skorzystać z API.

API, czyli Application Public Interface
to zestaw reguł komunikacji z komponentem.
Dla naszego serwera API definiuje w jaki sposób możemy pobrać z niego informacje, format akceptowanych danych przy próbie zapisu nowych postów itp.

W naszej aplikacji posty mogą być dodawane przez różnych użytkowników - wobec tego musimy pobierać z serwera ich aktualną listę. Nasza lista postów jest dostępna pod adresem. 

Wchodząc w link w przeglądrce (poprzez wpisanie w pasku nawigacji jako adresu URL) możemy podejrzeć jakie dane nam zwróci.

Ten sam adres możesz znaleźć już
w projekcie, jest on przypisany do stałej POSTS_ENDPOINT.

OK, spróbujmy w takim razie pobrać
te dane w naszej aplikacji!

Wykorzystamy do tego metodę fetch, która jako argument przyjmuje adres, z którego chcemy pobrać dane.

fetch('adres_do_zapytania_do_serwera')

Pobieranie danych z serwera może trwać różną ilość czasu, w zależności m. in.
od szybkości łącza, rozmiaru pobieranej odpowiedzi czy czasu jej generowania przez serwer.

Z tego powodu obsługa odpowiedzi serwera jest dokonywana w sposób asynchroniczny.

Jak myślisz, co to oznacza?

Ołówki w dłoń!

 

Standardowo, kod Javascript jest wykonywany
“z góry na dół“. W niektórych przypadkach nie chcemy jednak blokować dalszego wywoływania kodu ze względu na potencjalnie długi czas wykonywania wcześniejszej funkcji. Jedną z takich sytuacji jest właśnie pobieranie danych z serwera.

 

Dopiero, gdy przeglądarka otrzyma dane o które poprosiła może obsłużyć je w należyty sposób.

Do obsługi danych z serwera po wywołaniu metody fetch, należy wykorzystać obiekt Promise zwrócony przez tę metodę.

Promise to swego rodzaju obietnica otrzymania odpowiedzi.

 

Zapewnia ona:

- możliwość obsłużenia danych jeśli je otrzymamy,

   -  obsługę błędu, który może pojawić się podczas komunikacji z serwerem.

Wywołując na obiekcie Promise metodę then, możemy przekazać jako argument funkcję, która ma zostać wywołana w celu obsługi danych.

W naszym przypadku kod będzie wyglądał mniej więcej tak:

fetch('adres').then(obsłużDane)

*obsłużDane - funkcja, która zostanie wykonana w celu obsługi danych

Pamiętasz funkcje strzałkowe?
Teraz znowu się przydadzą.

 

Niestety nie może być za prosto ;)

Otrzymane dane nie są jeszcze w takiej formie, o jaką nam chodzi. Musimy jeszcze je przetworzyć na zrozumiały dla nas model danych. Do tego posłuży nam metoda json, która... również zwraca Promise

fetch(‘adres_do_odpytania’)
   .then(response => response.json())
   .then(data => {
       // obsłuż dane
   });

Zwrócone dane przez nasz serwer zawierają listę - podobną do tej używanej w części pierwszej. Na niej również możemy wywołać metodę forEach, aby dodać posty na naszą stronę.

Wykorzystując zdobytą już wiedzę o funkcji fetch oraz metodzie forEach, spróbuj pobrać
i wyświetlić dane z serwera

Twój kod powinien wyglądać mniej więcej tak:

fetch(POSTS_ENDPOINT)
   .then(response => response.json())
   .then(data => data.posts.forEach(addPostToHTML));

TADAA! 

Udało nam się uzyskać komunikację
z serwerem i poprawnie obsłużyć zwrócone dane!

Jeśli wciąż nie rozumiesz jakiegoś zagadnienia, spytaj Mentorki/Mentora.

Pamiętaj, nie ma głupich pytań!

W następnym kroku zajmiemy się dodawaniem postów na naszej stronie
i zapisywaniem ich na serwerze.

 

Ale to już po przerwie na kawę ;)

Ok, zacznijmy od formularza,
za pomocą którego będziemy dodawać nowe posty.

W pliku index.html jest kawałek zakomentowanego kodu. Usuń oznaczenia komentarza i zobacz jak wygląda i działa formularz.

Formularz pozwala na wpisanie nowego posta, ale niestety po kliknięciu na przycisk Dodaj posta nic się nie dzieje.

Twoim następnym zadaniem będzie zapewnienie interaktywności formularza - chcemy aby po naciśnięciu na przycisk post dodał się na stronie i został zapisany na serwerze.

Zaczniemy od dodawania posta na stronie.

W pliku app.js zdefiniuj nową funkcję (możesz ją nazwać np. sendNewPost), którą będziemy wywoływać po kliknięciu na przycisk.

Co należy zrobić aby wywołać tę funkcję po kliknięciu na przycisk?

Do dodawania obsługi wydarzeń zachodzących na danym elemencie takich jak kliknięcia służy metoda addEventListener

element.addEventListener('typ_wydarzenia', funkcja_do_obslugi)


Spraw, aby Twoja funkcja była wywoływana po kliknięciu w przycisk.

*typ_wydarzenia: click, hover, focus itp. więcej tutaj

Następnym krokiem będzie pobranie danych
z formularza i na ich podstawie dodanie nowego posta na stronie.

W tym celu musimy pobrać odpowiednią wartość pola obiektu textarea.
Możemy tego dokonać korzystając z właściwości value.

textAreaElement.value

Znajdź element textArea z formularza, pobierz jego wartość, a następnie korzystając ze stworzonej przez Ciebie funkcji addPostToHTML dodaj nowego posta na stronie

Udało się? Świetnie! 🎉
 

 

 

 

 

 

Po dodaniu nowego posta warto zadbać o wyczyszczenie formularza. Możesz to zrobić przypisując do pola value elementu textArea pusty łańcuch znakowy.

Jako autorkę posta możesz dodać w kodzie
swoje imię :)

Ok! Na obecną chwilę nasza aplikacja wygląda tak:

- pobieramy posty z serwera i je wyświetlamy,

- dodajemy nowe posty i je wyświetlamy

Jest jedna mała rzecz, której brakuje. Jaka?

Nie zapisujemy jeszcze danych na serwerze. Jeśli odświeżysz stronę zauważysz, że dodane przez Ciebie posty zniknęły :(

W celu zapisania danych na serwerze również wykorzystamy funkcję fetch.

 

Tym razem będziemy musieli dodać dodatkowe parametry dotyczące treści posta i jego autora. Zrobimy to poprzez przekazanie ich jako drugiego argumentu funkcji. Dodamy też dodatkowe wskazówki potrzebne do wysłania danych
w odpowiedni dla serwera sposób.

Zacznijmy od zdefiniowania trybu w jakim będziemy komunikować się z serwerem poprzez zdefiniowanie pola method. Domyślnie, fetch służy do pobierania danych (GET), więc w przypadku innych operacji (np. zapisu) musimy sami je dodać. W naszym przypadku wartością będzie 'POST'

 

fetch('adres_do_komunikacji', {
    method: 'POST'
});

Dodatkowo, musimy upewnić się, że dane są zrozumiałe dla serwera. Standardowym formatem danych przesyłania informacji jest JSON. Każdy obiekt w Javascript można sparsować do JSON'a korzystając z metody JSON.stringify. Dane posta należy umieścić w polu body.
 

fetch('adres_do_komunikacji', {
    method: 'POST',
    body: JSON.stringify(postData)
});

*sparsować - przepisać, przetłumaczyć na inny format danych

Pozostaje nam jeszcze poinformować serwer o tym w jakim formacie przesyłamy do niego informacje. W tym celu posłuży nam nagłówek HTTP 'Content-Type'

fetch('adres_do_komunikacji', {
    method: 'POST',
    body: JSON.stringify(postData),
​    headers: {
        'Content-Type': 'application/json'
    }
});

W wyniku pomyślnego dodania posta, serwer powinien zwrócić jego dane wraz z informacją
o dacie utworzenia. 

Zastąp wcześniejsze dodawanie posta
do HTMLa wywołaniem
addPostHTML z danymi zwróconymi przez serwer

Twój kod powinien wyglądać mniej więcej tak:

let requestParams = {
    method: 'POST',
    body: JSON.stringify(content),
    headers: {
        'Content-Type': 'application/json'
    }
};

fetch(POSTS_ENDPOINT, requestParams)
    .then(response => response.json())
    .then(post => addPostToHTML(post));

Gratulacje! W pełni ukończyłaś aplikację!

 

 

Mamy nadzieję, że podoba Ci się końcowy efekt Twojej pracy... oraz to czego się przy okazji nauczyłaś :)