<!-- | |
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) | |
} | |
} | |
}) |
This comment has been minimized.
This comment has been minimized.
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 |
This comment has been minimized.
This comment has been minimized.
nice |
This comment has been minimized.
This comment has been minimized.
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. |
This comment has been minimized.
This comment has been minimized.
First! NOT! |
This comment has been minimized.
This comment has been minimized.
I feel like this comment,
is unfair, knockout does have a way of maintaining scope, and will use it frequently, |
This comment has been minimized.
This comment has been minimized.
Nice! |
This comment has been minimized.
This comment has been minimized.
As I'm new to Vue and a rather old hand at Knockout this was highly useful to me. Thanks so much for putting this together! |
This comment has been minimized.
This comment has been minimized.
There should be a coma between todos:[] and netTodoText |
This comment has been minimized.
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.