Рубрики
Без рубрики

Как построить редактируемую в реальном времени таблицу данных в Vue.js

Как построить редактируемую в реальном времени таблицу данных в приложениях Vue.js, управляемых данными. Таблица данных используется для отображения данных в табличном формате с возможностью редактирования и удаления записей на месте.

Питер Мбануго

Введение

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

Многие приложения сегодня имеют функции реального времени, и вам может быть интересно, как можно синхронизировать редактирование и удаление данных в режиме реального времени. Для этого есть три варианта:

  1. Использовать WebSocket API. Это не лучший вариант, если некоторые из ваших пользователей используют браузеры, которые еще не поддерживают WebSocket.
  2. Используйте библиотеку, которая абстрагируется от этих кроссбраузерных различий с помощью резервного механизма, например Socket.IO, SignalR и SockJS. При таком варианте вам придется управлять сервером, обрабатывающим большое количество открытых соединений, и решать вопросы масштабирования.
  3. Используйте сервис, предоставляющий библиотеку, которая делает то же самое, что и предыдущий вариант, но управляет сервером и масштабируется соответствующим образом. Это предпочтительный вариант для компаний и команд, которые принимают (или уже приняли) бессерверный подход.

Я покажу вам, как построить редактируемую в реальном времени таблицу данных в Vue.js, используя Hamoni Sync в качестве сервиса синхронизации состояния в реальном времени. На рисунке ниже показано, что мы будем создавать:

Чтобы следовать этому примеру, вам понадобятся базовые знания о Vue. Если у вас нет знаний о Vue, вы можете прочитать мою предыдущую статью, чтобы освоить Vue.js. Вам также понадобятся следующие инструменты:

  • Node.js и npm (перейдите по ссылке, чтобы загрузить программу установки для вашей ОС).
  • Vue CLI для создания нового проекта Vue. Если у вас его нет, запустите npm install -g vue-cli@2.9.6 из командной строки, чтобы установить его.

Настройка проекта

Мы настроим проект с помощью Vue CLI и шаблона от Vuetify. Откройте командную строку и выполните команду vue init vuetifyjs/simple realtime-datatable-vue. Вам будет предложено ввести имя и автора, поэтому примите значение по умолчанию, нажимая Enter для каждого запроса. Это создаст новый проект Vue с единственным файлом index.html.

Этот файл содержит ссылки на скрипты Vue и Vuetify. Vuetify – это компонент Material Design для Vue.js. В нем есть компонент v-data-table с функциями сортировки, поиска, пагинации, инлайн-редактирования, всплывающих подсказок заголовков и выбора строк.

Добавьте компонент таблицы данных

Откройте файл index.html в текстовом редакторе (или IDE). Замените содержимое строки 50 на следующее:

<div>
  <v-dialog v-model="dialog" max-width="500px">
    <v-btn slot="activator" color="primary" dark class="mb-2">New Item</v-btn>
    <v-card>
      <v-card-title>
        <span class="headline">{{ formTitle }}</span>
      </v-card-title>
      <v-card-text>
        <v-container grid-list-md>
          <v-layout wrap>
            <v-flex xs12 sm6 md4>
              <v-text-field v-model="editedItem.name" label="Dessert name"></v-text-field>
            </v-flex>
            <v-flex xs12 sm6 md4>
              <v-text-field v-model="editedItem.calories" label="Calories"></v-text-field>
            </v-flex>
            <v-flex xs12 sm6 md4>
              <v-text-field v-model="editedItem.fat" label="Fat (g)"></v-text-field>
            </v-flex>
            <v-flex xs12 sm6 md4>
              <v-text-field v-model="editedItem.carbs" label="Carbs (g)"></v-text-field>
            </v-flex>
            <v-flex xs12 sm6 md4>
              <v-text-field v-model="editedItem.protein" label="Protein (g)"></v-text-field>
            </v-flex>
          </v-layout>
        </v-container>
      </v-card-text>
      <v-card-actions>
        <v-spacer></v-spacer>
        <v-btn color="blue darken-1" flat @click.native="close">Cancel</v-btn>
        <v-btn color="blue darken-1" flat @click.native="save">Save</v-btn>
      </v-card-actions>
    </v-card>
  </v-dialog>
  <v-data-table :headers="headers" :items="desserts" hide-actions class="elevation-1">
    <template slot="items" slot-scope="props">
      <td>{{ props.item.name }}</td>
      <td class="text-xs-right">{{ props.item.calories }}</td>
      <td class="text-xs-right">{{ props.item.fat }}</td>
      <td class="text-xs-right">{{ props.item.carbs }}</td>
      <td class="text-xs-right">{{ props.item.protein }}</td>
      <td class="justify-center layout px-0">
        <v-btn icon class="mx-0" @click="editItem(props.item)">
          <v-icon color="teal">edit</v-icon>
        </v-btn>
        <v-btn icon class="mx-0" @click="deleteItem(props.item)">
          <v-icon color="pink">delete</v-icon>
        </v-btn>
      </td>
    </template>
  </v-data-table>
</div>

Приведенный выше код добавляет компонент v-dialog для отображения диалога для сбора данных для новых записей или редактирования существующей записи. Также добавляется компонент v-data-table, который отображает таблицу. Нам необходимо определить данные и методы, используемые этими компонентами. После строки 126 добавьте следующий код в свойства data:

dialog: false,
headers: [
  {
    text: 'Dessert (100g serving)',
    align: 'left',
    sortable: false,
    value: 'name'
  },
  {
    text: 'Calories',
    value: 'calories'
  },
  {
    text: 'Fat (g)',
    value: 'fat'
  },
  {
    text: 'Carbs (g)',
    value: 'carbs'
  },
  {
    text: 'Protein (g)',
    value: 'protein'
  },
  {
    text: 'Actions',
    value: 'name',
    sortable: false
  }
],
desserts: [],
editedIndex: -1,
editedItem: {
  name: '',
  calories: 0,
  fat: 0,
  carbs: 0,
  protein: 0
},
defaultItem: {
  name: '',
  calories: 0,
  fat: 0,
  carbs: 0,
  protein: 0
},
listPrimitive: null

Свойство desserts data будет содержать данные, которые будут отображаться в таблице. Свойство editedItem будет содержать значения для редактируемой записи, а editedIndex – индекс редактируемой записи.

Добавьте следующие свойства после определения свойства data, после строки 189:

computed: {
  formTitle() {
    return this.editedIndex === -1 ? 'New Item' : 'Edit Item'
  }
},
watch: {
  dialog(val) {
    val || this.close()
  }
},

Мы добавили вычисляемое и наблюдаемое свойства. Свойство computed определяет formTitle, которое дает диалоговому компоненту заголовок, основанный на значении editedIndex. Свойство watch следит за диалогом, когда его значение меняется. Если значение изменяется на false, вызывается функция close(), которая будет определена позже.

Добавление Hamoni Sync.

На этом этапе нам необходимо добавить Hamoni Sync. Она используется для синхронизации состояния приложения и разрешения конфликтов, чтобы избежать переопределения данных одного пользователя другим. Чтобы использовать Hamoni Sync, вам необходимо зарегистрировать учетную запись и идентификатор приложения. Выполните следующие шаги, чтобы создать приложение в Hamoni.

  1. Зарегистрируйтесь и войдите в приборную панель Hamoni.
  2. Введите желаемое название приложения в текстовое поле и нажмите кнопку создать. Это создаст приложение и отобразит его в разделе “Список приложений”.
  3. Нажмите кнопку “Показать ID аккаунта”, чтобы увидеть ID вашего аккаунта.

Ниже ссылки сценария на Vuetify в строке 139 добавьте ссылку на Hamoni Sync:

<script src="https://unpkg.com/hamoni-sync@0.4.0/hamoni.dev.js"><;/script>

Затем нам нужно инициализировать Hamoni Sync, как только компонент Vue будет смонтирован. Добавьте свойство mounted ниже свойства watch:

mounted: function () {
  let hamoni = new Hamoni("ACCOUNT_ID", "APP_ID");
  hamoni.connect().then(() => {
    hamoni.get("vue-table").then(primitive => {
      this.listPrimitive = primitive
      this.desserts = [...primitive.getAll()]
      this.subscribeToUpdate()
    }).catch(error => {
      if (error === "Error getting state from server") {
        this.initialise(hamoni);
      }
      else {
        alert(error);
      }
    })
  }).catch(alert)},

В приведенном выше коде мы инициализируем Hamoni Sync с помощью учетной записи и идентификатора приложения. Замените заполнители строк на ID учетной записи и приложения из приборной панели. Затем он подключается к серверу Hamoni вызовом hamoni.connect(), который возвращает обещание.

