20: Filter Todos
Show filtering todos.
- Add CSS to hide filtered todo item in <style>.
- Add visibility property into x-state.
- Add changeVisibility invocation to controller element by x-invocations directive.
- Set dataset to <ul> to hide by x-bind:data-filter directive.
- Add click event listener to root <ul> of filter links by x-on:click directive. It calls changeVisibility invocation.
- Set x-bind:class directive to filter links for show selected design.
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/todomvc-app-css@2.1.2/index.css" > <style> [data-filter="active"] > .completed { display: none; } [data-filter="completed"] > :not(.completed) { display: none; } </style><!DOCTYPE html>
<html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,initial-scale=1" ><body> <section class="todoapp" x-state="{ todos: [], editingTodoIndex: -1, visibility: 'all', }"<script src="https://x-ninja.org/assets/js/o_o.js"></script>
<script src="https://x-ninja.org/assets/js/flux._.js"></script> <title>TODOs @x-ninja</title> </head>clearCompleted (_, payload, meta) { this.todos = this.todos.filter(todo => !todo.completed) }, changeVisibility (_, payload, meta) { const hash =meta._.$._.attr('href') if (!hash) { return } this.visibility = hash.substring(2) }, }"x-invocations="{
addTodo (_, payload, meta) { const newTodo = payload.newTodo.trim() if (!newTodo) { return } this.todos.push({ title: newTodo, completed: false }) }, editTodo (_, payload, meta) { const todo = this.todos[payload.index] if (!todo) { return } this.beforeEditCache = todo.title this.editingTodoIndex = payload.index }, focusTodo (_, payload, meta) { const inputs = _.$.querySelectorAll('input.edit') const target = inputs[payload.index] if (!target) { return } target.focus() target.setSelectionRange( target.value.length, target.value.length ) }, doneEdit (_, payload, meta) { const todo = this.todos[payload.index] if (!todo) { return } if (this.editingTodoIndex === -1) { return } this.editingTodoIndex = -1 const newTitle = meta._.$.value.trim() if (newTitle) { todo.title = newTitle } else { _.functions.destroyTodoByIndex(this.todos, payload.index) } }, cancelEdit (_, payload, meta) { this.editingTodoIndex = -1 }, destroyTodo (_, payload, meta) { this.todos.splice(payload.index, 1) }, toggleAllTodos (_, payload, meta) { this.todos.forEach( todo => todo.completed = meta._.$.checked ) },x-functions="{
destroyTodoByIndex (todos, index) { todos.splice(index, 1) } }"x-computed="{
get remaining () { return this.todos.filter(todo => !todo.completed).length } }"::>x-filters="{
pluralize (count, word) { return word + (count === 1 ? '' : 's') }, }"<section class="main"><header class="header">
<h1>todos</h1> <input class="new-todo" autofocus autocomplete="off" placeholder="What needs to be done?" @change="addTodo({newTodo: launcher$.value})" x-callbacks="{ change (_) { _.$.value = '' } }" > </header><ul class="todo-list" x-each="todo, index in todos" :data-filter="this.visibility" ><input id="toggle-all" class="toggle-all" type="checkbox">
<label for="toggle-all">Mark all as complete</label></ul> </section> <footer class="footer"><li class="todo"
:class="{ completed: todo.completed, editing: index === editingTodoIndex, }" > <div class="view"> <input class="toggle" type="checkbox" x-model="todo.completed" > <label @dblclick="editTodo({index}), focusTodo({index})">{{ todo.title }}</label> <button class="destroy" @click="destroyTodo({index})"></button> </div> <input class="edit" :value="todo.title" @change="doneEdit({index})" @keyup.esc="cancelEdit" > </li><ul class="filters" @click.prevent="changeVisibility" > <li><a href="#/all" class="selected":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><span class="todo-count">
<strong x-text="remaining">3</strong> {{ remaining |> pluralize('item') }} left </span></footer> </section> </body> </html><button class="clear-completed"
@click="clearCompleted" x-show="todos.length > remaining" > Clear completed </button>