In this post, I am going to show you how to develop a to-do app using knockoutjs and Microsoft Web API.
What is knockoutjs
”Knockout is a standalone JavaScript implementation of the Model-View-ViewModel pattern with templates. The underlying principles are, therefore: a clear separation between domain data, view components and data to be displayed.”-Wikipedia
Knockout is built around three core features:
- Observables and dependency tracking
- Declarative bindings
- Templating
Before diving into example code first go through the following http://knockoutjs.com/documentation/observables.html#mvvm_and_view_models
Open visual studio and create a new Web API application
Open Package Manager console and install knockoutjs as shown in the below image
Right-click the Model folder and add a new class named TodoItem and paste the following code.
public class TodoItem
{
public int Id { get; set; }
public string Title { get; set; }
public bool Done { get; set; }
}
Right-click the model folder again and add a new Interface class and paste the following code
public interface ITodoRepository
{
IEnumerable<TodoItem> GetAll();
void AddTodo(TodoItem item);
}
}
Add a new class inside the model folder and paste the following code inside it
public class TodRepository : ITodoRepository
{
private static List<TodoItem> _localTodos;
private int MaxId()
{
return _localTodos.Max(x => x.Id) + 1;
}
public TodRepository()
{
_localTodos = new List<TodoItem>() {
new TodoItem{Id=1,Title="Todo Item 1",Done=false},
new TodoItem{Id=2,Title="Todo Item 2",Done=false},
new TodoItem{Id=3,Title="Todo Item 3",Done=true},
};
}
public IEnumerable<TodoItem> GetAll()
{
return _localTodos;
}
public void AddTodo(TodoItem item)
{
var id = MaxId();
TodoItem newTodo = new TodoItem() { Id = id, Title = item.Title, Done = item.Done };
_localTodos.Add(newTodo);
}
public void Delete(int id)
{
var taskToDeleted = _localTodos.Find(x => x.Id == id);
_localTodos.Remove(taskToDeleted);
}
}
Go to the controller folder and rename the ValuesController to TodoController and paste the following code.
public class TodoController : ApiController
{
// GET api/values
private ITodoRepository _repo;
public TodoController()
{
_repo = new TodRepository();
}
public IEnumerable<TodoItem> Get()
{
return _repo.GetAll();
}
public void Post([FromBody]TodoItem value)
{
_repo.AddTodo(value);
}
}
Till now I have not done anything related to knockoutjs. Let’s start the second part
Open Index.cshtml and remove everything from it and paste the following markup
<h1>Todo App</h1>
<div class="row">
<form role="form">
<div class="form-group">
<label for="email">Title:</label>
<input type="text" class="form-control" data-bind="value: taskName" id=" txttitle" placeholder="Enter Title">
</div>
<button type="submit" data-bind="click:addTask" class="btn btn-default">Submit</button>
</form>
<hr />
<div class="col-md-6">
<table class="table">
<thead>
<tr>
<th>Title</th>
<th>Completed?</th>
</tr>
</thead>
<tbody data-bind="foreach:todos">
<tr>
<td data-bind="text:Title"></td>
<td><input type="checkbox" data-bind="checked:Done" /> </td>
<td>
<a href="#" title="Delete the task" data-bind="click:$parent.deleteTodo" class="glyphicon glyphicon-trash"></a>
</td>
</tr>
</tbody>
</table>
</div>
</div>
The basic layout of the HTML is simple and taken from the inspiring example applications. The noteworthy parts are the data-bind attributes that are sprinkled in the appropriate places:
There is an input textbox that has the same value as the view models taskName and there is a button that will fire the addTask function on ViewModel.
Right-click the project and add a new javascript file inside the scripts folder named todoApp.js and add the following code
function TodoItem(Id, Title, Done) {
var self = this;
self.Id = Id;
self.Title = ko.observable(Title);
self.Done = ko.observable(Done);
self.Done.subscribe(function () {
});
}
function TodoViewModel() {
var self = this;
self.todos = ko.observableArray([]);
self.taskName = ko.observable();
self.addTask = function () {
var todoItem = new TodoItem(0, self.taskName(), false);
self.todos.push(todoItem);
$.ajax({
type: "POST",
url: "/api/todo",
data: ko.toJSON(todoItem),
dataType: "json",
contentType: "application/json",
success: function (data) {
}
});
};
self.deleteTodo = function (data) {
self.todos.remove(data);
$.ajax({
type: "DELETE",
url: "/api/todo/" + data.Id,
dataType: "json",
contentType: "application/json",
success: function () {
}
});
};
self.init = function () {
$.getJSON("/api/todo", function (data) {
$.each(data, function (index, item) {
self.todos.push(new TodoItem(item.Id, item.Title, item.Done));
});
});
};
}
$(document).ready(function () {
var viewModel = new TodoViewModel();
viewModel.init();
ko.applyBindings(viewModel);
});
open _Layout.cshtml and add the following script
<script src="~/Scripts/knockout-3.2.0.js"></script>
<script src="~/Scripts/todoApp.js"></script>