Создание списка задач с Vue.js

Разработка /
Разработка: Создание списка задач с Vue.js

Сегодня мы создадим приложение — список задач с Vue.js, а также рассмотрим и другие удобные инструменты для создания современных веб-приложений.

Убедитесь, что у вас установлена Vue CLI! Если нет, то установите её командой
$ npm install --global vue-cli


С Vue CLI идут несколько шаблонов готовых приложений:

webpack - Полнофункциональная установка Webpack + Vue-loader с hot reload, linting, testing & CSS extraction.

webpack-simple - Упрощённая установка Webpack + Vue-loader.

browserify - Полнофункциональная установка Browserify + vueify с hot-reload, linting & unit testing.

browserify-simple - Упрощённая установка Browserify + vueify.

simple - Простенькая установка Vue всего в одном файле HTML

Создадим приложение:

$ vue init webpack todo-app

Вас спросят название для проекта, описание, автора и сборку Vue. Мы будем работать не устанавливая Vue-router.

Установим зависимости:

$ cd my-project
$ npm install

Для запуска приложения выполните команду:

$ npm run dev

Откроется браузер на странице localhost:8080

Разработка: Создание списка задач с Vue.js

Для стилизации приложения будем использовать Semantic.

Структура компонента

Каждое приложение Vue имеет компонент верхнего уровня, у нас таким компонентом будет TodoList.

У нас уже есть готовый компонент Hello (сгенерированный Vue CLI) в папке src/App.vue, осталось создать недостающие компоненты нижнего уровня.
Создадим простенький компонент TodoList.vue:

<template>
  <div>
    <ul>
        <li> Todo A </li> 
        <li> Todo B </li> 
        <li> Todo C </li> 
    </ul> 
  </div>
</template>

<script type = "text/javascript" >

export default {
};
</script>
<style>
</style>

Это обычная заготовка, которую мы заполним позднее.

Импортируем наш компонент в главный экземпляр Vue, чтобы можно было с ним работать. Откройте файл src/App.vue и отредактируйте его:

<script>
import TodoList from './components/TodoList';

export default {
  components: {
    // добавим ссылку на компонент TodoList
    TodoList,
  },
};
</script>

Для обработки компонента необходимо его включить в HTML:

<template>
  <div>
    // Обработка компонента TodoList
    // он будет здесь:
    <todo-list></todo-list>
  </div>
</template>

Теперь наше приложение выглядит так:

Разработка: Создание списка задач с Vue.js

Мы должны передавать данные главному компоненту для вывода списка задач. Задачи будут иметь три свойства: Название, Проект и Выполнено.

Добавим данных в наш компонент:

export default {
  name: 'app',
  components: {
    TodoList,
  },
  // data function avails data to the template
  data() {
    return {
      todos: [{
        title: 'Todo A',
        project: 'Project A',
        done: false,
      }, {
        title: 'Todo B',
        project: 'Project B',
        done: true,
      }, {
        title: 'Todo C',
        project: 'Project C',
        done: false,
      }, {
        title: 'Todo D',
        project: 'Project D',
        done: false,
      }],
    };
  },
};

Нам нужно передать данные из главного компонента в TodoList и для этого мы будем использовать директиву v-bind. Она принимает аргумент (он отделяется двоеточием от имени директивы), в нашем случае это будет todos; это показывает директиве v-bind связать элемент todos с тем, что идёт в нём.

<todo-list v-bind:todos="todos"></todo-list>

Теперь нужно доработать компонент TodoList для доступа к этим данным. Добавим свойство к классу компонента:

export default {  
    props: ['todos'],
}


Обработка данных

Пройдёмся в цикле по списку задач в шаблоне TodoList и выведем количество завершённых и незавершённых задач:

