Недавно начал изучать Vue.js
Для обкатки знаний поставил себе следующую задачу:
Реализовать todo-list (план дел на ближайшее время) со следующими простыми возможностями:
1) Добавление новой задачи
2) Удаление задачи
3) Отметка задачи как выполненной/не выполненной
4) Синхронизация состояния задач с удалённым хранилищем (чтобы после перезагрузки страницы данные не пропадали)
Получилось что-то вроде этого:
https://web-finder.ru/files/todo-checklist/
Особенности реализации читайте под катом.
Собственно начнём с вёрстки, но с учетом использования Vue.
Нам понадобится 2 шаблона:
- шаблон блока с задачами (task-list)
- шаблон задачи (task-item)
Собственно index.html:
<div id="app"> <task-list :tasks="tasks"></task-list> </div> <template id="task-list"> <section class="tasks"> <div class="tasks__new input-group"> <input type="text" placeholder="Название задачи" class="input-group-field" v-model="newTask" @keyup.enter ="addTask"> <span class="input-group-button"> <button class="button" @click="addTask"> <i class="fa fa-plus"></i> Добавить </button> </span> </div> <ul class="tasks__list no-bullet"> <task-item v-for="(task, index) in tasks" @remove="removeTask(index)" @complete="completeTask(task)" :task="task" :key ="task.id"></task-item> </ul> </section> </template> <template id="task-item"> <li class="tasks__item"> <button :class="className" @click.self="$emit('complete')"> {{ task.title }} </button> <button class="tasks__item__remove button alert pull-right" @click="$emit('remove')"> <i class="fa fa-times"></i> </button> </li> </template>
JS код же из себя представляет 2 компонента Vue:
- один для работы с записью (task-item)
- второй для работы с списком записей (task-list)
Кроме того нам понадобится сам экземпляр Vue ( см. переменную app)
Vue.component('task-list', { template: '#task-list', props: { tasks: {default: []} }, data() { return { newTask: '' }; }, methods: { addTask() { if (this.newTask) { this.tasks.push({ title: this.newTask, completed: false }); this.newTask = ''; } }, completeTask(task) { task.completed = ! task.completed; }, removeTask(index) { this.tasks.splice(index, 1); } } }); Vue.component('task-item', { template: '#task-item', props: ['task'], computed: { className() { let classes = ['tasks__item__toggle']; if (this.task.completed) { classes.push('tasks__item__toggle--completed'); } return classes.join(' '); } } }); let app = new Vue({ el: '#app', data: { tasks: [] }, flag_rewrite: false, watch: { tasks: { handler: function (newVal) { if (this.flag_rewrite){ axios({ method: 'post', url: '/files/todo-checklist/ajax.php', data: { action: 'set-storage', 'data-storage': JSON.stringify(newVal) } }) .then(function (response) { if (response.data == 'error'){ alert('Ошибка: не удалось сохранить данные'); } }) .catch(function (){ alert('Ошибка: не удалось сохранить данные'); }) } if (!this.flag_rewrite) this.flag_rewrite = true; }, deep: true } } }); document.addEventListener('DOMContentLoaded', function(){ axios({ method: 'post', url: '/files/todo-checklist/ajax.php', data: { action: 'get-storage' } }) .then(function (response) { data = response.data; if (data !== 'error'){ app.tasks = data; } else { alert('Ошибка: не удалось загрузить данные'); } }) .catch (function (){ alert('Ошибка: не удалось загрузить данные'); }) });
Компонент task-item управляет наличием класса tasks__item__toggle—completed, который перечеркивает задачу, если она выполнена.
Компонент task-list реализует методы на добавление задачи, удаление и установку флага «выполнено»
Сохранение состояния задач производится за счёт обработчика метода-наблюдателя (см. watch в экземпляре приложения Vue — app)
Опция deep установлена в true для того, чтобы слежение реагировало на изменения во вложенных объектах — в данном случае поля задачи completed.
Собственно в момент загрузки DOM (см. document.addEventListener(‘DOMContentLoaded’, function(){…} ) производится post обращение к скрипту /files/todo-checklist/ajax.php через action = ‘get-storage’
Он в свою очередь отдаёт нам все задачи в формате JSON и загружает их в экземпляр Vue.
ajax.php:
<? header('Content-Type: application/json'); $data = 'error'; $post = json_decode(file_get_contents('php://input'), true); if ($post['action'] == 'get-storage'){ $data = file_get_contents('storage.storage'); } if ($post['action'] == 'set-storage'){ $data_storage = json_decode($post['data-storage']); if ($data_storage!==false && $data_storage!==NULL){ if (file_put_contents('storage.storage', $post['data-storage'])){ $data = 'success'; } } } echo $data; ?>
В момент именения задачи в методе-наблюдателе объекта Vue производится обращение к ajax.php но уже с action = ‘set-storage’, который сохраняет json представление задач в файле storage.storage