<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="https://unpkg.com/todomvc-app-css@2.0.6/index.css">
<style>
[v-cloak] { display: none; }
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.39/system.js"></script>
<script src="config.js"></script>
<script>
System.import('app').catch(console.log.bind(console));
</script>
</head>
<body>
<div id="app" v-cloak>
<section class="todoapp">
<header class="header">
<h1>todos</h1>
<input class="new-todo" placeholder="What needs to be done?" autofocus v-model="newTodoTitle" @keyup.enter="addTodo">
</header>
<!-- This section should be hidden by default and shown when there are todos -->
<section class="main" v-show="anyTodo" v-cloak>
<input class="toggle-all" type="checkbox" v-model="allDone">
<label for="toggle-all">Mark all as complete</label>
<ul class="todo-list">
<!-- These are here just to show the structure of the list items -->
<!-- List items should get the class `editing` when editing and `completed` when marked as completed -->
<li class="todo" v-for="todo in filteredTodo" :class="{completed: todo.completed, editing: todo.editing}">
<div class="view">
<input class="toggle" type="checkbox" v-model="todo.completed">
<label @dblclick="editTodo(todo)">{{todo.title}}</label>
<button class="destroy" @click="removeTodo(todo)"></button>
</div>
<input class="edit" type="text" v-model="todo.title" v-todo-focus="todo.editing" @blur="doneEdit(todo)" @keyup.enter="doneEdit(todo)" @keyup.esc="cancelEdit(todo)">
</li>
</ul>
</section>
<!-- This footer should hidden by default and shown when there are todos -->
<footer class="footer" v-show="anyTodo" v-cloak>
<!-- This should be `0 items left` by default -->
<span class="todo-count"><strong>{{ remaining }}</strong> {{ remaining | pluralize }} left</span>
<ul class="filters">
<li><a href="#/all" :class="{ selected: visibility == 'all' }">All</a></li>
<li><a href="#/active" :class="{ selected: visibility == 'active' }">Active</a></li>
<li><a href="#/completed" :class="{ selected: visibility == 'completed' }">Completed</a></li>
</ul>
<button class="clear-completed" @click="clearCompleted" v-show="todos.length > remaining">Clear completed</button>
</footer>
</section>
<footer class="info">
<p>Double-click to edit a todo</p>
<p>Created by <a href="https://github.com/budiadiono/vue-typed">Budi Adiono</a></p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer>
</div>
</body>
</html>
#VueTyped - Example - Todo MVC
Example write Todo MVC application using `vue` with `vue-typed`.
System.config({
defaultJSExtensions: true,
transpiler: 'typescript',
typescriptOptions: {
emitDecoratorMetadata: true
},
map: {
'vue': 'https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.3/vue.min.js',
'vue-typed': 'https://cdn.rawgit.com/vue-typed/vue-typed/dev/index.js',
'typescript': 'https://raw.githubusercontent.com/Microsoft/TypeScript/master/lib/typescript.js',
},
packages: {
app: {
main: 'index.ts',
defaultExtension: 'ts',
}
}
});
import { App } from './app';
var app = new App();
// handle routing
function onHashChange () {
app.visibility = window.location.hash.replace(/#\/?/, '') || 'all'
}
window.addEventListener('hashchange', onHashChange)
onHashChange()
app.$mount('#app');
import { Storage } from './storage';
/**
* Todo Data Model
*/
export class Todo {
constructor(title: string) {
this.id = Storage.uid++;
this.title = title;
this.completed = false;
this.editing = false;
}
id: number
title: string
completed: boolean
editing: boolean
}
import { Todo } from './todo';
// localStorage persistence
var STORAGE_KEY = 'todos-vue-typed-vuejs-2.0'
/**
* Storage class
*/
export class Storage {
static uid: number
static fetch() {
var todos = <Todo[]> JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]')
todos.forEach(function (todo, index) {
todo.id = index
})
this.uid = todos.length
return todos
}
static save(todos: Todo[]) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(todos))
}
}
import Vue from 'vue'
import { Component, Watch } from 'vue-typed';
import { Todo } from './todo';
import { Storage } from './storage';
import { Utils } from './utils'
@Component({
directives: {
'todo-focus': function (el, binding) {
var value = binding.value;
if (!value) {
return;
}
Vue.nextTick(function () {
el.focus();
});
}
},
filters: {
pluralize: function (n) {
return n === 1 ? 'item' : 'items'
}
},
})
export class App {
newTodoTitle: string = ''
oldTodoTitle: string = ''
visibility: string = 'all'
todos: Todo[] = Storage.fetch()
@Watch('todos', true)
onTodoChange(todos: Todo[]) {
Storage.save(todos)
}
get filteredTodo() {
return Utils.filter(this.visibility, this.todos);
}
get anyTodo(): boolean {
return this.todos.length > 0;
}
addTodo() {
this.todos.push(new Todo(this.newTodoTitle));
this.newTodoTitle = '';
}
editTodo(todo: Todo) {
this.oldTodoTitle = todo.title;
todo.editing = true;
}
doneEdit(todo: Todo) {
todo.editing = false;
}
cancelEdit(todo: Todo) {
todo.title = this.oldTodoTitle;
todo.editing = false;
}
removeTodo(todo: Todo) {
this.todos.splice(this.todos.indexOf(todo), 1);
}
clearCompleted() {
this.allDone = false;
}
get remaining(): number {
var remaining = this.todos.filter(function (todo) {
return !todo.completed;
});
return remaining.length;
}
get allDone(): boolean {
return this.remaining == 0;
}
set allDone(value: boolean) {
this.todos.forEach(function (todo) {
todo.completed = value;
});
}
}
import { Todo } from './Todo';
/**
* Utility class
*/
export static class Utils {
static filter(state: any, todos: Todo[]): Todo[] {
switch (state) {
case 'active':
return todos.filter(function (todo) {
return !todo.completed
});
case 'completed': {
return todos.filter(function (todo) {
return todo.completed
});
}
}
return todos;
}
}