Praca z aplikacją pisaną w Vue sprawia mi naprawdę dużą przyjemność. Jednak powoli zaczyna mi brakować pomysłów na to jak bardziej ją rozwijać (a zarazem uczyć się nowych rzeczy o bibliotece.) Trudno najwyżej stworzę nową, bardziej oryginalną apkę 😉
Tym razem udało mi się jeszcze dodać do mojej Todo Listy coś konkretnego – wartości obliczane (ang. computed values). Są one bardzo przydatnym elemenemt Vue. Szczerze mówiąc nie przychodzi mi do głowy podobne rozwiązanie z innych frameworków, które można by porównać do wartości oblicznaych.
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. Tam można pobrać i pobawić się aplikacją na własną rękę.
Wartości obliczane to specjalne funkcje, które dodaję do modelu widoku. Można przypisywać je do elementów vue tak jakby były normalnymi zmiennymi. Vue wykorzystywać będzie zwracane wartości. Co najlepsze, wartości takie odświeżane są w czasie rzeczywistym. Jest to bardzo przydatne, co zresztą postaram się zilustrować na przykładzie mojej aplikacji „Todo”.
Postanowiłem dodać do modelu moich zadań dodatkową cechę. Teraz każde zadanie może mieć albo wysoki albo niski priorytet. Chcę aby w widoku aktywnych zadań była możliwość filtrowania pozycji po ich ważności.
Pierwszą rzeczą, która wymagała zmiany, były elementy widoku odpowiadające za dodawanie nowych zadań:
1 2 3 4 5 6 |
<input v-model="currTodo" value=""> <select v-model="newImportance"> <option value="high">high</option> <option value="low">low</option> </select> <input v-on:click="addTodo" :disabled="currTodo.length === 0" type="button" value="Add"> |
Dodałem element select, który powiązałem z wartością modelu widoku newImportance. Element ten ma w sobie dwie opcje high oraz low.
Aby wszystko działało jak należy musiałem zmienić też metodę addTodo:
1 2 3 4 5 6 7 8 9 |
addTodo: function(){ var tempImportance = this.newImportance if(this.newImportance === "" || !this.newImportance) { tempImportance = "low" } this.todos.push({name:this.currTodo,editing:false,complete:false,importance:tempImportance}); this.currTodo = ""; this.newImportance = ""; }, |
przypisuję wartość newImportance do tymczasowej zmiennej. Jeżeli po tej operacji tymczasowa zmienna nie ma wartość, otrzymuje wartość low. Następnie tworzę nowy obiekt todo z dodatkowym polem i wpycham go do tablicy todos. Na koniec czyszczę oba powiązane z widokiem pola:
Teraz kiedy już mam gotowy model dla obiektów todo. Mogę przejść do budowy filtra. Aby działał jak należy, wykorzystam wartości obliczane.
Najpierw muszę jednak zmienić widoku aktywnych zadań. Dodam kolejny element select, który wykorzystam do filtrowania pozycji na liście. To co będzie wybrane na selekcie, wyznaczy co będzie widział użytkownik. Jest jednak mały myk. Chcę aby opcje selekta generowały się dynamicznie. To znaczy, jeżeli na liście będą tylko pozycje o wysokim priorytecie, selekt będzie miał tylko jedną opcję: high. korzyści z takiego płynących jest kilka, najważniejsza jest taka, że jeżeli dojdzie nowa opcja priorytetu np. średnio ważny, mój selekt sam to złapie.
Pierwszym krokiem będzie stworzenie wartości obliczanej, potrzebuję jej do wyłuskania unikatowych ważności na aktualnej liście zadań.
Wartości obliczane dodaję do pola computed. Jest ono jednym z głównych pól obiektu Vue, tak jak data czy created. Pole to zawiera obiekt, które pola z klei to tworzone przez programiste obliczane wartości. Oto jak tworzę swoją wartość uniqueImportance:
1 2 3 4 5 6 7 8 9 10 11 |
computed: { uniqueImportance:function(){ importanceArray = []; for (var i = 0; i < this.todos.length; i++) { if(importanceArray.indexOf(this.todos[i].importance) === -1){ importanceArray.push(this.todos[i].importance); } } return importanceArray; } }, |
Moja wartość obliczana nosi nazwę uniqueImportance i jak widać jest to po prostu funkcja, która zwraca tablicę niepowtarzających się ważności z aktualnej listy todos.
Teraz mogę zmienić widok:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<p>Active:</p> Importance: <select v-model="selector"> <option v-for="todoImportance in uniqueImportance" value={{todoImportance}}>{{todoImportance}}</option> </select> <ul> <li v-for="todo in todos | filterBy false in 'complete' | filterBy selector in 'importance'"> <input v-if="!todo.editing" type="checkbox" v-on:change="changeCheck(todo)" checked={{todo.complete}}> <span v-if="!todo.editing">{{todo.name | capitalize}}</span> <input v-if="todo.editing" v-model="todo.name"> <input v-if="!todo.editing" v-on:click="edit(todo)" type="button" value="edit"> <input v-if="todo.editing" v-on:click="save(todo)" type="button" value="save"> <input v-if="!todo.editing" v-on:click="remove(todo)" type="button" value="remove"> </li> </ul> |
Tworzę nowy element select, którego wartość powiązuję ze zmienną selector. Natomiast opcje dla tego selekta generuję za pomocą mojej wartości obliczanej. Dla każdego elementu tablicy zwracanej przez todoImportance, powstaje nowa opcja o odpowiedniej wartości. Ponieważ jest to odświeżane w czasie rzeczywistym, nowe opcje mogą pojawiać się wraz z nowymi zadaniami w liście zadań.
Teraz wystarczy tylko dodać odpowiedni filtr do listy. Wystarczy dopisać ten filtr:
1 |
filterBy selector in 'importance' |
Dzięki temu, pokażą się tylko te zadania, które w polu importance, mają aktualną wartość zmiennej selector, czyli to co akurat jest wybrane w elemencie select powyżej.
To mało skomplikowane rozwiązanie jest naprawdę przydatne i daje programiście sporo możliwości. Dobrze wykorzystane wartości obliczane sprawiają, że proste aplikację dają wrażenie naprawdę zaawansowanych i potężnych.
To wszystko na dziś ale jeśli 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 :).