Tym razem rozwinę moją aplikację Vue o backend. Dzisiejszy post będzie dotyczył Vue tylko połowicznie. Trochę miejsca poświęcę na pobieżne opisanie tego jak napisałem obsługę apki od strony serwera.
Do stworzenia serwera użyłem bardzo popularnego frameworka Express. To znaczy, że wszystko zostaje w JavaScripcie. Natomiast po stronie front-endu dodałem obsługę zapytań aplikacji Vue do serwera, więc będzie ciekawie 🙂
Do tej pory aplikacja nie zapisywała nigdzie listy rzeczy do zrobienia. Zawsze wyglądała tak samo a wszelkie zmiany znikały po zamknięciu strony. Dzięki opisanej dziś aktualizacji mogę zmienić tę sytuację. Rozszerzyłem aplikację o bardzo prosty mechanizm, który po stronie serwera pobiera i zapisuje aktualny stan list. Dane umieszczane są w pliku JSON.
Aplikacja zaczęła nabierać rozmiarów, więc w poście raczej nie pojawi się kompletny kod. Założyłem za to specjalne repozytorium na Gicie, w którym można zobaczyć aktualny stan projektu. Można tam pobrać i pobawić się aplikacją na własną rękę.
Część frontendowa znajduje się w plikach index.html i app.js. Ten drugi plik znajduje się w katalogu src. Usunąłem z projektu katalog lib i pobieram vue z CDNa.
Cały kod serwera znajduje się w pliku server.js. Od niego zacznę dzisiejszy opis. Oto jego zawartość:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
var express = require('express'); var bodyParser = require('body-parser') var path = require('path'); var fs = require('fs') var app = express(); app.use(express.static(__dirname + '/src')); app.use(bodyParser.urlencoded({ extended: false })); app.use(bodyParser.json()); app.get('/', function(req,res){ res.sendFile(path.join(__dirname + '/index.html')); }) app.get('/todos', function(req,res){ fs.readFile('data.json', 'utf8', function (err,data) { if (err) { return console.log(err); } res.json(data); }); }) app.post('/todos', function(req,res){ console.log("someone is posting"); console.log(req.body); var recieved = JSON.stringify(req.body); var toSave = '{"todos":'+recieved+'}' fs.writeFile("data.json", toSave, function(err) { if(err) { return console.log(err); } console.log("The file was saved!"); }); }) app.listen(3000, function () { console.log('Example app listening on port 3000!'); }); |
Jak widać jest to typowa aplikacja Node’owa. Najpierw importuje do programu potrzebne moduły. Dwa z nich należy zainstalować z npma. Są to express oraz body-parser. Pierwszy to kod frameworka do obsługi serwera, a drugi to middleware, pozwalający serwerowi na parsowanie danych przesyłanych przez klienta.
Następnie tworzę zmienną app do której przypisuję instancję expressa. Można powiedzieć, że app to mój serwer. Kolejne trzy linijki to wywołanie metody serwera use. Pierwsza z nich wskazuje na katalog, który ma serwować pliki statyczne. W moim wypadku to oczywiście src z kodem javascriptowym. Jeżeli nie jesteś pewny o co chodzi, nie martw się. Ważne żeby wiedzieć, że jest to konieczne aby korzystać z pliku javascriptowego po stronie klienta. Dwa kolejne wywołania use to wskazanie, w którym miejscu serwer ma używać body-parser`a.
Teraz kiedy wszystko jest gotowe, mogę przejść do mięcha serwera, czyli obsługi zapytań od klienta. Mam tu obsługę dwóch zapytań typu GET oraz jednego typu POST. Obsługa tych zapytań wygląda zawsze tak samo. Wywołuję odpowiednią metodę obiektu app (get lub post) i przekazuje mu dwa argumenty, pierwszy to adres na który wysłane zostało zapytanie, a drugi to funkcja, która ma zostać wywołana w takiej sytuacji.
Pierwszy get, wywoływany jest gdy klient aplikacji wyśle zapytanie do root’a serwera. Funkcja, która wywoływana jest w odpowiedzi otrzymuje jako argumenty dwa obiekty: req oraz res. Pierwszy to request czyli zapytanie otrzymane od klienta a drugi to response, czyli odpowiedzi, która ma zostać przesłana. W odpowiedzi na pierwszy get, używam metody sendFile dostępnej w obiekcie res. Służy ona do wysłania pliku. To właśnie robię i wysyłam do klienta plik index.html.
Kolejny get, służy do odczytu pliku data.json. Wywoływany jest gdy żądanie zostanie wysłane przez klienta na adres /todos. Kiedy to się dzieje, używam wbudowanego do node modułu fs aby pobrać zawartość pliku. Jeżeli to się uda, wysyłam go do klienta przy pomocy metody json obiektu res. Do wysłania mogę używać też metody send, jednak json lepiej obsługuje interesujący mnie format.
Ostatnie żądanie, które obsługuje mój prosty serwer to post, wysłany na adres todo. W takim wypadku, zamieniam otrzymane w żądaniu dane (dostępne w polu body obiektu req, którego odczyt, swoją drogą, wymaga modułu body-parser) na JSON. Następnie doklejam do nich łańcuch znaków, który spowoduje, że dane owinięte zostaną w jeszcze jeden obiekt. Gdy mam już gotowego JSONa, używając modułu fs zapisuję go do pliku.
Na końcu wywołuję metodę listen obiektu app. To ona powoduje, że serwer zaczyna działać. Jako pierwszy argument podaję porta na którym serwer będzie nasłuchiwał. Drugim argumentem jest funkcja wywoływana, gdy serwer już wystartuje. W moim przypadku loguje tylko odpowiednią informację w konsoli.
Tak przygotowany serwer świetnie nada się do obsługi aplikacji napisanej w Vue. Aby jednak wszystko dobrze współgrało, musiałem wprowadzić odpowiednie zmiany w dotychczasowym kodzie. Pierwsze pojawiły się w pliku index.html. Po elemencie input, do którego użytkownik wpisywać może nowe tudusy dodałem następujący kod:
1 2 3 4 |
<br><br> <input v-on:click="submit" type="button" value="Save To Do List"> <br><br> {{todos | json}} |
Jak widać zmian nie ma wiele. Po prostu pojawił się nowy przycisk, który po kliknięciu wywołuje metodę submit. Pod spodem wyświetlam też aktualną zawartość pola todos, znajdującego się w obiekcie data. Używam wbudowanego filtra json, dzięki któremu dane będą przedstawione w czytelny sposób.
Jest jeszcze jedna zmiana, której tu nie będę wklejał. Na końcu elementu body, dodałem nowy elemetn script, który pobiera rozszerzenie Vue – Vue-resources. Robię to ponieważ domyślnie, Vue nie obsługuje wywołań AJAXowych. Potrzebuję vue-resources aby móc z nich korzystać.
W pliku app.js też nie zmieniło się specjalnie wiele. Przede wszystkim pole todos obiektu data, jest zapisane jako pusta tablica. Dane do niej pobierane będą z serwera. Aby to zrobić musiałem wykorzystać metodę Created Vue. Jest to część obiektu Vue, tak jak methods czy el. Funkcja zawarta w Created wykonywana jest raz na początku życia aplikacji, gdy tylko dokument się załaduje. W moim wypadku jej zawartość wygląda tak:
1 2 3 4 5 6 7 8 9 |
created: function(){ this.$http.get('/todos').then(function(response) { console.log(response.body); var data = JSON.parse(response.body); this.todos = data.todos; }, function(response) { console.log("errors!") }); } |
W celu wykonania zapytania get użyć muszę obiektu $http, która dostępna jest dzięki wcześniej wspomnianym vue-resources. Jeżeli komuś wydaje się to znajome to nic dziwnego, w Angularze jest bardzo podobne rozwiązanie 🙂 . $http posiada metody dla każdego rodzaju zapytania. W tym przypadku używam get. Jako argument przyjmuje ona łańcuch znaków, reprezentujący adres do którego chcę się dostać. Następnie wywołuję promise’a then (kod który wykona się asynchronicznie, gdy zapytanie get zostanie zakończone). Promis przyjmuje dwa argumenty, funkcje. Pierwsza wywoływana jest gdy zapytanie się uda, druga gdy serwer odpowie błędem. Obie metody otrzymują w argumencie obiekt response.
W wypadku udanego połączenia, pobieram pole body obiektu response i jego zawartość przypisuję do mojego lokalnego pola todos obiektu data. W wyniku tego kodu, zawsze po załadowaniu aplikacji, pobrany zostanie aktualny stan listy to do z pliku znajdującego się na serwerze.
Teraz pozostał tylko sposób na modyfikacje tej listy. Po stronie klienta nic się nie zmienia. Można edytować listę tak jak dotychczas. Ale aby zapisać jej zmieniony stan na serwerze należy kliknąć przycisk
„Save To Do List”. Wywoła to metodę submit:
1 2 3 4 5 6 7 |
submit: function(){ this.$http.post('/todos', this.todos).then(function(response) { console.log(response.body) }, function(response) { console.log("errors!") }); } |
Metoda submit, jest częścią obiektu methods, tak jak wszystkie inne metody wykorzystywane przez widok aplikacji. W niej też korzystam z obiektu $http, lecz tym razem wywołuję post, który w odróżnieniu do get, przyjmuje też drugi argument, dane, które chcę wysłać do serwera. Następnie za za pomocą promisa loguję w konsoli odpowiednie informacje. Jednak to ma tylko cele testowe, ponieważ sama akcja zapisania danych ma miejsce po stronie serwera.
Uff… I na dziś to wszystko. Jeżeli chcesz być na bieżąco z postami na blogu zachęcam do polubienia mojej strony na facebooku. Zawsze zamieszczam tam informacje o wszystkich nowościach. Jest to też dobre miejsce na kontakt ze mną. Na wszystkie pytania zawsze odpowiem :).