KnockoutJS – CRUD App, podsumowanie

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('');
}

Tak wygląda okienko modal:

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 🙂
 

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

*