Tak o to dotrwaliśmy do końca samouczka Knockout.js. Żeby sprawdzić wiedzę teoretyczną postanowiłem napisać prostą aplikację która ma możliwość generowanie listy z kilkoma opcjami, takimi jak: dodawanie nowego elementu do listy, edycja, usuwanie, obejrzenie konkretnego wiersza. Dorzuciłem jeszcze proste stronicowanie. Do napisania apki użyłem HTML, CSS(bootstrap), JavaScript i oczywiście KnockoutJS. Kod aplikacji można zobaczyć na githubie tutaj.
Aplikacja składa się z 4 plików, index.html, modal.js, personViewModel.js oraz style.css. W pliku index.html znajduję się cały szablon HTML z bindowaniami Knockouta, w style.css znajdują się reguły do tworzenia okienka w którym edytujemy, dodajemy, podglądamy wiersz, plik modal.js odpowiedzialny jest za to co plik style.css operuję oknem, czyści input jeżeli jest taka potrzeba, oraz na koniec personViewModel.js oczywiście kod odpowiedzialny za operacje Knockout’a.
Poniżej znajduję się szkielet tabeli w HTML z bindowaniami Knockouta.js
... <table class="table table-striped"> <thead> <tr> <th>id</th> <th>Name</th> <th>Number</th> <th>Remove</th> <th>Edit</th> <th>Details</th> </tr> </thead> <tbody data-bind="foreach: paginated"> <tr> <th> <span data-bind="text: id"></span> </th> <th> <span data-bind="text: name"></span> </th> <th> <span data-bind="text: number"></span> </th> <th> <a href="#" data-bind="click: $parent.removePerson">Remove</a> </th> <th> <a href="#" id="myBtnEdit" data-bind="click: $parent.editPerson.bind($data)">Edit</a> </th> <th> <a href="#" id="myBtnDetails" data-bind="click: $parent.detailsPerson.bind($data)">Details</a> </th> </tr> </tbody> </table> ...
Oraz kod Knockout.js który inicjalizuje zmienne i generuję tabele, w pętli można zmieniać ile elementów listy chcemy wygenerować, mógłbym zrobić pole w którym użytkownik sam mógłby ustalać jak dużą chce mieć listę, ale pisząc aplikację głównie chciałem skupić się na mechanizmach Knockout’a a niżeli na cudach bajerach.
... self.personArray = ko.observableArray([]); ... self.loadData = function(){ for(let i = 0; i < 400; i++) { self.personArray.push({id: ko.observable(i), name: ko.observable("name00"+i), number: ko.observable(i) }); } } self.loadData(); ...
Stronicowanie(paginacja) za każdym razem gdy klikniemy w element stronicowana uruchomi się odpowiednia funkcja odpowiedzialna za przejście na kolejną stronę. W moim wypadku wystarczy aktualizować zmienna self.pageNumber()
<div style="text-align: center;"> <ul class="pagination"> <li class="page-item"><a class="page-link" data-bind="click: $root.firstPage" href="#">First</a></li> <li class="page-item"><a class="page-link" data-bind="click: $root.previous, visible: $root.hasPrevious" href="#">Previous</a></li> <li data-bind="foreach: nbPaginationOnSite" class="page-item"> <a class="page-link" href="#" data-bind="text: numberr, click: $root.currentPage"></a> </li> <li class="page-item"><a class="page-link" href="#" data-bind="click: $root.next, visible: $root.hasNext">Next</a></li> <li class="page-item"><a class="page-link" href="#" data-bind="click: $root.lastPage">Last</a></li> </ul> </div>
Zmienna self.nbPagination jest odpowiedzialna za ilość wierszy paginacji wyświetlanych na stronie, oraz analogicznie self.nbPerPage który wyświetla ilość wierszy na stronie.
//PAGNIG self.nbPagination = 3; self.nbPaginationOnSite = ko.observableArray([]); self.pageNumber = ko.observable(0); self.nbPerPage = 10; self.totalPages = ko.computed(function() { let div = Math.floor(self.personArray().length / self.nbPerPage); div += self.personArray().length % self.nbPerPage > 0 ? 1 : 0; return div - 1; }); self.paginated = ko.computed(function() { let first = self.pageNumber() * self.nbPerPage; return self.personArray.slice(first, first + self.nbPerPage); }); self.hasPrevious = ko.computed(function() { return self.pageNumber() !== 0; }); self.hasNext = ko.computed(function() { return self.pageNumber() !== self.totalPages(); }); self.next = function() { if (self.pageNumber() < self.totalPages()) { self.pageNumber(self.pageNumber() + 1); } }; self.previous = function() { if (self.pageNumber() != 0) { self.pageNumber(self.pageNumber() - 1); } }; self.PageCount = ko.computed(function() { return Math.ceil(self.totalPages() / self.nbPerPage); }); self.firstPage = function() { self.pageNumber(0); }; self.lastPage = function() { self.pageNumber(self.totalPages()); }; self.displayFewNumbers = ko.computed(function() { self.nbPaginationOnSite([]); for (let i = 0; i < self.nbPagination; i++) { let temp = new Object(); temp.numberr = parseInt(self.pageNumber()) + parseInt(i); self.nbPaginationOnSite.push(temp); } }); self.currentPage = function(nb) { if (nb.numberr > self.pageNumber()) { self.next(); } else { self.previous(); } }; //END PAGING
Przedstawiam metody odpowiedzialne za CRUD – Create, Read, Update, Delete.
//CRUD self.addPerson = function(user) { let lastId = self.personArray()[self.personArray().length - 1].id(); self.personArray.push({ id: ko.observable(lastId + 1), name: ko.observable(user.perName()), number: ko.observable(user.perNumber()) }); modalStyleDisplay(modal); resetAllValues('myModal'); }; self.removePerson = function() { self.personArray.remove(this); }; self.editPerson = function(user) { self.personName(user.name()); self.personNumber(user.number()); btnEdit.onclick(); self.editUserData = user; }; self.saveEditPerson = function(saveEditUser) { for (let i = 0; i < self.personArray().length; i++) { if (self.personArray()[i].name == self.editUserData.name && self.personArray()[i].number == self.editUserData.number) { let newObj = new Object(); self.personArray()[i].id(self.editUserData.id()); self.personArray()[i].name(saveEditUser.personName()); self.personArray()[i].number(saveEditUser.personNumber()); break; } } self.editUserData = ""; modalStyleDisplay(modalEdit); resetAllValues('myModalEdit'); }; self.detailsPerson = function(user) { self.personName(user.name()); self.personNumber(user.number()); btnDetails.onclick(); }; self.closeDetailsPerson = function() { spanDetails.onclick(); }; //END CRUD
Teraz pliczek modal.js kod który odpowiedzialny jest za wyświetlanie okienka danej akcji:
//ADD USER // Get the modal var modal = document.getElementById('myModal'); // Get the button that opens the modal var btn = document.getElementById("myBtn"); // Get the <span> element that closes the modal var span = document.getElementsByClassName("close")[0]; // When the user clicks the button, open the modal btn.onclick = function() { modal.style.display = "block"; resetAllValues('myModal'); } // When the user clicks on <span> (x), close the modal span.onclick = function() { modalStyleDisplay(modal); resetAllValues('myModal'); } //EDIT USER var modalEdit = document.getElementById('myModalEdit'); var btnEdit = document.getElementById("myBtnEdit"); var spanEdit = document.getElementsByClassName("closeEdit")[0]; btnEdit.onclick = function() { modalEdit.style.display = "block"; } spanEdit.onclick = function() { console.log("Xfg") modalStyleDisplay(modalEdit); resetAllValues('myModalEdit'); } //DETAILS USER var modalDetails = document.getElementById('myModalDetails'); var btnDetails = document.getElementById("myBtnDetails"); var spanDetails = document.getElementsByClassName("closeDetails")[0]; btnDetails.onclick = function() { modalDetails.style.display = "block"; } spanDetails.onclick = function() { modalStyleDisplay(modalDetails); } function modalStyleDisplay(mod) { mod.style.display = "none"; } function resetAllValues(idName) { $('#' + idName).find('input:text').val(''); }
To wszystko w tej serii 🙂 mam nadzieję, że pomoże to osobą które zaczynają, bądź już miały doświadczenie z Knockout.js. Jeżeli dostrzegliście jakiekolwiek błędy lub macie pytania, śmiało piszcie postaram się odpowiedzieć na wszystkie 🙂
Pozdro 🙂