Это вторая часть многостраничного руководства, в котором мы создаем 90-дневный планировщик действий с использованием Laravel и Vue.js.
Исходный код части 2 можно найти здесь.
Что мы будем освещать?
Продолжая с того места, на котором мы остановились в части 1, в этом посте мы:
- добавить эффекты перехода при пошаговом переходе; и
- позволяют пользователю добавлять или удалять дополнительные поля показателей и действий.
Давайте запустим наше приложение, запустив yarn watch
или npm run watch
, если вы используете npm из консоли.
Теперь, прежде чем мы перейдем к добавлению эффектов перехода, я хотел бы, чтобы мы быстро рассмотрели две небольшие вещи.
- Название приложения.
Если вы помните, внутри index.blade.php мы устанавливаем заголовок на имя приложения. Вроде так <title>{{ config('app.name') }}</title>
. Теперь по умолчанию это «Laravel». Чтобы изменить это, нам нужно открыть файл среды /.env и вверху изменить APP_NAME=Laravel
на APP_NAME="90 Day Planner"
. Если вы забудете кавычки, будет выдана внутренняя ошибка сервера 500.
Также можно изменить значение по умолчанию на тот случай, если кто-то забудет обновить APP_NAME.
Давайте откроем ./config/app.php и изменим значение по умолчанию с «Laravel» на «90 Day Planner».
'name' => env('APP_NAME', '90 Day Planner
'),
2. Добавьте кнопку «Домой» в многошаговую форму.
Прямо сейчас у пользователя нет возможности вернуться на домашнюю страницу, не нажав несколько раз стрелку назад в браузере. Думаю, мы можем согласиться с тем, что это не лучший пользовательский опыт (UX). Чтобы исправить это, давайте откройте ./resources/assets/js/pages/Create.vue и добавьте новую ссылку на маршрутизатор над <router-view>
следующим образом:
<template> <div> <step-bar :steps="steps"></step-bar> <div class="row align-center"> <div class="column large-8"> <router-link class="mb-5 block" :to="{ name: 'home' }" > <i class="fa fa-home"></i> Home </router-link> <router-view></router-view> </div> </div> </div> </template>
Заметили, что мы представили новый вспомогательный класс .block
. Все это приводит к блокировке отображения CSS. Мы используем это, потому что добавление нижнего поля без отображения, установленного на блок, не имеет никакого эффекта. Мы можем добавить этот новый вспомогательный класс в нижнюю часть нашего файла ./resources/assets/sass/_helpers.scss:
.block { display: block };
Добавление эффекта перехода
Чтобы добавить наш эффект перехода, мы воспользуемся Motion UI и утилитой JavaScript, которую Foundation предоставляет для этого.
Что такое Motion UI?
«Motion UI - это библиотека Sass для создания переходов и анимации CSS…»
- "Источник"
Шаг 1 - Импорт Motion UI Sass
Сначала мы откроем ./resources/assets/sass/app.scss, импортируем интерфейс движения, а затем включим все его переходы:
// Fonts @import url('https://fonts.googleapis.com/css?family=Rubik'); // Settings @import 'settings'; // Import Foundation and Font Awesome @import "node_modules/foundation-sites/scss/foundation"; @import "node_modules/font-awesome/scss/font-awesome"; // Import Motion UI @import "node_modules/motion-ui/src/motion-ui"; // Include everything from foundation and // use flex-grid for our grid system. @include foundation-everything($flex: true); // Include all Motion UI transitions. @include motion-ui-transitions; // Components @import "components/callout"; // Helpers @import "helpers";
Шаг 2 - Инициализируйте фундамент
Чтобы использовать утилиту Foundations JavaScript для пользовательского интерфейса движения, нам сначала нужно инициализировать Foundation.
Есть несколько мест, где мы могли бы инициализировать Foundation. Можно просто создать новый тег <script>
после того, как мы включим app.js в файл index.blade.php, например:
<!-- Compressed JavaScript --> <script src="{{ mix('js/app.js') }}"></script> <script> $(document).foundation(); </script>
Альтернативный подход - инициализировать Foundation внутри app.js сразу после создания нашего экземпляра Vue, например:
const app = new Vue({ router, store, created() { $(document).foundation(); }, el: '#app' });
Оба подхода допустимы и должны работать нормально. Однако я думаю, что лучшим местом для инициализации Foundation будет наш файл bootstrap.js сразу после того, как он нам понадобится. Итак, давайте откроем ./resources/assets/js/bootstrap.js и после того, как нам потребуется Foundation, мы продолжим его инициализацию.
try { window.$ = window.jQuery = require('jquery'); require('foundation-sites'); $(document).foundation(); } catch (e) {}
Шаг 3 - Добавьте эффекты перехода к каждому шагу
Для наших эффектов перехода мы собираемся сделать так, чтобы выноска каждого шага div
сдвигалась слева и сдвигалась вправо при переходе к следующему шагу.
Давайте откроем ./resources/assets/js/pages/Goal.vue в тегах <script>
, позволяющих добавить наши эффекты перехода.
<script> export default { mounted() { Foundation.Motion.animateIn( this.$el, 'slide-in-left fast' ); }, methods: { next() { Foundation.Motion.animateOut( this.$el, 'slide-out-right fast', () => { this.$store.dispatch('goToNextStep') } ); } } } </script>
Что здесь происходит?
- Foundation предоставляет два служебных метода
Foundation.Motion.animateIn()
для переходов, которые входят в окно, иFoundation.Motion.animateOut()
для переходов, которые существуют в окне. Оба метода принимают одинаковое количество параметров в одном и том же порядке. Первый параметр - это элемент, к которому мы хотим добавить переход, второй - встроенный переход, который мы хотим применить, и в-третьих, необязательный обратный вызов, который запускается только после завершения перехода. $el
- это свойство экземпляра нашего компонента, которое ссылается на корневой элемент DOM. В данном случае это наша выноскаdiv
.
Примечание. Мы также можем использовать jQuery для ссылки на наше уточнение
div
. Это можно сделать, присвоив нашему выноскеdiv
идентификатор / класс, а затем сославшись на этот идентификатор / класс. Например, мы можем указать выноскуdiv
и идентификатор «цели»:<div id="goal" class="secondary callout shadow" ref="main">
затем внутри метода перехода мы ссылаемся на идентификатор, используя jQuery
$('#goal')
, например:
Foundation.Motion.animateIn($('#goal'), 'slide-in-left fast');
- После того, как компонент смонтирован, шаблон отображается, и у нас есть доступ ко всем элементам DOM. На этом этапе мы говорим Foundation, что хотим, чтобы наша выноска
div
быстро скользила слева. - Затем мы изменили наш
next()
метод, чтобы сказать, когда нажимается следующая кнопка, быстро сдвиньте выноскуdiv
вправо. После завершения перехода мы переходим к следующему шагу.
Вот полная страница Goal.vue после только что внесенных нами изменений:
Давайте продолжим и добавим переходы в./resources/assets/js/pages/Metrics.vue.
Наконец, для ./resources/assets/js/pages/Actions.vue мы просто добавим входящий переход.
Примечание. Также можно использовать атрибут ref Vues. В случаях, когда вы хотите сослаться на элемент, который не является корневым элементом.
Возможность добавлять / удалять метрики и действия.
В этом разделе мы дадим пользователю возможность добавлять или удалять дополнительные показатели или действия.
Шаг 1 - Создайте возможность добавлять / удалять метрики.
Для начала давайте откроем файл ./resources/assets/js/pages/Metrics.vue. Внутри наших <script>
тегов мы будем определять массив показателей и добавим два новых метода add()
и remove()
.
<script> export default { data() { return { metrics: [{ name: '' }] } }, ... methods: { ... add() { this.metrics.push({ name: '' }); }, remove(index) { if (this.metrics.length > 1) { this.metrics.splice(index, 1) } } } } </script>
Что здесь происходит?
metrics: [{ name: '' }]
: мы определяем массив показателей внутри нашего объекта данных. В этом массиве у нас есть единственный объект метрики со свойством name, которое по умолчанию равно пустой строке.add()
: при срабатывании триггера мы помещаем новый объект метрики в массив метрик.remove()
: мы всегда хотим иметь хотя бы одно поле ввода метрики. Таким образом, мы удаляем метрику только в том случае, если в нашем массиве метрик их несколько. Чтобы фактически удалить метрику из массива, мы используем функциюsplice()
. Первый параметр, который принимаетsplice()
, - это начальная точка, в которой он удаляет элемент из массива. В нашем случае мы используем индекс метрики. Второй параметр - это количество элементов, которые мы хотим удалить. Поскольку мы хотим удалить только саму метрику, мы устанавливаем ее равной единице.
Примечание. Есть некоторые предостережения, когда Vue обнаруживает изменения в массивах. Это описано в документации здесь.
Внутри тегов <template>
давайте заменим форму следующей:
<form> <div class="input-group" v-for="(metric, index) in metrics"> <span class="input-group-label"> <i class="fa fa-line-chart"></i> </span> <input class="input-group-field" type="text" v-model="metric.name" @keydown.enter.prevent="add" placeholder="Metric" v-focus > <div class="input-group-button" v-show="metrics.length > 1"> <button type="button" class="button alert" @click="remove(index)" > <i class="fa fa-minus"></i> </button> </div> </div> <button class="button expanded tiny" type="button" @click="add" title="Add another metric" > <i class="fa fa-plus"></i> </button> <hr> <button class="button expanded" type="submit" @click.prevent="next" > Next </button> </form>
Что здесь происходит?
v-for="(metric, index) in metrics"
: мы создаем новыйinput-group
для каждой метрики и ссылаемся на ее индекс.v-model="metric.name"
: мы используем двустороннюю привязку данных. Это обновит свойство name объекта метрики, когда пользователь вводит имя метрики.v-focus
: это настраиваемая директива. Мы его еще не создали, так что давайте сделаем это сейчас. Сейчас мы добавим это в наш файл resources / assets / js / app.js после того, как нам потребуется Vue.
window.Vue = require('vue'); // Register a global custom directive called v-focus Vue.directive('focus', { inserted: (el) => el.focus() })
- в основном это говорит о том, что как только элемент, содержащий директиву
v-focus
, вставлен в DOM, сосредоточьтесь на нем. - Мы заменили кнопку «Добавить» на кнопку «Удалить», при нажатии которой удаляется соответствующая метрика.
v-show="metrics.length > 1"
: показывать кнопку удаления только тогда, когда в массиве более одной метрики.- Мы также добавили новую кнопку «Добавить» после нашего
input-group
. При нажатии отображается новое поле ввода.
Шаг 2 - Создайте возможность «Добавить / удалить действия».
С точки зрения функциональности добавление и удаление действий будет работать точно так же, как и для метрик. Однако мы внесем некоторые заметные изменения в форму на нашей странице действий. Сначала давайте откроем ./resources/assets/js/pages/Actions.vue и в наших <tempalte>
тегах мы обновим form
следующим образом:
<form @submit.prevent="save"> <div class="row" v-for="(action, index) in actions"> <div class="medium-6 small-12 columns"> <div class="input-group"> <span class="input-group-label"> <i class="fa fa-list"></i> </span> <input class="input-group-field" type="text" placeholder="Action..." v-model="action.name" v-focus > </div> </div> <div class="medium-6 small-12 columns"> <div class="input-group"> <span class="input-group-label"> <i class="fa fa-user"></i> </span> <input class="input-group-field" type="text" placeholder="Who will perform the action?" v-model="action.person_responsible" > <div class="input-group-button"> <button type="button" class="button alert" title="Remove action" v-show="actions.length > 1" @click="remove(index)" > <i class="fa fa-minus"></i> </button> </div> </div> </div> </div> <button type="button" class="button expanded tiny warning" title="Add another action" @click="add" > <i class="fa fa-plus"></i> </button> <hr> <button class="button expanded" type="submit">Done</button> </form>
Мы не вводили здесь никаких новых концепций, поэтому давайте быстро рассмотрим визуальные изменения, которые мы внесли в нашу форму.
Какие изменения были внесены?
- Теперь у нас есть
row
, который содержит дваcolumns
, каждый из которых содержит поле ввода. Для маленьких экранов эти поля отображаются одно за другимsmall-12
. Для средних дисплеев и выше мы показываем их бок о бокmedium-6
. - Было введено новое текстовое поле, позволяющее пользователю указать, кто отвечает за выполнение действия.
- Затем мы удалили кнопку «Добавить» из
input-group
и заменили ее кнопкой «Удалить». - Наконец, как и в нашей форме показателей, мы добавили новую кнопку «Добавить», но на этот раз после
row
.
Давайте продолжим и добавим в массив действий и создадим методы добавления и удаления, вот так:
<script> export default { data() { return { actions: [{ name: '', person_responsible: '' }] } }, mounted() { Foundation.Motion.animateIn(this.$el, 'slide-in-left fast'); }, methods: { save() { alert('Saving...'); }, add() { this.actions.push({ name: '', person_responsible: ''}); }, remove(index) { if (this.actions.length > 1) { this.actions.splice(index, 1) } } } } </script>
После того, как все скомпилировано, у нас должна получиться такая форма:
Вот полный файл Action.vue с внесенными нами изменениями:
На этом давайте подведем итоги и рассмотрим только что внесенные изменения. Сначала мы добавили эффект перехода при переходе от шага к шагу. Мы также дали пользователю возможность добавлять или удалять столько показателей или действий, сколько они сочтут нужным.
На данный момент у нас действительно есть некоторые общие функции между формами, и, в зависимости от того, как идет работа, может быть стоит извлечь их в миксин.
В следующем посте мы рассмотрим создание нового общего состояния для отслеживания цели, показателей и действий. Мы также можем посмотреть, как мы можем использовать локальное хранилище в случае, если пользователь теряет соединение с Интернетом или по ошибке обновляет страницу.
До следующего раза, удачного кодирования и спасибо за чтение!