<!DOCTYPE html>
<html>
<head>
<script data-require="jquery@2.1.3" data-semver="2.1.3" src="http://code.jquery.com/jquery-2.1.3.min.js"></script>
<link data-require="bootstrap@*" data-semver="3.3.2" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" />
<script data-require="bootstrap@*" data-semver="3.3.2" src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/knockout/3.3.0/knockout-min.js"></script>
<script type="text/javascript" src="//rawgit.com/rniemeyer/knockout-sortable/master/build/knockout-sortable.js"></script>
<link href="style.css" rel="stylesheet" />
</head>
<body>
<div class="container">
<h1>Knockout.js Task List with Search and Filter, Random Tasks, In-place Editing, Sorting</h1>
<a href="http://plnkr.co/ZmUclh?p=info">View the source on plunker</a>
<hr/>
<!-- New Task Form -->
<div id="new-task" class="row">
<div class="col-lg-12">
<h2>Add New Task</h2>
<form action="" data-bind="submit: addTask">
<div class="input-group">
<input type="text" class="form-control" placeholder="Task Name" data-bind="value: addTaskForm.taskName">
<span class="input-group-btn">
<button class="btn btn-primary" type="submit">Add Task</button>
</span>
</div>
</form>
</div>
</div>
<!-- Random Tasks Form -->
<div id="random-tasks" class="row">
<div class="col-lg-12">
<h2>Add Random Task(s)</h2>
<form action="" data-bind="submit: addRandomTasks">
<div class="input-group">
<input type="text" class="form-control" placeholder="N Tasks" data-bind="value: randomTaskForm.numberOfTasks">
<span class="input-group-btn">
<button class="btn btn-primary" type="submit">Generate Tasks</button>
</span>
</div>
</form>
</div>
</div>
<!-- Filter Tasks Form -->
<div id="filter-tasks" class="row">
<div class="col-lg-12">
<h2>Existing Tasks (<span data-bind="text: tasks().length"></span>)</h2>
<form action="" data-bind="submit: function() { return false; }">
<div class="form-group">
<input type="text" class="form-control" placeholder="Type to Filter Tasks" data-bind="value: filterTaskForm.taskName, valueUpdate: 'afterkeydown'">
</div>
</form>
</div>
</div>
<!-- Tips and Sorting -->
<div class="row">
<div class="col-lg-6">
<p>Click the task name to edit it.</p>
</div>
<div class="col-lg-6">
<form action="" class="form-inline">
<label for="sort-options">Sort Tasks</label>
<select id="sort-options" class="form-control" data-bind="options: sortTaskForm.order, value: sortTaskForm.selectedOrder"></select>
</form>
</div>
</div>
<!-- Existing Task List -->
<div id="existing-tasks" class="row">
<div class="col-lg-12" id="existing-tasks">
<ul data-bind="sortable: tasks">
<li class="task" data-bind="css: { complete: isComplete() }, visible: isFiltered()">
<div class="row">
<div class="col-lg-6">
<!-- Task name -->
<span class="task-name" data-bind="text: $data.name, visible: !$data.isBeingEdited(), click: $data.edit"></span>
<!-- Edit task form -->
<form action="" data-bind="visible: $data.isBeingEdited(), submit: $data.edit">
<div class="input-group">
<input type="text" class="form-control" data-bind="value: name, hasFocus: $data.isBeingEdited()">
<span class="input-group-btn">
<button class="btn btn-success" type="submit">Save</button>
</span>
</div>
</form>
</div>
<!-- Task buttons (complete, delete, up, down) -->
<div class="col-lg-6">
<div class="pull-right">
<span class="glyphicon glyphicon-ok" aria-hidden="true" data-bind="click: $data.complete"></span>
<span class="glyphicon glyphicon-remove" aria-hidden="true" data-bind="click: $parent.deleteTask"></span>
<span class="glyphicon glyphicon-arrow-up" aria-hidden="true" data-bind="click: $parent.upTask"></span>
<span class="glyphicon glyphicon-arrow-down" aria-hidden="true" data-bind="click: $parent.downTask"></span>
</div>
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
<script src="RandomTasks.js"></script>
<script src="script.js"></script>
</body>
</html>
function ViewModel() {
var self = this;
/**
* Public variables and functions
*/
this.tasks = ko.observableArray();
this.addTaskForm = {
taskName: ko.observable()
}
this.filterTaskForm = {
taskName: ko.observable()
}
this.randomTaskForm = {
numberOfTasks: ko.observable()
}
this.sortTaskForm = {
selectedOrder: ko.observable(),
order: ko.observableArray([
'By Name (A - Z)',
'By Name (Z - A)'
])
}
this.addTask = addTask;
this.deleteTask = deleteTask;
this.upTask = upTask;
this.downTask = downTask;
this.addRandomTasks = addRandomTasks;
/**
* KO Bindings
*/
this.filterTaskForm.taskName.subscribe(function(newValue) {
filterTasks(newValue);
});
this.sortTaskForm.selectedOrder.subscribe(function(newValue) {
sortTasks(newValue);
})
/**
* Function definitions
*/
function addTask(item, event) {
if (self.addTaskForm.taskName().trim()) {
self.tasks.push(new Task(self.addTaskForm.taskName()));
}
}
function deleteTask(item, event) {
self.tasks.remove(item);
}
function upTask(item, event) {
var index = self.tasks.indexOf(item);
if (index === 0) return;
self.tasks.splice(index - 1, 2, self.tasks()[index], self.tasks()[index - 1]);
}
function downTask(item, event) {
var index = self.tasks.indexOf(item);
if (index === self.tasks().length - 1) return;
self.tasks.splice(index, 2, self.tasks()[index + 1], self.tasks()[index]);
}
function addRandomTasks() {
var n = self.randomTaskForm.numberOfTasks(),
newTasks = [];
for (var i = 0; i < n; i++) {
newTasks.push(new Task(RandomTasks.generate()));
}
self.tasks(self.tasks().concat(newTasks));
}
function filterTasks(filter) {
filter = filter.toLowerCase();
self.tasks().forEach(function(task) {
if (task.name().toLowerCase().indexOf(filter) > -1) {
task.isFiltered(true);
} else {
task.isFiltered(false);
}
});
}
function sortTasks(order) {
var sorted;
sorted = self.tasks();
if(order === "By Name (A - Z)") {
sorted.sort(function sortAsc(a,b) {
if(a.name() > b.name()) return 1;
if(a.name() < b.name()) return -1;
return 0;
});
} else if(order === 'By Name (Z - A)') {
sorted.sort(function sortDesc(a,b) {
if(a.name() > b.name()) return -1;
if(a.name() < b.name()) return 1;
return 0;
});
}
self.tasks(sorted);
}
}
function Task(name) {
this.name = ko.observable(name);
this.isComplete = ko.observable(false);
this.isFiltered = ko.observable(true);
this.isBeingEdited = ko.observable(false);
this.complete = complete;
this.edit = edit;
function complete() {
this.isComplete(!this.isComplete());
}
function edit() {
this.isBeingEdited(!this.isBeingEdited())
}
}
/**
* Initialize the view model and generate some random tasks
*/
var vm = new ViewModel();
ko.applyBindings(vm);
for (var i = 0; i < 5; i++) {
vm.tasks.push(new Task(RandomTasks.generate()));
}
/* Styles go here */
body {
font-family: arial;
}
#existing-tasks ul {
list-style-type: none;
padding: 0px;
}
#existing-tasks li.task {
background-color: LightCyan;
padding: 10px;
margin: 4px;
border: 1px solid lightblue;
border-radius: 4px;
font-weight: bold;
}
#existing-tasks li.complete {
background-color: lightgreen;
border-color: green;
}
#existing-tasks li.task .glyphicon {
cursor: pointer;
}
.task-name {
cursor: pointer;
}
A simple task list with completion, deleting, and up/down buttons using Knockout.js.
var RandomTasks = {
verbs: ["be","have","do","say","get","make","go","know","take","see","come","think","look","want","give","use","find","tell","ask","work","seem","feel","try","leave","call"],
nouns: ["time","person","year","way","day","thing","man","world","life","hand","part","toy","eye","pet","place","work","week","case","point","government","company","number","group","problem","fact"],
generate: generate
}
function generate(n) {
var vlen = this.verbs.length,
nlen = this.nouns.length,
vrand = Math.floor((Math.random() * vlen)),
nrand = Math.floor((Math.random() * nlen));
return [this.verbs[vrand], 'the', this.nouns[nrand]].join(" ");
}