Create a gist now

Instantly share code, notes, and snippets.

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.
-->
<li>
<span data-bind="text: title"></span>
<button data-bind="event: { click: $root.removeTodo }">
X
</button>
</li>
</ul>
</div>
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() })
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.
self.todos.remove(todo)
}
}
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">
<ul>
<!--
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>
</li>
</ul>
</div>
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)
}
}
})
Owner

chrisvfritz commented Sep 4, 2016

For anyone who'd like to comment here, please note that GitHub does not trigger notifications for Gist comments, so I probably won't see it! If you notice an error, you should instead submit an issue to the Vue docs repo.

onlyurei commented Oct 4, 2016

With some small customizations, such as opt-in the hidden gem https://github.com/nathanboktae/knockout-es5-option4, Knockout's approach, in my opinion can be as unopinionated as it can be. Here's my attempt for example: https://github.com/onlyurei/todomvc-knockout-spa

JunCCore commented Apr 1, 2017

nice

The data object in the Vue js example has a missing comma. Looks like you switched the order of data.todos and data.newTodoText, but forgot to change the position of the comma.

First!

NOT!

BMeyerKC commented Oct 20, 2017

I feel like this comment,

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.

is unfair, knockout does have a way of maintaining scope, and will use it frequently,

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment