[Перевод] Веб-приложение на Node и Vue, часть 4: повторное использование кода

Перед вами четвёртая часть серии материалов, которые посвящены разработке веб-приложения Budget Manager с использованием Node.js, Vue.js и MongoDB. В первой, второй и третьей частях речь шла о создании основных серверных и клиентских компонентов приложения. Сегодня мы продолжим развитие проекта, а именно — займёмся списками документов и клиентов. Кроме того, нельзя не заметить, что к настоящему моменту сделано уже немало, поэтому вполне можно критически взглянуть на то, что получилось, и поработать над повторным использованием кода.

jtx5pcu7ber0bpd1j01lt9akcpo.jpeg



Совершенствование компонентов


Начнём с изменения имени папки Budget на List. Кроме того, переименуем три компонента, которые находятся в этой папке. А именно, BudgetList теперь будет называться List, BudgetListHeader получит название ListHeader, а BudgetListBody — ListBody. В итоге папка и файлы компонентов должны выглядеть так, как показано на рисунке ниже.

980fc7ac5a0025c6da8d74e7f3b8c9ac.png


Откроем файл компонента List и приведём его к такому виду:




Тут мы изменили имя класса таким образом, чтобы оно соответствовало имени компонента. Кроме того, мы поменяли имена слотов.

Откроем файл компонента ListHeader и внесём в него следующие изменения:






Здесь, опять же, мы поменяли имена классов, а так же отредактировали шаблон, настроив его на вывод данных из свойств (props). Так мы сможем повторно использовать этот компонент на других страницах.

Теперь пришёл черёд компонента ListBody:






Этот компонент мы тоже подготовили к повторному использованию, задействовав вывод данных из свойств.

Теперь откроем файл компонента Home и отредактируем его:






Здесь мы поменяли команды импорта и теги, привели их в соответствие переименованным компонентам, добавили snackbar, что даёт возможность показывать сообщения об ошибках в том случае, если нам не удастся получить данные. Кроме того, мы добавили сюда новый массив для данных компонента, budgetHeaders, а также данные, необходимые для snackbar.

Мы будем использовать budgetHearders для показа заголовков списка. Кроме того, мы внесли некоторые изменения в метод getAllBudgets:

getAllBudgets () {
  Axios.get(`${BudgetManagerAPI}/api/v1/budget`, {
    headers: { 'Authorization': Authentication.getAuthenticationHeader(this) },
    params: { user_id: this.$cookie.get('user_id') }
  }).then(({data}) => {
    this.budgets = this.dataParser(data, '_id', 'client', 'title', 'state')
  }).catch(error => {
    this.snackbar = true
    this.message = error.message
  })
},


Теперь, вместо того, чтобы передавать данные документов напрямую, мы используем новый метод:

dataParser (targetedArray, ...options) {
  let parsedData = []
  targetedArray.forEach(item => {
    let parsedItem = {}
    options.forEach(option => (parsedItem[option] = item[option]))
    parsedData.push(parsedItem)
  })
  return parsedData
}


Этот метод, в качестве первого аргумента, принимает массив и произвольное число аргументов, которые сформируют массив options с использованием оператора расширения.

Метод будет брать каждый элемент из массива, в данном случае это — документы, и создавать новый объект parsedItem.

Этот объект будет содержать данные массива options, после завершения его подготовки он будет помещён в массив parsedData, который мы возвращаем из этого метода.

И, наконец, мы перехватываем ошибки (если таковые возникнут), активируя snackbar.

Теперь нужно отредактировать код маршрутизатора, для этого перейдём в папку router и откроем index.js:

import Vue from 'vue'
import Router from 'vue-router'
import * as Auth from '@/components/pages/Authentication'

// Pages
import Home from '@/components/pages/Home'
import Authentication from '@/components/pages/Authentication/Authentication'

// Global components
import Header from '@/components/Header'
import List from '@/components/List/List'

// Register components
Vue.component('app-header', Header)
Vue.component('list', List)

Vue.use(Router)

const router = new Router({
  routes: [
    {
      path: '/',
      name: 'Home',
      components: {
        default: Home,
        header: Header,
        list: List
      }
    },
    {
      path: '/login',
      name: 'Authentication',
      component: Authentication
    }
  ]
})

