Создание диаграммы в реальном времени с помощью Vue.js

Создание диаграммы в реальном времени с помощью Vue.js

От автора: в последнее время данные стали очень важной частью нашей жизни, и понимание того, что данные одинаково важны. Нет смысла иметь данные, если вы не можете отслеживать или анализировать их, особенно если эти данные имеют какое-либо отношение к финансам.

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

На диаграмме приборной панели будет работать Node.js + Express в качестве внутреннего сервера и Vue + vue-chartjs для интерфейса, загруженного vue-cli.

Создание приложения с помощью vue-cli

vue-cli — это простой CLI для создания проектов Vue.js. Мы установим vue-cli, а затем используем его для загрузки приложения с помощью шаблона webpack со следующими командами:

Фреймворк VUE JS: быстрый старт, первые результаты

Получите бесплатный курс и создайте веб-приложение на трендовой Frontend-технологии VUE JS с полного нуля

Узнать подробнее
npm install -g vue-cli
vue init webpack-simple realtime-chart-pusher

Совет. шаблон webpack-simple — это простая настройка webpack + vue-loader для быстрого прототипирования.

Настройка сервера Node.js

Следующее, что нужно сделать, это настроить сервер, который поможет нам общаться с Pusher. Я собираюсь предположить, что и Node и npm установлены в вашей системе. Затем мы установим зависимости, которые мы будем использовать для сервера Node.

npm install body-parser express nodemon pusher 

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

Еще одна вещь, нам понадобится точка входа / файл для нашего Node-сервера. Мы можем сделать это, создав файл server.js в корне приложения.

Настройка Pusher

Чтобы реализовать функциональность в реальном времени, нам понадобится мощность Pusher. Если вы еще этого не сделали, зарегистрируйте учетную запись Pusher и создайте новое приложение. Когда ваше новое приложение будет создано, получите свой app_id, ключи и кластер из панели инструментов Pusher.

Настройка приложения

Теперь, когда у нас есть учетная запись Pusher, и мы установили зависимости, необходимые для бэкэнд Node.js, давайте создавать. Давайте напишем код для файла server.js.


const express = require('express');
const path = require('path');
const bodyParser = require("body-parser");
const app = express();
const Pusher = require('pusher');

const pusher = new Pusher({
 appId: 'YOUR_APP_ID',
 key: 'YOUR_APP_KEY',
 secret: 'YOUR_APP_SECRET',
 cluster: 'eu',
 encrypted: true
});

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname + '/app')));

app.set('port', (process.env.PORT || 5000));

app.listen(app.get('port'), function() {
 console.log('Node app is running on port', app.get('port'));
});

Давайте посмотрим, что здесь происходит. Мы требуем Express, path, body-parser и Pusher, и мы инициализировали express () с приложением.

Мы используем body-parser для извлечения всей части входящего потока запросов и выставляем его на req.body.

Pusher также инициализируется учетными данными приложения и кластером из панели управления. Обязательно обновите это, иначе сервер node не будет подключен к панели управления. Наконец, сервер Node будет работать на 5000 порте.

Следующее, что нужно сделать, это определить маршрут нашего приложения, а также добавить макет данных для диаграммы расходов и доходов. Обновите файл server.js следующим образом.


let expensesList = {
 data: [
 {
 date: "April 15th 2017",
 expense: 100,
 income: 4000
 },
 {
 date: "April 22nd 2017",
 expense: 500,
 income: 2000
 },
 {
 date: "April 24th 2017",
 expense: 1000,
 income: 2300
 },
 {
 date: "April 29th 2017",
 expense: 2000,
 income: 1234
 },
 {
 date: "May 1st 2017",
 expense: 500,
 income: 4180
 },
 {
 date: "May 5th 2017",
 expense: 4000,
 income: 5000
 },
 ]
}

Во-первых, у нас есть объект costList с данными, содержащими расходы и доходы за определенные дни.

app.get('/finances', (req,res) => { 
 res.send(expensesList); 
 }); 

Этот маршрут просто отправляет объект costList как JSON. Мы используем этот маршрут для получения данных и отображения на интерфейсе.

app.post('/expense/add', (req, res) => {
  let expense = Number(req.body.expense)
  let income = Number(req.body.income)
  let date = req.body.date;


  let newExpense  = {
 date: date,
 expense: expense,
 income: income
  };


  expensesList.data.push(newExpense);


  pusher.trigger('finance', 'new-expense', {
 newExpense: expensesList
  });


  res.send({
 success : true,
 income: income,
 expense: expense,
 date: date,
 data: expensesList
  })
});

Маршрут /expense/add делает очень много. Это POST-маршрут, что означает, что мы будем ожидать некоторых входящих данных (в данном случае, суммы расходов и суммы дохода).

Затем мы подталкиваем этот новый доход и расходы к существующему, после чего мы также подталкиваем обновленный costList к Pusher.

Наконец, мы отправляем JSON в ответ на маршрут, содержащий последние доходы, расходы, дату и обновленные расходыList. Ваш окончательный server.js должен выглядеть следующим образом:


const express = require('express');
const path = require('path');
const bodyParser = require("body-parser");
const app = express();
const Pusher = require('pusher');

const pusher = new Pusher({
 appId: 'APP_ID',
 key: 'YOUR_KEY',
 secret: 'YOUR_SECRET',
 cluster: 'YOUR_CLUSTER',
 encrypted: true
});

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname + '/app')));

app.set('port', (process.env.PORT || 5000));

let expensesList = {
 data: [
 {
 date: "April 15th 2017",
 expense: 100,
 income: 4000
 },
 {
 date: "April 22nd 2017",
 expense: 500,
 income: 2000
 },
 {
 date: "April 24th 2017",
 expense: 1000,
 income: 2300
 },
 {
 date: "April 29th 2017",
 expense: 2000,
 income: 1234
 },
 {
 date: "May 1st 2017",
 expense: 500,
 income: 4180
 },
 {
 date: "May 5th 2017",
 expense: 4000,
 income: 5000
 },
 ]
}

app.get('/finances', (req,res) => {
 res.send(expensesList);
});

app.post('/expense/add', (req, res) => {
 let expense = Number(req.body.expense)
 let income = Number(req.body.income)
 let date = req.body.date;

 let newExpense  = {
 date: date,
 expense: expense,
 income: income
 };

 expensesList.data.push(newExpense);

 pusher.trigger('finance', 'new-expense', {
 newExpense: expensesList
 });

 res.send({
 success : true,
 income: income,
 expense: expense,
 date: date,
 data: expensesList
 })
});

app.listen(app.get('port'), function() {
 console.log('Node app is running on port', app.get('port'));
});

Построение Frontend (Vue + vue-chartjs)

Большая часть работы с интерфейсом будет выполнена внутри папки src/components. Перейдите в этот каталог, и вы увидите файл Hello.vue. Вы можете либо удалить этот файл, либо переименовать его в Home.vue поскольку нам понадобится файл Home.vue внутри папки компонентов.

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


<template>
  <div id="app">
 <home></home>
  </div>
</template>

<script>
import Home from './components/Home' //We are importing the Home component

export default {
  name: 'app',
  components: {
 Home
  }
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

Затем мы установим vue-chartjs, momentjs, pusher-js (библиотека Javascript Pusher) и axios (мы будем использовать axios для создания запросов API). А затем добавьте их в приложение Vue.js.

npm install axios vue-chartjs pusher-js moment 

Как только это будет сделано, мы импортируем axios и зарегистрируем его глобально в нашем приложении. Мы можем это сделать, открыв файл main.js в папке src.

Фреймворк VUE JS: быстрый старт, первые результаты

Получите бесплатный курс и создайте веб-приложение на трендовой Frontend-технологии VUE JS с полного нуля

Узнать подробнее
// src/main.js
import Vue from 'vue'
import App from './App'
import axios from 'axios' // we import axios from installed dependencies

Vue.config.productionTip = false

Vue.use(axios) // we register axios globally

/* eslint-disable no-new */
new Vue({
  el: '#app',
  template: '<App/>',
  components: { App }
})

Затем создадим компонент Vue.js, который поможет отобразить нашу диаграмму. Мы собираемся использовать это, чтобы указать, какой тип диаграммы мы хотим, настроить его внешний вид и как он себя ведет.

Затем мы импортируем этот компонент в компонент Home.vue и используем его там. Это одно из преимуществ vue-chartjs, оно работает путем импорта базового класса диаграммы, который мы затем можем расширить. Давайте продолжим и создадим этот компонент. Создайте новый файл с именем LineChart.vue внутри папки src/components и отредактируйте с помощью приведенного ниже кода.


<script>
  import {Line, mixins} from 'vue-chartjs' // We specify what type of chart we want from vue-chartjs and the mixins module
  const { reactiveProp } = mixins
  export default Line.extend({ //We are extending the base chart class as mentioned above
 mixins: [reactiveProp],
 data () {
 return {
 options: { //Chart.js options
 scales: {
 yAxes: [{
 ticks: {
 beginAtZero: true
 },
 gridLines: {
 display: true
 }
 }],
 xAxes: [ {
 gridLines: {
 display: false
 }
 }]
 },
 legend: {
 display: true
 },
 responsive: true,
 maintainAspectRatio: false
 }
 }
 },
 mounted () {
 // this.chartData is created in the mixin
 this.renderChart(this.chartData, this.options)
 }
  })
</script>

В приведенном выше блоке кода мы импортировали линейную диаграмму из vue-chartjs и модуля mixins. Chart.js обычно не предоставляет возможность автоматического обновления всякий раз, когда набор данных изменяется, но это можно сделать в vue-chartjs с помощью следующих миксинов: reactiveProp, reactiveData.

Эти миксины автоматически создают chartData в качестве опоры или данных и добавляют наблюдателя. Если данные изменились, диаграмма обновится.

Кроме того, this.renderChart() внутри mounted функции отвечает за отображение диаграммы. this.chartData — это объект, содержащий набор данных, необходимый для диаграммы, и мы получим его, включив его как опору в шаблон Home.vue this.options содержит объект опций, который определяет внешний вид и конфигурацию диаграммы.

Теперь у нас есть компонент LineChart, но как мы можем увидеть нашу диаграмму и проверить ее функциональность в реальном времени? Мы делаем это, добавляя LineChart к нашему компоненту Home.vue а также подписываясь на наш канал Pusher через pusher-js.

Откройте файл Home.vue и измените его следующим образом:


<template>
  <div class="hello">
 <div class="container">
 <div class="row">
 <h2 class="title">Realtime Chart with Vue and Pusher</h2>
 <h3 class="subtitle">Expense and Income Tracker</h3>
 <!--We are using the LineChart component imported below in the script and also setting the chart-data prop to the datacollection object-->
 <line-chart :chart-data="datacollection"></line-chart>
 </div>
 </div>
 <div class="container">
 <div class="row">
 <form class="form" @submit.prevent="addExpenses">
 <h4>Add New Entry</h4>
 <div class="form-group">
 <label>Expenses</label>
 <input class="form-control" placeholder="How much did you spend?" type="number" v-model="expenseamount" required>
 </div>
 <div class="form-group">
 <label>Income</label>
 <input class="form-control" placeholder="How much did you earn?" type="number" v-model="incomeamount" required>
 </div>
 <div class="form-group">
 <label>Date</label>
 <input class="form-control" placeholder="Date" type="date" v-model="entrydate" required>
 </div>
 <div class="form-group">
 <button class="btn btn-primary">Add New Entry</button>
 </div>
 </form>
 </div>
 </div>
  </div>
</template>

<script>
  import axios from 'axios'
  import moment from 'moment'
  import Pusher from 'pusher-js'
  import LineChart from '@/components/LineChart'

  const socket = new Pusher('APP_KEY', {
 cluster: 'eu',
 encrypted: true
  })
  const channel = socket.subscribe('finance')

  export default {
 name: 'home',
 components: {LineChart},
 data () {
 return {
 expense: null,
 income: null,
 date: null,
 expenseamount: null,
 incomeamount: null,
 datacollection: null,
 entrydate: null
 }
 },
 created () {
 this.fetchData()
 this.fillData()
 },
 mounted () {
 this.fillData()
 },
 methods: {
 fillData () {
 },
 addExpenses () {
 },
 fetchData () {
 }
 }
  }
</script>


<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>

  .title {
 text-align: center;
 margin-top: 40px;
  }
  .subtitle {
 text-align: center;
  }
  .form {
 max-width: 600px;
 width: 100%;
 margin: 20px auto 0 auto;
  }
  .form h4 {
 text-align: center;
 margin-bottom: 30px;
  }

  h1, h2 {
  font-weight: normal;
}

ul {
  list-style-type: none;
  padding: 0;
}

li {
  display: inline-block;
  margin: 0 10px;
}

a {
  color: #42b983;
}
</style>

fillData

Эта функция вызывается сразу после того, как приложение смонтировано, и в основном делает запрос API к бэкэнду узла (/ finances) и получает costList.


 fillData () {
 axios.get('/finances')
 .then(response => {
 let results = response.data.data
 
 let dateresult = results.map(a => a.date)
 let expenseresult = results.map(a => a.expense)
 let incomeresult = results.map(a => a.income)
 
 this.expense = expenseresult
 this.income = incomeresult
 this.date = dateresult
 
 this.datacollection = {
 labels: this.date,
 datasets: [
 {
 label: 'Expense',
 backgroundColor: '#f87979',
 data: this.expense
 },
 {
 label: 'Income',
 backgroundColor: '#5bf8bf',
 data: this.income
 }
 ]
 }
 })
 .catch(error => {
 console.log(error)
 })
  }

Запрос GET делается для маршрута /finances Node.js, который, в свою очередь, возвращает последние expensesList и затем мы обрабатываем эти данные с помощью .map Javascript и присваиваем его различным переменным.

addExpenses


addExpenses () {
  //We first get the new entries via the v-model we defined on the income and expense input tag
  let expense = this.expenseamount
  let income = this.incomeamount
  let today = moment(this.entrydate).format('MMMM Do YYYY') //Formats the date via momentJS
 
  //Sends a POST request to /expense/new along with the expense, income and date.
  axios.post('/expense/add', {
 expense: expense,
 income: income,
 date: today
  })
 .then(response => {
 this.expenseamount = ''
 this.incomeamount = ''
 //We are bound to new-expense on Pusher and once it detects a change via the new entry we just submitted, we use it to build the Line Chart again.
 channel.bind('new-expense', function(data) {
 let results = data.newExpense.data
 
 let dateresult = results.map(a => a.date);
 let expenseresult = results.map(a => a.expense);
 let incomeresult = results.map(a => a.income);
 
 //The instance data are updated here with the latest data gotten from Pusher
 this.expense = expenseresult
 this.income = incomeresult
 this.date = dateresult
 
 //The Chart's dataset is updated with the latest data gotten from Pusher
 this.datacollection = {
 labels: this.date,
 datasets: [
 {
 label: 'Expense Charts',
 backgroundColor: '#f87979',
 data: this.expense
 },
 {
 label: 'Income Charts',
 backgroundColor: '#5bf8bf',
 data: this.income
 }
 ]
 }
 });
  })
}

В приведенном выше блоке кода используется метод POST-роута для /expense/add для обновления expensesList («не забывайте /expense/add маршрут на сервере Node» отправляет обновленный список expensesList на панель управления Pusher) вместе с данными о доходах, расходах и дате.

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

fetchData

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


fetchData () {
 //We are bound to new-expense on Pusher and it listens for changes to the dataset so it can automatically rebuild the Line Chart in realtime. 
 channel.bind('new-expense', data => {
 let _results = data.newExpense.data
 let dateresult = _results.map(a => a.date);
 let expenseresult = _results.map(a => a.expense);
 let incomeresult = _results.map(a => a.income);
 
 //The instance data are updated here with the latest data gotten from Pusher
 this.expense = expenseresult
 this.income = incomeresult
 this.date = dateresult

 //The Chart's dataset is updated with the latest data gotten from Pusher
 this.datacollection = {
 labels: this.date,
 datasets: [
 {
 label: 'Expense Charts',
 backgroundColor: '#f87979',
 data: this.expense
 },
 {
 label: 'Income Charts',
 backgroundColor: '#5bf8bf',
 data: this.income
 }
 ]
 }
 });
}

Ваш последний файл Home.vue должен выглядеть следующим образом:


<template>
  <div class="hello">
 <div class="container">
 <div class="row">
 <h2 class="title">Realtime Chart with Vue and Pusher</h2>
 <h3 class="subtitle">Expense and Income Tracker</h3>
 <line-chart :chart-data="datacollection"></line-chart>
 </div>
 </div>
 <div class="container">
 <div class="row">
 <form class="form" @submit.prevent="addExpenses">
 <h4>Add New Entry</h4>
 <div class="form-group">
 <label>Expenses</label>
 <input class="form-control" placeholder="How much did you spend today?" type="number" v-model="expenseamount" required>
 </div>
 <div class="form-group">
 <label>Income</label>
 <input class="form-control" placeholder="How much did you earn today?" type="number" v-model="incomeamount" required>
 </div>
 <div class="form-group">
 <button class="btn btn-primary">Add New Entry</button>
 </div>
 </form>
 </div>
 </div>
  </div>
</template>

<script>
  import axios from 'axios'
  import moment from 'moment'
  import Pusher from 'pusher-js'
  import LineChart from '@/components/LineChart'

  const socket = new Pusher('3e6b0e8f2442b34330b7', {
 cluster: 'eu',
 encrypted: true
  })
  const channel = socket.subscribe('finance')

  export default {
 name: 'home',
 components: {LineChart},
 data () {
 return {
 expense: null,
 income: null,
 date: null,
 expenseamount: null,
 incomeamount: null,
 datacollection: null
 }
 },
 created () {
 this.fetchData()
 this.fillData()
 },
 mounted () {
 this.fillData()
 },
 methods: {
 fillData () {
 axios.get('/finances')
 .then(response => {
 let results = response.data.data

 let dateresult = results.map(a => a.date)
 let expenseresult = results.map(a => a.expense)
 let incomeresult = results.map(a => a.income)

 this.expense = expenseresult
 this.income = incomeresult
 this.date = dateresult

 this.datacollection = {
 labels: this.date,
 datasets: [
 {
 label: 'Expense',
 backgroundColor: '#f87979',
 data: this.expense
 },
 {
 label: 'Income',
 backgroundColor: '#5bf8bf',
 data: this.income
 }
 ]
 }
 })
 .catch(error => {
 console.log(error)
 })
 },
 addExpenses () {
 let expense = this.expenseamount
 let income = this.incomeamount
 let today = moment().format('MMMM Do YYYY')
 axios.post('/expense/add', {
 expense: expense,
 income: income,
 date: today
 })
 .then(response => {
 this.expenseamount = ''
 this.incomeamount = ''
 channel.bind('new-expense', function (data) {
 let results = data.newExpense.data

 let dateresult = results.map(a => a.date)
 let expenseresult = results.map(a => a.expense)
 let incomeresult = results.map(a => a.income)

 this.expense = expenseresult
 this.income = incomeresult
 this.date = dateresult

 this.datacollection = {
 labels: this.date,
 datasets: [
 {
 label: 'Expense',
 backgroundColor: 'transparent',
 pointBorderColor: '#f87979',
 data: this.expense
 },
 {
 label: 'Income',
 backgroundColor: 'transparent',
 pointBorderColor: '#5bf8bf',
 data: this.income
 }
 ]
 }
 })
 })
 .catch(error => {
 console.log(error)
 })
 },
 fetchData () {
 channel.bind('new-expense', data => {
 let results = data.newExpense.data
 let dateresult = results.map(a => a.date)
 let expenseresult = results.map(a => a.expense)
 let incomeresult = results.map(a => a.income)

 this.expense = expenseresult
 this.income = incomeresult
 this.date = dateresult

 this.datacollection = {
 labels: this.date,
 datasets: [
 {
 label: 'Expense Charts',
 backgroundColor: '#f87979',
 data: this.expense
 },
 {
 label: 'Income Charts',
 backgroundColor: '#5bf8bf',
 data: this.income
 }
 ]
 }
 })
 }
 }
  }
</script>


<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>

  .title {
 text-align: center;
 margin-top: 40px;
  }
  .subtitle {
 text-align: center;
  }
  .form {
 max-width: 600px;
 width: 100%;
 margin: 20px auto 0 auto;
  }
  .form h4 {
 text-align: center;
 margin-bottom: 30px;
  }

  h1, h2 {
  font-weight: normal;
}

ul {
  list-style-type: none;
  padding: 0;
}

li {
  display: inline-block;
  margin: 0 10px;
}

a {
  color: #42b983;
}
</style>

Еще кое-что!

Прежде чем мы сможем запустить наше приложение, нам нужно сделать что-то, называемое API-прокси. API-прокси позволяет нам интегрировать наше приложение vue-cli с backend сервером (в нашем случае — Node-сервер). Это означает, что мы можем запускать сервер dev и бэкэнд API бок о бок и позволить серверу-разработчику проксировать все запросы API на фактический сервер.

Мы можем включить API-прокси, отредактировав параметр dev.proxyTable в config/index.js. Вы можете редактировать с помощью кода ниже.


proxyTable: {
  '/expense/add': {
 target: 'http://localhost:5000',
 changeOrigin: true
  },
  '/finances': {
 target: 'http://localhost:5000',
 changeOrigin: true
  },
}

После этого мы, наконец, готовы увидеть наше приложение, и вы можете запустить npm run dev, чтобы запустить приложение.

Это оно! На этом этапе вы должны иметь диаграмму приборной панели реального времени, которая обновляется в реальном времени.

Вы можете проверить демо-версию здесь или перейти к коду для всего приложения, которое размещено на Github для вашего прочтения.

Заключение

Мы видели, как создать базовую линейную диаграмму с ChartJS в Vue с помощью vue-chartjs, а также добавить функции в реальном времени через Pusher.

Затем мы увидели, как использовать реактивные приложения, чтобы сделать ChartJS обновлением своего набора данных, если произошел сбой в наборе данных. Мы также видели, как использовать Pusher для запуска событий на сервере и прослушивания их на стороне клиента с помощью JS.

Вы недавно создали что-нибудь интересное с Pusher, может быть, диаграмма? Дайте знать в ответах ниже.

Автор: Yomi

Источник: https://medium.com/

Редакция: Команда webformyself.

Фреймворк VUE JS: быстрый старт, первые результаты

Получите бесплатный курс и создайте веб-приложение на трендовой Frontend-технологии VUE JS с полного нуля

Узнать подробнее

VUE JS. Быстрый старт

Практический курс по созданию веб-приложения на VUE JS с полного нуля

Получить

Метки:

Похожие статьи:

Комментарии Вконтакте:

Комментарии Facebook:

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Я не робот.

Spam Protection by WP-SpamFree