<template>
  <div>
    // JavaScript expressions in Vue are enclosed in double curly brackets.
    <p>Completed Tasks: {{todos.filter(todo => {return todo.done === true}).length}}</p>
    <p>Pending Tasks: {{todos.filter(todo => {return todo.done === false}).length}}</p>
    <div class='ui centered card' v-for="todo in todos">
      <div class='content'>
        <div class='header'>
          {{ todo.title }}
        </div>
        <div class='meta'>
          {{ todo.project }}
        </div>
        <div class='extra content'>
          <span class='right floated edit icon'>
            <i class='edit icon'></i>
          </span>
        </div>
      </div>
      <div class='ui bottom attached green basic button' v-show="todo.done">
        Completed
      </div>
      <div class='ui bottom attached red basic button' v-show="!todo.done">
        Complete
      </div>
  </div>
</template>

<script type = "text/javascript" >

export default {
  props: ['todos'],
};
</script>


Редактирование задач

Разделим шаблон задачи для улучшения читабельности кода. Создайте новый компонент Todo.vue в папке src/components и перенесите в него шаблон задачи:

<template>
  <div class='ui centered card'>
    <div class='content'>
        <div class='header'>
            {{ todo.title }}
        </div>
        <div class='meta'>
            {{ todo.project }}
        </div>
        <div class='extra content'>
            <span class='right floated edit icon'>
            <i class='edit icon'></i>
          </span>
        </div>
    </div>
    <div class='ui bottom attached green basic button' v-show="todo.done">
        Completed
    </div>
    <div class='ui bottom attached red basic button' v-show="!todo.done">
        Complete
    </div>
</div>
</template>

<script type="text/javascript">
  export default {
    props: ['todo'],
  };
</script>

Переработаем компонент TodoList для работы с компонентом Todo:

<template>
  <div>
    <p>Completed Tasks: {{todos.filter(todo => {return todo.done === true}).length}}</p>
    <p>Pending Tasks: {{todos.filter(todo => {return todo.done === false}).length}}</p>
   // we are now passing the data to the todo component to render the todo list
    <todo  v-for="todo in todos" :todo.sync="todo"></todo>
  </div>
</template>

<script type = "text/javascript" >

import Todo from './Todo';

export default {
  props: ['todos'],
  components: {
    Todo,
  },
};
</script>

Добавим свойство isEditing в класс компонента Todo, это свойство покажет редактируется ли в данный момент задача или нет. Повесим обработчик события на элемент Edit и будем показывать форму редактирования, при этом изменим значение свойства isEditing в true. Осталось добавить форму и установить начальное значение свойства. Наш шаблон теперь выглядит так:

<template>
  <div class='ui centered card'>
    // Todo shown when we are not in editing mode.
    <div class="content" v-show="!isEditing">
      <div class='header'>
          {{ todo.title }}
      </div>
      <div class='meta'>
          {{ todo.project }}
      </div>
      <div class='extra content'>
          <span class='right floated edit icon' v-on:click="showForm">
          <i class='edit icon'></i>
        </span>
      </div>
    </div>
    // form is visible when we are in editing mode
    <div class="content" v-show="isEditing">
      <div class='ui form'>
        <div class='field'>
          <label>Title</label>
          <input type='text' v-model="todo.title" >
        </div>
        <div class='field'>
          <label>Project</label>
          <input type='text' v-model="todo.project" >
        </div>
        <div class='ui two button attached buttons'>
          <button class='ui basic blue button' v-on:click="hideForm">
            Close X
          </button>
        </div>
      </div>
    </div>
    <div class='ui bottom attached green basic button' v-show="!isEditing &&todo.done" disabled>
        Completed
    </div>
    <div class='ui bottom attached red basic button' v-show="!isEditing && !todo.done">
        Pending
    </div>
  </div>
</template>
</template>

У нас есть метод showForm, открывающий форму правки, теперь добавим метод hideForm для её закрытия при нажатии на кнопку отмены:

<script>
export default {
  props: ['todo'],
  data() {
    return {
      isEditing: false,
    };
  },
  methods: {
    showForm() {
      this.isEditing = true;
    },
    hideForm() {
      this.isEditing = false;
    },
  },
};
</script>

Данные в форме уже привязаны к списку задач и их редактирование автоматически сохраняет обновлённые данные. По нажатию кнопки Close увидим обновлённую задачу:

Разработка: Создание списка задач с Vue.js

Удаление задачи

Добавим иконку удаления задачи:

<template>
    <span class='right floated edit icon' v-on:click="showForm">
      <i class='edit icon'></i>
    </span>
    /* add the trash icon in below the edit icon in the template */
    <span class='right floated trash icon' v-on:click="deleteTodo(todo)">
      <i class='trash icon'></i>
    </span>
</template>

Добавим метод в класс компонента, вызываемый по событию delete-todo, и передающий компоненту текущую задачу к удалению.

<span class='right floated trash icon' v-on:click="deleteTodo(todo)">

// Todo component
methods: {
    deleteTodo(todo) {
      this.$emit('delete-todo', todo);
    },
  },

Теперь нужно добавить обработчик события удаления в родительский компонент (TodoList):

// TodoList component
methods: {
    deleteTodo(todo) {
      const todoIndex = this.todos.indexOf(todo);
      this.todos.splice(todoIndex, 1);
    },
  },

Передаём метод deleteTodo в компонент Todo:

// шаблон TodoList 
<todo  v-on:remove-todo="deleteTodo" v-for="todo in todos" :todo.sync="todo"></todo>


Добавление новой задачи

Для создания новой задачи сделаем новый компонент CreateTodo в папке src/components. Он выведет кнопку со знаком плюс, она при нажатии откроет форму добавления задачи:

<template>
  <div class='ui basic content center aligned segment'>
    <button class='ui basic button icon' v-on:click="openForm" v-show="!isCreating">
      <i class='plus icon'></i>
    </button>
    <div class='ui centered card' v-show="isCreating">
      <div class='content'>
        <div class='ui form'>
          <div class='field'>
            <label>Title</label>
            <input v-model="titleText" type='text' ref='title' defaultValue="">
          </div>
          <div class='field'>
            <label>Project</label>
            <input type='text' ref='project' defaultValue="">
          </div>
          <div class='ui two button attached buttons'>
            <button class='ui basic blue button' v-on:click="sendForm()">
              Create
            </button>
            <button class='ui basic red button' v-on:click="closeForm">
              Cancel
            </button>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      titleText: '',
      projectText: '',
      isCreating: false,
    };
  },
  methods: {
    openForm() {
      this.isCreating = true;
    },
    closeForm() {
      this.isCreating = false;
    },
    sendForm() {
      if (this.titleText.length > 0 && this.projectText.length > 0) {
        const title = this.titleText;
        const project = this.projectText;
        this.$emit('create-todo', {
          title,
          project,
          done: false,
        });
        this.newTodoText = '';
      }
      this.isCreating = false;
    },
  },
};
</script>

Добавим новый компонент в главный компонент:

// главный компонент в App.vue
components: {
    TodoList,
    CreateTodo,
  },

А также добавим метод для создания новой задачи:

// в App.vue
methods: {
    addTodo(title) {
      this.todos.push({
        title,
        done: false,
      });
    },
  },

Вызов компонента CreateTodo из шаблона App.vue:

<create-form v-on:add-todo="addTodo">

Разработка: Создание списка задач с Vue.js

Выполнение задачи

Осталось добавить метод для установки отметки о выполнении задачи.

// компонент Todo 
methods: {
      completeTodo(todo) {
        this.$emit('complete-todo', todo);
      },
}

Обработчик события будет добавлен в TodoList:

methods: {
    completeTodo(todo) {
      const todoIndex = this.todos.indexOf(todo);
      this.todos[todoIndex].done = true;
    },
  },

И добавим вызов метода в шаблоне:

<todo v-on:delete-todo="deleteTodo" v-on:complete-todo="completeTodo" v-for="todo in todos" :todo.sync="todo"></todo>


Готово! Ниже по ссылкам можно увидеть полный код приложения или пощупать демонстрационную версию.

Полный код примера. Демо-версия.

По материалам «Build a To-Do App with Vue.js 2» by Jeremy Kithome
0 комментариев
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.