Skip to content

Instantly share code, notes, and snippets.

Forked from chrisvfritz/knockout-todos.html
Created February 22, 2018 17:29
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
What would you like to do?
In Vue, we use v-model for all form bindings, while
Knockout maintains separate binding types, such as
textInput, checked, and options. In some cases,
such as for an input of type "range", Knockout
simply doesn't have an equivalent two-way binding
helper and the more verbose value and valueUpdate
must be used.
<div id="app">
<input data-bind="textInput: newTodoText, event: { keypress: addTodo }">
<ul data-bind="foreach: todos">
Inside of the foreach, a new scope is implicitly
created and we must reference $parent or $root to
access other scopes. In contrast, Vue maintains
the root scope and allows you to explicitly name
any nested scopes, so that it's always clear
what data you're accessing.
<span data-bind="text: title"></span>
<button data-bind="event: { click: $root.removeTodo }">
var TodoList = function () {
var self = this
self.todos = ko.observableArray([])
self.newTodoText = ko.observable('')
self.addTodo = function (_, event) {
// Since it's such a common use case, it's worth
// noting that Knockout lacks native keyCode
// modifiers. This means it's necessary to either
// detect the keyCode here, thus coupling addTodo
// to the specific context it's called in, as we
// do below, or go through the tedious process of
// declaring a new bindingHandler.
if (event.keyCode === 13 && self.newTodoText()) {
self.todos.push({ title: self.newTodoText() })
// Since event handlers in Knockout will interfere
// with each other by default, we must explicitly
// return true to avoid breaking the input.
return true
self.removeTodo = function (todo) {
// Instead of making it easy to pass arguments
// to a method, Knockout implicitly passes the
// current scope as the first argument then adds
// a proprietary method to observable arrays so
// that manipulating them is easier.
ko.applyBindings(new TodoList(), document.getElementById('app'))
<div id="app">
Vue uses separate directives for different kinds of
binding, such as v-model for two-way input binding,
v-on for events, and v-for to repeat elements.
<input v-model="newTodoText" v-on:keyup.enter="addTodo">
Vue prefers to explicitly name new variables, rather
than implicitly creating new scopes.
<li v-for="(todo, index) in todos">
{{ todo.title }}
In general, Vue tries its best to act in very
unsurprising ways. One example of this is that
event callbacks in Vue behave exactly like they do
outside of Vue. In Knockout however, it's
surprisingly cumbersome to just pass an argument
to a method.
<button v-on:click="removeTodo(index)">X</button>
new Vue({
el: '#app',
// Instead of wrapping reactive data in observable
// objects, Vue knows what to make reactive by
// having it declared in the `data` option
data: {
todos: []
newTodoText: '',
// Instead of adding everything directly to the
// instance, Vue has specific options to help
// organize your components. Any methods are
// registered under the "methods" options, computed
// properties are registered under "computed", etc.
methods: {
addTodo: function () {
// In Knockout, reactive data acts mostly like
// plain JavaScript objects, except getting
// and setting are done through a function call.
// In Vue, there's no special syntax for getters
// and setters.
if (this.newTodoText) {
this.todos.push({ title: this.newTodoText })
this.newTodoText = ''
removeTodo: function (index) {
this.todos.splice(index, 1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment