Недавно начал изучать 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