router.beforeEach((to, from, next) => {
  if (to.path !== '/login') {
    if (Auth.default.user.authenticated) {
      next()
    } else {
      router.push('/login')
    }
  } else {
    next()
  }
})

export default router


Тут мы поправили команды импорта, имена компонентов, теги и пути, а также сделали некоторые улучшения в router.beforeEach, так как мы собираемся защищать любой маршрут, отличающийся от login, мы убираем meta из маршрута страницы Home.

Вывод информации о клиентах


Вместо того, чтобы создавать новую страницу, предназначенную для вывода списка клиентов, мы будем использовать уже существующую страницу Home. Поэтому вернёмся к компоненту Home и создадим новый массив в данных компонента, дав ему имя clients, а также создадим массив clientHeaders и логическую переменную budgetsVisible:

return {
  budgets: [],
  clients: [],
  budgetHeaders: ['Client', 'Title', 'Status', 'Actions'],
  clientHeaders: ['Client', 'Email', 'Phone', 'Actions'],
  budgetsVisible: true,
  snackbar: false,
  timeout: 6000,
  message: ''
}


Теперь добавим новый метод:

getAllClients () {
  Axios.get(`${BudgetManagerAPI}/api/v1/client`, {
    headers: { 'Authorization': Authentication.getAuthenticationHeader(this) },
    params: { user_id: this.$cookie.get('user_id') }
  }).then(({data}) => {
    this.clients = this.dataParser(data, '_id', 'client', 'email', 'phone')
  }).catch(error => {
    this.snackbar = true
    this.message = error.message
  })
},


Вызовем этот метод при монтировании компонента:

mounted () {
  this.getAllBudgets()
  this.getAllClients()
},


Как теперь вывести сведения о клиентах? Очень просто. Достаточно внести ещё некоторые изменения в компонент Home:






Теперь мы передаём переменную budgetVisible в Header и, кроме того, используем эту переменную в тернарном операторе сравнения для вывода нужных данных. В Header так же попадает переменная toggleVisibleData, где мы инвертируем значение budgetsVisible. Причина, по которой мы передаём в Header свойства, заключается в том, что благодаря такому подходу мы можем сделать ещё некоторые улучшения, о которых поговорим ниже. Кроме того, в слотах list-header и list-body мы используем тернарные операторы сравнения.

Итак, теперь внесём улучшения в Header:






Теперь цвет заголовка будет зависеть от состояния переменной budgetsVisible. Если документы видимы, заголовок будет иметь светло-синий цвет, если нет — зелёный.

Кроме того, цвет и надпись на кнопке будут меняться в зависимости от значения budgetVisible, по её щелчку вызывается обработчик соответствующего события, меняющий состояние логической переменной.

Кроме того, мы внесли некоторые изменения в scss.

И, наконец, займёмся компонентом ListBody:






Изменения, внесённые сюда, похожи на те, что мы выполнили в коде компонента Header.

Промежуточные результаты


Вот как теперь выглядит список документов:

833081b1ce8507334df3978656575c09.png

А вот — список клиентов:

bfd09420816c94530807421a2b0e824a.png

Теперь, когда мы можем видеть списки зарегистрированных документов и клиентов, создадим плавающую кнопку (Floating Action Button, FAB), которая будет содержать кнопки, позволяющие работать со списком. Всё ещё находясь в коде компонента Home, добавим следующий код ниже v-snackbar:


  
      
            add
            close
      

      
        
              assignment
        
        Add new Budget
      

      
        
              account_circle
        
        Add new Client
      

  


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

data () {
  return {
    budgets: [],
    clients: [],
    budgetHeaders: ['Client', 'Title', 'Status', 'Actions'],
    clientHeaders: ['Client', 'Email', 'Phone', 'Actions'],
    budgetsVisible: true,
    snackbar: false,
    timeout: 6000,
    message: '',
    fab: false
  }
},


Здесь мы добавили логическое значение fab, которое используется для указания того, активна плавающая кнопка или нет.

Итоги


Сегодня мы внесли некоторые улучшения в компоненты, переработали их с прицелом на повторное использование кода, добавили функционал вывода списка клиентов. Полный вариант приложения, как обычно, можно найти в репозитории проекта.

В следующем материале мы продолжим работу над приложением, и, вероятнее всего, её завершим.

Уважаемые читатели! Стремитесь ли вы к возможности повторного использования кода при работе над своими проектами?

© Habrahabr.ru