После подключения мы вызываем hamoni.get() с именем состояния, хранящегося в Hamoni. Для того чтобы получить состояние из Hamoni, оно должно быть создано, иначе будет выдана ошибка. Здесь я обработал эту ошибку в блоке catch, вызвав другую функцию для инициализации состояния в Hamoni Sync.

Если вызов для получения состояния приложения успешен, он возвращает объект, который будет использоваться для изменения данных, содержащихся в этом состоянии. Этот объект называется примитивом Sync. Существует три типа примитивов Sync:

  • Примитив значения: Этот тип состояния хранит простую информацию, представленную такими типами данных, как строка, булевы или числа. Он лучше всего подходит для таких случаев, как подсчет непрочитанных сообщений, переключение и т.д.
  • Объектный примитив: Объектное состояние представляет состояния, которые могут быть смоделированы как объект JavaScript. Примером использования может быть хранение счета в игре.
  • Примитив списка: Здесь хранится список объектов состояния. Объект состояния – это объект JavaScript. Вы можете обновить элемент на основе его индекса в списке.

Для этого примера мы использовали примитив списка. Мы вызываем primitive.getAll(), чтобы получить состояние и передать его десерту. После этого он вызывает функцию subscribeToUpdate(). Эта функция будет использоваться для подписки на события изменения состояния от Hamoni Sync.

Добавьте следующий код после свойства mounted в строке 215:

methods: {
  initialise(hamoni) {
    hamoni.createList("vue-table", [
      { name: 'Frozen Yogurt', calories: 159, fat: 6.0, carbs: 24, protein: 4.0 },
      { name: 'Ice cream sandwich', calories: 237, fat: 9.0, carbs: 37, protein: 4.3 },
      { name: 'Eclair', calories: 262, fat: 16.0, carbs: 23, protein: 6.0 }
    ]).then(primitive => {
      this.listPrimitive = primitive
      this.desserts = this.listPrimitive.getAll()
      this.subscribeToUpdate();
    }).catch(alert)
  },
  subscribeToUpdate() {
    this.listPrimitive.onItemAdded(item => {
      this.desserts.push(item.value)
    })
    this.listPrimitive.onItemUpdated(item => {
      //update the item at item.index
      this.desserts.splice(item.index, 1, item.value);
    })
    this.listPrimitive.onItemDeleted(item => {
      //remove the item at item.index
      this.desserts.splice(item.index, 1);
    })
  },
  editItem(item) {
    this.editedIndex = this.desserts.indexOf(item)
    this.editedItem = Object.assign({}, item)
    this.dialog = true
  },
  deleteItem(item) {
    const index = this.desserts.indexOf(item)
    confirm('Are you sure you want to delete this item?') && this.listPrimitive.delete(index)
  },
  close() {
    this.dialog = false
    setTimeout(() => {
      this.editedItem = Object.assign({}, this.defaultItem)
      this.editedIndex = -1    
    }, 300)
  },
  save() {
    if (this.editedIndex > -1) {
      this.listPrimitive.update(this.editedIndex, this.editedItem)
    }
    else {
      this.listPrimitive.push(this.editedItem)
    }
    this.close()
  }
}

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

Функция initialise() создает примитив списка с именем vue-table.

Функции subscribeToUpdate() содержат код для обработки добавления, обновления или удаления элемента из примитива списка.

Функция deleteItem() удаляет элемент из примитива списка, вызывая listPrimitive.delete(index) с индексом удаляемого элемента.

Функция save() вызывает listPrimitive.push(editedItem) для добавления нового элемента в примитив списка и вызывает listPrimitive.update(editedIndex, editedItem) для обновления записи по определенному индексу.

Это весь код, который необходим для достижения нашей цели – редактируемой в реальном времени таблицы данных. Откройте файл index.html в браузере, и приложение готово к использованию!

Вот и все!

Мы создали редактируемую в реальном времени таблицу данных в Vue.js. Hamoni Sync позволяет легко добавить функциональность в режиме реального времени. И Vuetify, и Hamoni Sync имеют пакеты npm, если вы работаете с системой сборки и используете однофайловые компоненты. Вы можете найти исходный код на GitHub.

Ресурсы

Оригинал: “https://www.freecodecamp.org/news/how-to-build-a-real-time-editable-data-table-in-vue-js-46b7f0b11684/”