KnockoutJS bindings and observables
KnockoutJS it’s the JavaScript implementation of the MVVM pattern that is based on native JavaScript event management. Implementing knockoutJS both as a standalone solution or in conjunction with other web technologies can greatly improve user experience in our web applications.
As a example lets create simple list of places and restaurants as below:
In our example, places can have multiple related restaurants. To make programming more funny lets create two separate view models for our entities. In order to differentiate two models we need to use overloaded applyBindings methods passing in form elements.
var PVM = new PlacesViewModel(); var RVM = new RestaurantViewModel() ko.applyBindings(PVM, document.getElementById('placesModel')); ko.applyBindings(RVM, document.getElementById('restaurantModel'));
Lets define html form for places and it’s template:
<div id="placesModel" style="float: left; width: 350px;"> <h2> Places</h2> <div data-bind="foreach: places"> <div style="background-color: #e5dfdf;" data-bind="template: { name: 'place-template', data: $data }"> </div> </div> <br /> <br /> <hr /> <form data-bind="submit: addPlace"> Name: <input name="name" /><br /> <br /> Details: <textarea rows="4" name="details" cols="20"></textarea> <button type="submit"> add place</button> </form> </div> //template for places <script type="text/html" id="place-template"> <h3 style="color: green;"><b data-bind="visible: !editing(), text: name, click: $root.edit"> </b> <input data-bind="visible: editing, value: name, hasfocus: editing" /> </h3> <p><em>Click the name above to edit</em></p> <p>Place details: <span data-bind="text: details"></span></p> <span data-bind="text: $data.name"></span> <a href="#" data-bind="click: $root.removePlace">Remove</a> <h4><span data-bind="text: name"></span> restaurants</h4> <ul data-bind="foreach: RVM.restaurants"> <!--ko if: $data.place.toLowerCase().indexOf($parent.name().toLowerCase()) >= 0 --> <li style=""> <span data-bind="text: $data.name"></span> <a href="#" data-bind="click: RVM.removeRestaurant">Remove</a> </li> <!--/ko--> </ul> </script>
At this point we need to define knockout view model and define functions for data binding and observables:
function PlacesViewModel() { var self = this; self.places = ko.observableArray([ { name: ko.observable('London'), details: '....', editing: ko.observable(false) }, ]); self.selectedPlace = ko.observable({ name: ko.observable('London'), details: '....', editing: ko.observable(false) }); self.removePlace = function (place) { self.places.remove(place) } self.addPlace = function (placeForm) { //basic validation if (placeForm.name.value.trim().length == 0) { alert('Type the place name!'); return; } var place = { name: ko.observable(placeForm.name.value), details: placeForm.details.value, editing: ko.observable(false) } self.places.push(place); placeForm.name.value = ''; placeForm.details.value = ''; //clear form } self.edit = function (arg) { arg.editing(true) } }
Now, lets create simple restaurant form and controls:
<div id="restaurantModel" style="float: left; padding-left: 30px;"> <h2> Restaurant list</h2> <ul data-bind="foreach: restaurants"> <li style=""><span data-bind="text: $data.name"></span> (<i><span data-bind="text: $data.place"></span></i>) <a href="#" data-bind="click: $parent.removeRestaurant">Remove</a> </li> </ul> <br /> <br /> <hr /> <form data-bind="submit: addRestaurant"> Name: <input name="name" /><br /> <br /> Place: <select data-bind="options: PVM.places, optionsValue:'name', optionsText: 'name'" name="place"> </select> <br /> <br /> <button type="submit"> add restaurant</button> </form> </div>
At the end we need implement basic restaurants model functions
function RestaurantViewModel() { var self = this; self.restaurants = ko.observableArray([{ name: 'Restaurant 1', place: 'London' }, { name: 'Restaurant 2', place: 'Tokyo' }, { name: 'Restaurant 3', place: 'Paris'}]); self.removeRestaurant = function (restaurant) { self.restaurants.remove(restaurant) } self.addRestaurant = function (restaurantForm) { //basic validation if (restaurantForm.name.value.trim().length == 0) { alert('Type the restaurant name!'); return; } var restaurant = { name: restaurantForm.name.value, place: restaurantForm.place.value }; self.restaurants.push(restaurant); restaurantForm.name.value = ''; //clear form } }
I have included sample project to be downloaded below. Please use it to check how binding is working when adding, editing and deleting objects on the page.
knockoutjs