Utilizarea Vue.js pentru a crea un tablou de bord interactiv meteo cu API-uri

Publicat: 2022-03-10
Rezumat rapid ↬ Crearea unui tablou de bord cu date API este adesea o chestiune complexă. Alegerea stivei de tehnologie, integrarea API-urilor, selectarea graficelor potrivite și înfrumusețarea cu stiluri CSS pot deveni dificile. Acest tutorial este un ghid pas cu pas despre cum să vă ajute să creați un tablou de bord meteo în Vue.js folosind datele API.

(Acesta este un articol sponsorizat.) În acest tutorial, veți construi un tablou de bord simplu pentru vremea de la zero. Va fi o aplicație de tip client care nu este nici un exemplu „Hello World”, nici prea intimidantă în dimensiunea și complexitatea sa.

Întregul proiect va fi dezvoltat folosind instrumente din ecosistemul Node.js + npm. În special, ne vom baza în mare măsură pe API-ul Dark Sky pentru date, pe Vue.js pentru toate sarcinile grele și pe FusionCharts pentru vizualizarea datelor.

Cerințe preliminare

Ne așteptăm să fiți familiarizat cu următoarele:

  • HTML5 și CSS3 (vom folosi și caracteristicile de bază oferite de Bootstrap;
  • JavaScript (în special modul ES6 de utilizare a limbajului);
  • Node.js și npm (elementele de bază ale mediului și gestionării pachetelor sunt foarte bune).

În afară de cele menționate mai sus, ar fi grozav dacă sunteți familiarizat cu Vue.js sau cu orice alt cadru JavaScript similar. Nu ne așteptăm să știți despre FusionCharts - este atât de ușor de utilizat încât îl veți învăța din mers!

Învățături așteptate

Învățăturile tale cheie din acest proiect vor fi:

  1. Cum să planificați implementarea unui tablou de bord bun
  2. Cum se dezvoltă aplicații cu Vue.js
  3. Cum se creează aplicații bazate pe date
  4. Cum să vizualizați datele folosind FusionCharts

În special, fiecare dintre secțiuni vă duce cu un pas mai aproape de obiectivele de învățare:

  1. O introducere în tabloul de bord pentru vreme
    Acest capitol vă oferă o privire de ansamblu asupra diferitelor aspecte ale întreprinderii.
  2. Creați Proiectul
    În această secțiune, învățați despre crearea unui proiect de la zero folosind instrumentul de linie de comandă Vue.
  3. Personalizați structura implicită a proiectului
    Schela implicită de proiect pe care o obțineți în secțiunea anterioară nu este suficientă; aici înveți elementele suplimentare necesare proiectului din punct de vedere structural.
  4. Achiziția și prelucrarea datelor
    Această secțiune este miezul proiectului; tot codul critic pentru achiziționarea și procesarea datelor din API este prezentat aici. Așteptați-vă să petreceți timp maxim pe această secțiune.
  5. Vizualizarea datelor cu FusionCharts
    Odată ce avem toate datele și alte părți în mișcare ale proiectului stabilizate, această secțiune este dedicată vizualizării datelor folosind FusionCharts și un pic de CSS.

1. Fluxul de lucru din tabloul de bord

Înainte de a ne aprofunda în implementare, este important să fim clari cu privire la planul nostru. Împărțim planul nostru în patru aspecte distincte:

Cerințe

Care sunt cerințele noastre pentru acest proiect? Cu alte cuvinte, care sunt lucrurile pe care vrem să le prezentăm prin tabloul de bord pentru vreme? Ținând cont de faptul că publicul vizat este probabil simpli muritori cu gusturi simple, am dori să le arătăm următoarele:

  • Detalii despre locația pentru care doresc să vadă vremea, împreună cu câteva informații primare despre vreme. Deoarece nu există cerințe stricte, vom afla detaliile plictisitoare mai târziu. Cu toate acestea, în această etapă, este important să rețineți că va trebui să punem la dispoziție publicului o casetă de căutare, astfel încât să poată furniza informații pentru locația de interes.
  • Informații grafice despre vremea locației lor de interes, cum ar fi:
    • Variația temperaturii pentru ziua interogării
    • Repere ale vremii de astăzi:
      • Viteza și direcția vântului
      • Vizibilitate
      • Index UV

Notă : Datele obținute din API oferă informații cu privire la multe alte aspecte ale vremii. Alegem să nu le folosim pe toate de dragul de a menține codul la minimum.

Structura

Pe baza cerințelor, ne putem structura tabloul de bord după cum se arată mai jos:

Structura tabloului de bord
(Previzualizare mare)

Date

Tabloul nostru de bord este la fel de bun ca și datele pe care le obținem, deoarece nu vor exista vizualizări frumoase fără date adecvate. Există o mulțime de API-uri publice care oferă date meteorologice - unele dintre ele sunt gratuite, iar altele nu. Pentru proiectul nostru, vom colecta date din API-ul Dark Sky. Cu toate acestea, nu vom putea interoga punctul final API direct de la capătul clientului. Nu vă faceți griji, avem o soluție care va fi dezvăluită exact la momentul potrivit! Odată ce obținem datele pentru locația căutată, vom face unele procesări și formatare a datelor - știți, tipul de aspecte tehnice care ne ajută să plătim facturile.

Vizualizarea

Odată ce obținem date curate și formatate, le conectăm la FusionCharts. Există foarte puține biblioteci JavaScript în lume la fel de capabile ca FusionCharts. Din numărul mare de oferte de la FusionCharts, vom folosi doar câteva - toate scrise în JavaScript, dar funcționează perfect atunci când sunt integrate cu wrapper-ul Vue pentru FusionCharts.

Înarmați cu imaginea de ansamblu, să ne murdărim mâinile - este timpul să concretizăm lucrurile! În secțiunea următoare, veți crea proiectul Vue de bază, pe deasupra căruia vom construi în continuare.

2. Crearea Proiectului

Pentru a crea proiectul, executați următorii pași:

  1. Instalați Node.js + npm
    ( Dacă aveți Node.js instalat pe computer, săriți peste acest pas. )
    Node.js vine cu npm la pachet, deci nu trebuie să instalați separat npm. În funcție de sistemul de operare, descărcați și instalați Node.js conform instrucțiunilor date aici.

    Odată instalat, este probabil o idee bună să verificați dacă software-ul funcționează corect și care sunt versiunile lor. Pentru a testa asta, deschideți linia de comandă/terminalul și executați următoarele comenzi:
     node --version npm --version
  2. Instalați pachete cu npm
    Odată ce ați instalat și rulat npm, executați următoarea comandă pentru a instala pachetele de bază necesare proiectului nostru.
     npm install -g vue@2 vue-cli@2
  3. Inițializați schelele de proiect cu vue-cli
    Presupunând că pasul anterior a mers bine, următorul pas este să utilizați vue-cli — un instrument de linie de comandă de la Vue.js, pentru a inițializa proiectul. Pentru a face acest lucru, executați următoarele:
    • Inițializați schelele cu șablonul webpack-simple.
       vue init webpack-simple vue_weather_dashboard
      Vi se vor pune o grămadă de întrebări — acceptarea setărilor implicite pentru toate, dar ultima întrebare va fi suficient de bună pentru acest proiect; răspunsul N pentru ultimul.
      O captură de ecran a liniei de comandă/terminalului
      (Previzualizare mare)
      Rețineți că, deși webpack-simple este excelent pentru prototipare rapidă și aplicații ușoare precum a noastră, nu este deosebit de potrivit pentru aplicații serioase sau implementare în producție. Dacă doriți să utilizați orice alt șablon (deși am sfătui împotriva lui dacă sunteți începător) sau doriți să denumiți proiectul cu altceva, sintaxa este:
       vue init [template-name] [project-name]
    • Navigați la directorul creat de vue-cli pentru proiect.
       cd vue_weather_dashboard
    • Instalați toate pachetele menționate în package.json , care a fost creat de instrumentul vue-cli pentru șablonul webpack-simple .
       npm install
    • Porniți serverul de dezvoltare și vedeți proiectul implicit Vue lucrând în browser!
       npm run dev

Dacă sunteți nou în Vue.js, acordați-vă un moment pentru a savura cea mai recentă realizare - ați creat o mică aplicație Vue și rulează la localhost:8080!

O captură de ecran a site-ului Vue.js
(Previzualizare mare)

Scurtă explicație a structurii implicite a proiectului

Este timpul să aruncăm o privire asupra structurii din directorul vue_weather_dashboard , astfel încât să înțelegeți elementele de bază înainte de a începe să o modificăm.

Structura arată cam așa:

 vue_weather_dashboard |--- README.md |--- node_modules/ | |--- ... | |--- ... | |--- [many npm packages we installed] | |--- ... | |--- ... |--- package.json |--- package-lock.json |--- webpack.config.js |--- index.html |--- src | |--- App.vue | |--- assets | | |--- logo.png | |--- main.js

Deși ar putea fi tentant să omiteți familiarizarea cu fișierele și directoarele implicite, dacă sunteți nou în Vue, vă recomandăm cu tărie să aruncați o privire la conținutul fișierelor. Poate fi o sesiune educațională bună și poate declanșa întrebări pe care ar trebui să le urmăriți singur, în special următoarele fișiere:

  • package.json și doar o privire la vărul său package-lock.json
  • webpack.config.js
  • index.html
  • src/main.js
  • src/App.vue

O scurtă explicație a fiecărui fișier și directoare prezentate în diagrama arborescentă sunt prezentate mai jos:

  • README.md
    Nici un premiu pentru ghicire - este în primul rând pentru oameni să citească și să înțeleagă pașii necesari pentru crearea schelei proiectului.
  • module_nod/
    Acesta este directorul în care npm descarcă pachetele necesare pentru pornirea proiectului. Informațiile despre pachetele necesare sunt disponibile în fișierul package.json .
  • pachet.json
    Acest fișier este creat de instrumentul vue-cli pe baza cerințelor șablonului webpack-simple și conține informații despre pachetele npm (inclusiv cu versiunile acestora și alte detalii) care trebuie instalate. Aruncați o privire atentă la conținutul acestui fișier - aici ar trebui să vizitați și, probabil, să editați pentru a adăuga/șterge pachetele necesare pentru proiect, apoi rulați npm install. Citiți mai multe despre package.json aici.
  • package-lock.json
    Acest fișier este creat de npm însuși și este menit în primul rând pentru a păstra un jurnal al lucrurilor pe care npm le-a descărcat și instalat.
  • webpack.config.js
    Acesta este un fișier JavaScript care conține configurația webpack - un instrument care reunește diferite aspecte ale proiectului nostru (cod, active statice, configurație, medii, mod de utilizare etc.) și care se reduce înainte de a-l servi utilizatorului. Avantajul este că toate lucrurile sunt legate automat, iar experiența utilizatorului se îmbunătățește foarte mult datorită îmbunătățirii performanței aplicației (paginile sunt difuzate rapid și se încarcă mai repede în browser). După cum ați putea întâlni mai târziu, acesta este fișierul care trebuie inspectat atunci când ceva din sistemul de compilare nu funcționează așa cum a fost intenționat să fie. De asemenea, atunci când doriți să implementați aplicația, acesta este unul dintre fișierele cheie care trebuie editate (citiți mai multe aici).
  • index.html
    Acest fișier HTML servește ca matrice (sau puteți spune, șablon) în care datele și codul urmează să fie încorporate dinamic (așa face în primul rând Vue), și apoi oferite utilizatorului.
  • src/main.js
    Acest fișier JavaScript conține cod care gestionează în primul rând dependențele de nivel superior/proiect și definește componenta Vue de cel mai înalt nivel. Pe scurt, orchestrează JavaScript pentru întregul proiect și servește drept punct de intrare al aplicației. Editați acest fișier atunci când trebuie să declarați dependențe la nivelul întregului proiect de anumite module de noduri sau doriți să se schimbe ceva în ceea ce privește cea mai înaltă componentă Vue din proiect.
  • src/App.vue
    În punctul anterior, când vorbeam despre „cea mai înaltă componentă Vue”, vorbeam în esență despre acest fișier. Fiecare fișier .vue din proiect este o componentă, iar componentele sunt legate ierarhic. La început, avem un singur fișier .vue , adică App.vue , ca singura noastră componentă. Dar în scurt timp vom adăuga mai multe componente la proiectul nostru (în primul rând urmând structura tabloului de bord) și le vom lega în conformitate cu ierarhia dorită, App.vue fiind strămoșul tuturor. Aceste fișiere .vue vor conține cod într-un format pe care Vue dorește să-l scriem. Nu vă faceți griji, sunt cod JavaScript scris, menținând o structură care ne poate menține sănătoși și organizați. Ați fost avertizat — până la sfârșitul acestui proiect, dacă sunteți nou la Vue, este posibil să deveniți dependent de template — script — style template — script — style template — script — style de organizare a codului!

Acum că am creat fundația, este timpul să:

  • Modificați șabloanele și modificați puțin fișierele de configurare, astfel încât proiectul să se comporte exact așa cum ne dorim.
  • Creați noi fișiere .vue și implementați structura tabloului de bord cu codul Vue.

Le vom învăța în secțiunea următoare, care va fi puțin lungă și necesită puțină atenție. Dacă aveți nevoie de cofeină sau apă sau doriți să vă descărcați, acum este momentul!

3. Personalizarea structurii implicite a proiectului

E timpul să ne mâncăm cu fundația pe care ne-a oferit-o proiectul de schele. Înainte de a începe, asigurați-vă că serverul de dezvoltare furnizat de webpack rulează. Avantajul rulării continuu a acestui server este că orice modificări pe care le faceți în codul sursă - o salvați și reîmprospătați pagina web - se reflectă imediat în browser.

Dacă doriți să porniți serverul de dezvoltare, executați următoarea comandă de pe terminal (presupunând că directorul dvs. curent este directorul proiectului):

 npm run dev

În secțiunile următoare, vom modifica unele dintre fișierele existente și vom adăuga câteva fișiere noi. Acesta va fi urmat de scurte explicații ale conținutului acelor fișiere, astfel încât să aveți o idee despre ceea ce sunt menite să facă acele modificări.

Modificați fișierele existente

index.html

Aplicația noastră este literalmente o aplicație cu o singură pagină, deoarece există o singură pagină web care este afișată în browser. Vom vorbi despre asta mai târziu, dar mai întâi să facem prima noastră schimbare — modificând textul din eticheta <title> .

Cu această mică revizuire, fișierul HTML arată astfel:

 <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <!-- Modify the text of the title tag below --> <title>Vue Weather Dashboard</title> </head> <body> <div></div> <script src="/dist/build.js"></script> </body> </html>

Luați un moment pentru a reîmprospăta pagina web la localhost:8080 și vedeți modificarea reflectată în bara de titlu a filei din browser - ar trebui să scrie „Vue Weather Dashboard”. Cu toate acestea, acesta a fost doar pentru a vă demonstra procesul de a face modificări și de a verifica dacă funcționează. Avem mai multe lucruri de făcut!

Această pagină HTML simplă nu are multe lucruri pe care le dorim în proiectul nostru, în special următoarele:

  • Câteva meta informații
  • Legături CDN către Bootstrap (cadru CSS)
  • link către foaia de stil personalizată (încă urmează să fie adăugată în proiect)
  • Indicatori către API-ul de geolocalizare Google Maps din eticheta <script>

După adăugarea acestor lucruri, index.html final are următorul conținut:

 <!DOCTYPE html> <html lang="en"> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"> <link rel="stylesheet" type="text/css" href="src/css/style.css"> <title>Weather Dashboard</title> <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyC-lCjpg1xbw-nsCc11Si8Ldg2LKYizqI4&libraries=places"></script> </head> <body> <div></div> <script src="/dist/build.js"></script> </body> </html>

Salvați fișierul și reîmprospătați pagina web. S-ar putea să fi observat o ușoară denivelare în timpul încărcării paginii - aceasta se datorează în primul rând faptului că stilul paginii este acum controlat de Bootstrap, iar elementele de stil precum fonturile, spațierea etc. sunt diferite de cele implicite pe care le aveam. mai devreme (dacă nu sunteți sigur, reveniți la valoarea implicită și vedeți diferența).

O captură de ecran când reîmprospătați pagina web cu localhost:8080
(Previzualizare mare)

Notă : Un lucru important înainte de a trece mai departe - adresa URL pentru API-ul Google Maps conține o cheie care este o proprietate a FusionCharts. Deocamdată, puteți folosi această cheie pentru a construi proiectul, deoarece nu vrem să vă blocați acest tip de detalii minuscule (care pot fi distragerii în timp ce sunteți nou). Cu toate acestea, vă îndemnăm cu tărie să generați și să utilizați propria cheie API Google Maps după ce ați făcut ceva progres și vă simțiți confortabil să acordați atenție acestor mici detalii.

pachet.json

La momentul scrierii acestui articol, am folosit anumite versiuni ale pachetelor npm pentru proiectul nostru și știm sigur că acele lucruri funcționează împreună. Cu toate acestea, până în momentul în care executați proiectul, este foarte posibil ca cele mai recente versiuni stabile ale pachetelor pe care npm le descarcă pentru dvs. să nu fie aceleași cu cele folosite de noi, iar acest lucru ar putea rupe codul (sau ar putea face lucruri care sunt dincolo de controlul nostru). Astfel, este foarte important să aveți exact același fișier package.json care a fost folosit pentru a construi acest proiect, astfel încât codul/explicațiile noastre și rezultatele pe care le obțineți să fie consecvente.

Conținutul fișierului package.json ar trebui să fie:

 { "name": "vue_weather_dashboard", "description": "A Vue.js project", "version": "1.0.0", "author": "FusionCharts", "license": "MIT", "private": true, "scripts": { "dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot", "build": "cross-env NODE_ENV=production webpack --progress --hide-modules" }, "dependencies": { "axios": "^0.18.0", "babel": "^6.23.0", "babel-cli": "^6.26.0", "babel-polyfill": "^6.26.0", "fusioncharts": "^3.13.3", "moment": "^2.22.2", "moment-timezone": "^0.5.21", "vue": "^2.5.11", "vue-fusioncharts": "^2.0.4" }, "browserslist": [ "> 1%", "last 2 versions", "not ie <= 8" ], "devDependencies": { "babel-core": "^6.26.0", "babel-loader": "^7.1.2", "babel-preset-env": "^1.6.0", "babel-preset-stage-3": "^6.24.1", "cross-env": "^5.0.5", "css-loader": "^0.28.7", "file-loader": "^1.1.4", "vue-loader": "^13.0.5", "vue-template-compiler": "^2.4.4", "webpack": "^3.6.0", "webpack-dev-server": "^2.9.1" } }

Vă încurajăm să parcurgeți noul package.json și să vă dați seama care sunt funcțiile diferitelor obiecte din json. Poate preferați să schimbați valoarea cheii „ author ” cu numele dvs. De asemenea, pachetele menționate în dependențe se vor dezvălui la momentul potrivit în cod. Deocamdată, este suficient să știți că:

  • Pachetele legate de babel sunt pentru manipularea corectă a codului de stil ES6 de către browser;
  • axios se ocupă de solicitările HTTP bazate pe promisiuni;
  • moment și moment-fus orar sunt pentru manipularea dată/ora;
  • fusioncharts și vue-fusioncharts sunt responsabile pentru randarea diagramelor:
  • vue , din motive evidente.

webpack.config.js

Ca și în cazul package.json , vă sugerăm să mențineți un fișier webpack.config.js care este în concordanță cu cel pe care l-am folosit pentru construirea proiectului. Cu toate acestea, înainte de a face modificări, vă recomandăm să comparați cu atenție codul implicit din webpack.config.js și codul pe care l-am furnizat mai jos. Veți observa destul de multe diferențe - căutați-le pe google și aveți o idee de bază despre ceea ce înseamnă. Deoarece explicarea în profunzime a configurațiilor pachetului web nu intră în domeniul de aplicare al acestui articol, sunteți pe cont propriu în acest sens.

Fișierul personalizat webpack.config.js este următorul:

 var path = require('path') var webpack = require('webpack') module.exports = { entry: ['babel-polyfill', './src/main.js'], output: { path: path.resolve(__dirname, './dist'), publicPath: '/dist/', filename: 'build.js' }, module: { rules: [ { test: /\.css$/, use: [ 'vue-style-loader', 'css-loader' ], }, { test: /\.vue$/, loader: 'vue-loader', options: { loaders: { } // other vue-loader options go here } }, { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ }, { test: /\.(png|jpg|gif|svg)$/, loader: 'file-loader', options: { name: '[name].[ext]?[hash]' } } ] }, resolve: { alias: { 'vue$': 'vue/dist/vue.esm.js' }, extensions: ['*', '.js', '.vue', '.json'] }, devServer: { historyApiFallback: true, noInfo: true, overlay: true, host: '0.0.0.0', port: 8080 }, performance: { hints: false }, devtool: '#eval-source-map' } if (process.env.NODE_ENV === 'production') { module.exports.devtool = '#source-map' // https://vue-loader.vuejs.org/en/workflow/production.html module.exports.plugins = (module.exports.plugins || []).concat([ new webpack.DefinePlugin({ 'process.env': { NODE_ENV: '"production"' } }), new webpack.optimize.UglifyJsPlugin({ sourceMap: true, compress: { warnings: false } }), new webpack.LoaderOptionsPlugin({ minimize: true }) ]) }

Odată cu modificările aduse webpack.config.js al proiectului, este imperativ să opriți serverul de dezvoltare care rulează ( Ctrl + C ) și să-l reporniți cu următoarea comandă executată din directorul proiectului după instalarea tuturor pachetelor menționate în package.json . fișier package.json :

 npm install npm run dev

Cu aceasta, se termină încercarea de a ajusta configurațiile și de a se asigura că pachetele potrivite sunt la locul lor. Totuși, aceasta marchează și călătoria modificării și scrierii codului, care este puțin lungă, dar și foarte plină de satisfacții!

src/main.js

Acest fișier este cheia pentru orchestrarea la nivel superior a proiectului - aici definim:

  • Care sunt dependențele de nivel superior (de unde să obțineți cele mai importante pachete npm necesare);
  • Cum să rezolvi dependențele, împreună cu instrucțiuni pentru Vue cu privire la utilizarea pluginurilor/wrapper-urilor, dacă există;
  • O instanță Vue care gestionează cea mai înaltă componentă a proiectului: src/App.vue (fișierul nodal .vue ).

În conformitate cu obiectivele noastre pentru fișierul src/main.js , codul ar trebui să fie:

 // Import the dependencies and necessary modules import Vue from 'vue'; import App from './App.vue'; import FusionCharts from 'fusioncharts'; import Charts from 'fusioncharts/fusioncharts.charts'; import Widgets from 'fusioncharts/fusioncharts.widgets'; import PowerCharts from 'fusioncharts/fusioncharts.powercharts'; import FusionTheme from 'fusioncharts/themes/fusioncharts.theme.fusion'; import VueFusionCharts from 'vue-fusioncharts'; // Resolve the dependencies Charts(FusionCharts); PowerCharts(FusionCharts); Widgets(FusionCharts); FusionTheme(FusionCharts); // Globally register the components for project-wide use Vue.use(VueFusionCharts, FusionCharts); // Instantiate the Vue instance that controls the application new Vue({ el: '#app', render: h => h(App) })

src/App.vue

Acesta este unul dintre cele mai importante fișiere din întregul proiect și reprezintă componenta cea mai de sus a ierarhiei — întreaga aplicație în sine, ca întreg. Pentru proiectul nostru, această componentă va face toate sarcinile grele, pe care le vom explora mai târziu. Deocamdată, vrem să scăpăm de placa implicită și să punem ceva al nostru.

Dacă sunteți nou în modul Vue de a organiza codul, ar fi mai bine să vă faceți o idee despre structura generală din fișierele .vue . Fișierele .vue sunt formate din trei secțiuni:

  • Șablon
    Aici este definit șablonul HTML pentru pagină. În afară de HTML static, această secțiune conține și modul Vue de a încorpora conținut dinamic, folosind acolade duble {{ }} .
  • Scenariul
    JavaScript reglementează această secțiune și este responsabil pentru generarea de conținut dinamic care merge și se află în șablonul HTML în locurile adecvate. Această secțiune este în primul rând un obiect care este exportat și constă din:
    • Date
      Aceasta este o funcție în sine și, de obicei, returnează unele date dorite încapsulate într-o structură de date frumoasă.
    • Metode
      Un obiect care constă dintr-una sau mai multe funcții/metode, fiecare dintre acestea de obicei manipulează datele într-un fel sau altul și controlează, de asemenea, conținutul dinamic al șablonului HTML.
    • Calculat
      La fel ca obiectul metodă discutat mai sus, cu o distincție importantă - în timp ce toate funcțiile din obiectul metodă sunt executate ori de câte ori oricare dintre ele este apelată, funcțiile din obiectul calculat se comportă mult mai sensibil și se execută dacă și numai dacă a fost numit.
  • Stil
    Această secțiune este pentru stilul CSS care se aplică HTML-ului paginii (scris în șablon) - puneți aici vechiul CSS bun pentru a vă face paginile frumoase!

Ținând cont de paradigma de mai sus, să personalizăm minim codul în App.vue :

 <template> <div> <p>This component's code is in {{ filename }}</p> </div> </template> <script> export default { data() { return { filename: 'App.vue' } }, methods: { }, computed: { }, } </script> <style> </style>

Amintiți-vă că fragmentul de cod de mai sus este pur și simplu pentru a testa faptul că App.vue funcționează cu propriul nostru cod în el. Mai târziu va trece printr-o mulțime de modificări, dar mai întâi salvați fișierul și reîmprospătați pagina în browser.

O captură de ecran a browserului cu mesajul „Codul acestei componente este în App.vue”
(Previzualizare mare)

În acest moment, probabil că este o idee bună să obțineți ajutor în scule. Consultați instrumentele de dezvoltare Vue pentru Chrome și, dacă nu aveți multe probleme în utilizarea Google Chrome ca browser implicit pentru dezvoltare, instalați instrumentul și jucați-vă puțin cu el. Va fi extrem de util pentru dezvoltare și depanare ulterioară, atunci când lucrurile devin mai complicate.

Directoare și fișiere suplimentare

Următorul pas ar fi adăugarea de fișiere suplimentare, astfel încât structura proiectului nostru să devină completă. Am adăuga următoarele directoare și fișiere:

  • src/css/style.css
  • src/assets/calendar.svgvlocation.svgsearch.svgwinddirection.svgwindspeed.svg
  • src/components/Content.vueHighlights.vueTempVarChart.vueUVIndex.vueVisibility.vueWindStatus.vue

Notă : Salvați fișierele .svg cu hyperlink în proiectul dvs.

Creați directoarele și fișierele menționate mai sus. Structura finală a proiectului ar trebui să arate (nu uitați să ștergeți folderele și fișierele din structura implicită care acum nu sunt necesare):

 vue_weather_dashboard/ |--- README.md |--- node_modules/ | |--- ... | |--- ... | |--- [many npm packages we installed] | |--- ... | |--- ... |--- package.json |--- package-lock.json |--- webpack.config.js |--- index.html |--- src/ | |--- App.vue | |--- css/ | | |--- style.css | |--- assets/ | | |--- calendar.svg | | |--- location.svg | | |--- location.svg | | |--- winddirection.svg | | |--- windspeed.svg | |--- main.js | |--- components/ | | |--- Content.vue | | |--- Highlights.vue | | |--- TempVarChart.vue | | |--- UVIndex.vue | | |--- Visibility.vue | | |--- WindStatus.vue

Ar putea exista și alte fișiere, cum ar fi .babelrc , .gitignore , .editorconfig , etc. în folderul rădăcină al proiectului. Puteți să le ignorați în siguranță deocamdată.

În secțiunea următoare, vom adăuga conținut minim la fișierele nou adăugate și vom testa dacă funcționează corect.

src/css/style.css

Deși nu va fi de mare folos imediat, copiați următorul cod în fișier:

 @import url("https://fonts.googleapis.com/css?family=Roboto:300,400,500"); :root { font-size: 62.5%; } body { font-family: Roboto; font-weight: 400; width: 100%; margin: 0; font-size: 1.6rem; } #sidebar { position: relative; display: flex; flex-direction: column; background-image: linear-gradient(-180deg, #80b6db 0%, #7da7e2 100%); } #search { text-align: center; height: 20vh; position: relative; } #location-input { height: 42px; width: 100%; opacity: 1; border: 0; border-radius: 2px; background-color: rgba(255, 255, 255, 0.2); margin-top: 16px; padding-left: 16px; color: #ffffff; font-size: 1.8rem; line-height: 21px; } #location-input:focus { outline: none; } ::placeholder { color: #FFFFFF; opacity: 0.6; } #current-weather { color: #ffffff; font-size: 8rem; line-height: 106px; position: relative; } #current-weather>span { color: #ffffff; font-size: 3.6rem; line-height: 42px; vertical-align: super; opacity: 0.8; top: 15px; position: absolute; } #weather-desc { font-size: 2.0rem; color: #ffffff; font-weight: 500; line-height: 24px; } #possibility { color: #ffffff; font-size: 16px; font-weight: 500; line-height: 19px; } #max-detail, #min-detail { color: #ffffff; font-size: 2.0rem; font-weight: 500; line-height: 24px; } #max-detail>i, #min-detail>i { font-style: normal; height: 13.27px; width: 16.5px; opacity: 0.4; } #max-detail>span, #min-detail>span { color: #ffffff; font-family: Roboto; font-size: 1.2rem; line-height: 10px; vertical-align: super; } #max-summary, #min-summary { opacity: 0.9; color: #ffffff; font-size: 1.4rem; line-height: 16px; margin-top: 2px; opacity: 0.7; } #search-btn { position: absolute; right: 0; top: 16px; padding: 2px; z-index: 999; height: 42px; width: 45px; background-color: rgba(255, 255, 255, 0.2); border: none; } #dashboard-content { text-align: center; height: 100vh; } #date-desc, #location-desc { color: #ffffff; font-size: 1.6rem; font-weight: 500; line-height: 19px; margin-bottom: 15px; } #date-desc>img { top: -3px; position: relative; margin-right: 10px; } #location-desc>img { top: -3px; position: relative; margin-left: 5px; margin-right: 15px; } #location-detail { opacity: 0.7; color: #ffffff; font-size: 1.4rem; line-height: 20px; margin-left: 35px; } .centered { position: fixed; top: 45%; left: 50%; transform: translate(-50%, -50%); } .max-desc { width: 80px; float: left; margin-right: 28px; } .temp-max-min { margin-top: 40px } #dashboard-content { background-color: #F7F7F7; } .custom-card { background-color: #FFFFFF !important; border: 0 !important; margin-top: 16px !important; margin-bottom: 20px !important; } .custom-content-card { background-color: #FFFFFF !important; border: 0 !important; margin-top: 16px !important; margin-bottom: 0px !important; } .header-card { height: 50vh; } .content-card { height: 43vh; } .card-divider { margin-top: 0; } .content-header { color: #8786A4; font-size: 1.4rem; line-height: 16px; font-weight: 500; padding: 15px 10px 5px 15px; } .highlights-item { min-height: 37vh; max-height: 38vh; background-color: #FFFFFF; } .card-heading { color: rgb(33, 34, 68); font-size: 1.8rem; font-weight: 500; line-height: 21px; text-align: center; } .card-sub-heading { color: #73748C; font-size: 1.6rem; line-height: 19px; } .card-value { color: #000000; font-size: 1.8rem; line-height: 21px; } span text { font-weight: 500 !important; } hr { padding-top: 1.5px; padding-bottom: 1px; margin-bottom: 0; margin-top: 0; line-height: 0.5px; } @media only screen and (min-width: 768px) { #sidebar { height: 100vh; } #info { position: fixed; bottom: 50px; width: 100%; padding-left: 15px; } .wrapper-right { margin-top: 80px; } } @media only screen and (min-width:1440px) { #sidebar { width: 350px; max-width: 350px; flex: auto; } #dashboard-content { width: calc(100% — 350px); max-width: calc(100% — 350px); flex: auto; } }

src/assets/

În acest director, descărcați și salvați fișierele .svg menționate mai jos:

  • calendar.svg
  • location.svg
  • search.svg
  • winddirection.svg
  • windspeed.svg

src/components/Content.vue

Aceasta este ceea ce numim o „componentă prostească” (adică un substituent) care este acolo doar pentru a menține ierarhia și, în esență, transmite datele componentelor sale secundare.

Amintiți-vă că nu există o bară tehnică pentru a scrie tot codul nostru în fișierul App.vue , dar adoptăm abordarea împărțirii codului prin imbricarea componentelor din două motive:

  • Pentru a scrie cod curat, care ajută la lizibilitate și întreținere;
  • Pentru a replica aceeași structură pe care o vom vedea pe ecran, adică ierarhia.

Înainte de a încadra componenta definită în Content.vue în componenta rădăcină App.vue , să scriem un cod de jucărie (dar educațional) pentru Content.vue :

 <template> <div> <p>This child components of Content.vue are:</p> <ul> <li v-for="child in childComponents">{{ child }}</li> </ul> </div> </template> <script> export default { data () { return { childComponents: ['TempVarChart.vue', 'Highlights.vue'] } }, methods: { }, computed: { }, } </script> <style> </style>

În cod, observați cu atenție și înțelegeți următoarele:

  • În cadrul etichetei <script> (unde, evident, scriem ceva cod JavaScript), definim un obiect care este exportat (pus la dispoziție pentru alte fișiere) în mod implicit. Acest obiect conține o funcție data() , care returnează un obiect matrice numit childComponents , cu elementele sale fiind numele fișierelor componente care ar trebui imbricate mai departe.
  • În cadrul etichetei <template> (unde scriem un șablon HTML), lucrul de interes este <ul> .
    • În lista neordonată, fiecare element din listă ar trebui să fie numele componentelor fii dorite, așa cum sunt definite în obiectul matrice childComponents . Mai mult, lista ar trebui să se extindă automat până la ultimul element al matricei. Se pare că ar trebui să scriem o buclă for , nu-i așa? Facem asta folosind directiva v-for furnizată de Vue.js. Directiva v-for :
      • Acționează ca un atribut al etichetei <li> , iterează prin matrice, redă numele componentelor copil unde iteratorul este menționat între paranteze {{ }} (unde scriem textul pentru elementele listei).

Codul și explicația de mai sus formează baza înțelegerii ulterioare a modului în care scriptul și șablonul sunt interconectate și cum putem folosi directivele furnizate de Vue.js.

Am învățat destul de multe, dar chiar și după toate acestea, mai avem un lucru de învățat despre conectarea perfectă a componentelor în ierarhie - transmiterea datelor de la componenta părinte către copiii acesteia. Deocamdată, trebuie să învățăm cum să transmitem unele date de la src/App.vue la src/components/Content.vue , astfel încât să putem folosi aceleași tehnici pentru restul imbricației componentelor din acest proiect.

Datele care se scurg de la componentele părinte la componentele copilului ar putea părea simplu, dar diavolul este în detalii! După cum se explică pe scurt mai jos, există mai mulți pași implicați pentru ca acesta să funcționeze:

  • Definirea și datele
    Deocamdată, vrem să ne jucăm niște date statice - un obiect care conține valori codificate în mod tare despre diferite aspecte ale vremii va fi bine! Creăm un obiect numit weather_data și îl returnăm din funcția data() a App.vue . Obiectul weather_data este prezentat în fragmentul de mai jos:
 weather_data: { location: "California", temperature: { current: "35 C", }, highlights: { uvindex: "3", windstatus: { speed: "20 km/h", direction: "NE", }, visibility: "12 km", }, },
  • Transmiterea datelor de la părinte
    Pentru a transmite datele, avem nevoie de o destinație unde vrem să trimitem datele! În acest caz, destinația este componenta Content.vue , iar modalitatea de implementare a acesteia este:
    • Atribuiți obiectul weather_data unui atribut personalizat al etichetei <Content>
    • Leagă atributul cu datele utilizând directiva v-bind : furnizată de Vue.js, care face ca valoarea atributului să fie dinamică (răspunzând la modificările făcute în datele originale).
       <Content v-bind:weather_data=“weather_data”></Content>

Definirea și transmiterea datelor se gestionează în partea sursă a strângerii de mână, care în cazul nostru este fișierul App.vue .

Codul pentru fișierul App.vue , în starea sa actuală, este prezentat mai jos:

 <template> <div> <p>This component's code is in {{ filename }}</p> <Content v-bind:weather_data="weather_data"></Content> </div> </template> <script> import Content from './components/Content.vue' export default { name: 'app', components: { 'Content': Content }, data () { return { filename: 'App.vue', weather_data: { location: "California", temperature: { current: "35 C", }, highlights: { uvindex: "3", windstatus: { speed: "20 km/h", direction: "NE", }, visibility: "12 km", }, }, } }, methods: { }, computed: { }, } </script> <style> </style> 
O captură de ecran a browserului cu mesajul „Codul acestei componente este în App.vue. Aceste componente secundare ale Content.vue sunt: ​​TempVarChart.vue, Highlights.vue”
(Previzualizare mare)

Cu datele definite și transmise de la sursă (componenta părinte), acum este responsabilitatea copilului să primească datele și să le reda în mod corespunzător, așa cum este explicat în următorii doi pași.

  • Primirea datelor de către copil
    Componenta copil, în acest caz Content.vue , trebuie să primească obiectul weather_data trimis de componenta părinte App.vue . Vue.js oferă un mecanism pentru a face acest lucru — tot ce aveți nevoie este un obiect matrice numit props , definit în obiectul implicit exportat de Content.vue . Fiecare element al matricei de props este un nume al obiectelor de date pe care dorește să le primească de la părintele său. Deocamdată, singurul obiect de date pe care ar trebui să-l primească este weather_data de la App.vue. Astfel, matricea de props arată astfel:
 <template> // HTML template code here </template> <script> export default { props: ["weather_data"], data () { return { // data here } }, } </script> <style> // component specific CSS here </style>
  • Redarea datelor în pagină
    Acum că ne-am asigurat că primim datele, ultima sarcină pe care trebuie să o îndeplinim este să redăm datele. Pentru acest exemplu, vom arunca direct datele primite pe pagina web, doar pentru a ilustra tehnica. Cu toate acestea, în aplicațiile reale (cum ar fi cea pe care urmează să o construim), datele trec în mod normal printr-o mulțime de procesări și numai părțile relevante ale acestora sunt afișate în moduri care se potrivesc scopului. De exemplu, în acest proiect vom obține în cele din urmă date brute din API-ul meteo, le vom curăța și formata, vom alimenta datele structurilor de date necesare pentru diagrame și apoi le vom vizualiza. Oricum, pentru a afișa dump-ul de date brute, vom folosi doar parantezele {{ }} pe care Vue le înțelege, așa cum se arată în fragmentul de mai jos:
 <template> <div> // other template code here {{ weather_data }} </div> </template>

Acum este timpul să asimilați toate părțile. Codul pentru Content.vue — în starea sa actuală — este prezentat mai jos:

 <template> <div> <p>This child components of Content.vue are:</p> <ul> <li v-for="child in childComponents">{{ child }}</li> </ul> {{ weather_data }} </div> </template> <script> export default { props: ["weather_data"], data () { return { childComponents: ['TempVarChart.vue', 'Highlights.vue'] } }, methods: { }, computed: { }, } </script> <style> #pagecontent { border: 1px solid black; padding: 2px; } </style> 
O captură de ecran a browserului cu rezultatul codului furnizat
(Previzualizare mare)

După ce ați făcut modificările discutate mai sus, reîmprospătați pagina web în browser și vedeți cum arată. Luați un moment pentru a aprecia complexitatea cu care se ocupă Vue - dacă modificați obiectul weather_data în App.vue , acesta este transmis în tăcere către Content.vue și, eventual, către browserul care afișează pagina web! Încercați să modificați valoarea pentru locația cheii.

Deși am aflat despre elementele de recuzită și legarea de date folosind date statice, vom folosi date dinamice colectate folosind API-uri web în aplicație și vom schimba codul în consecință .

rezumat

Înainte de a trece la restul fișierelor .vue , să rezumam ceea ce am învățat în timp ce am scris codul pentru App.vue și components/Content.vue :

  • Fișierul App.vue este ceea ce numim componenta rădăcină - cea care se află în partea de sus a ierarhiei componentelor. Restul fișierelor .vue reprezintă componente care sunt copilul său direct, nepotul și așa mai departe.
  • Fișierul Content.vue este o componentă inactivă — responsabilitatea sa este de a transmite datele la nivelurile de mai jos și de a menține ierarhia structurală, astfel încât codul nostru să rămână în concordanță cu filozofia „*ceea ce vedem este ceea ce implementăm*”.
  • Relația părinte-copil a componentei nu se întâmplă din neașteptate — trebuie să înregistrați o componentă (fie global, fie local, în funcție de utilizarea intenționată a componentei) și apoi să o plasați folosind etichete HTML personalizate (a căror ortografie este exactă). identic cu cel al denumirilor cu care au fost înregistrate componentele).
  • Odată înregistrate și imbricate, datele sunt transmise de la componentele părinte la componentele secundare, iar fluxul nu este niciodată invers (lucruri rele se vor întâmpla dacă arhitectura proiectului permite backflow). Componenta părinte este sursa relativă a datelor și transmite datele relevante copiilor săi folosind directiva v-bind pentru atributele elementelor HTML personalizate. Copilul primește datele destinate lui folosind recuzită și apoi decide singur ce să facă cu datele.

Pentru restul componentelor, nu ne vom răsfăța cu explicații detaliate - vom scrie doar codul pe baza învățăturilor din rezumatul de mai sus. Codul va fi de la sine înțeles și, dacă sunteți confuz cu privire la ierarhie, consultați diagrama de mai jos:

O diagramă care explică ierarhia codului
(Previzualizare mare)

Diagrama spune că TempVarChart.vue și Highlights.vue sunt copilul direct al Content.vue . Astfel, ar putea fi o idee bună să pregătim Content.vue pentru trimiterea datelor către acele componente, ceea ce facem folosind codul de mai jos:

 <template> <div> <p>This child components of Content.vue are:</p> <ul> <li v-for="child in childComponents">{{ child }}</li> </ul> {{ weather_data }} <temp-var-chart :tempVar="tempVar"></temp-var-chart> <today-highlights :highlights="highlights"></today-highlights> </div> </template> <script> import TempVarChart from './TempVarChart.vue' import Highlights from './Highlights.vue' export default { props: ["weather_data"], components: { 'temp-var-chart': TempVarChart, 'today-highlights': Highlights }, data () { return { childComponents: ['TempVarChart.vue', 'Highlights.vue'], tempVar: this.weather_data.temperature, highlights: this.weather_data.highlights, } }, methods: { }, computed: { }, } </script> <style> </style>

Odată ce salvați acest cod, veți primi erori - nu vă faceți griji, este de așteptat. Acesta va fi remediat odată ce aveți restul fișierelor componente pregătite. Dacă vă deranjează să nu puteți vedea rezultatul, comentați liniile care conțin etichetele de elemente personalizate <temp-var-chart> și <today-highlights> .

Pentru această secțiune, acesta este codul final al Content.vue . Pentru restul acestei secțiuni, ne vom referi la acest cod și nu la cele anterioare pe care le-am scris pentru învățare.

src/components/TempVarChart.vue

Odată cu componenta sa părinte Content.vue care transmite datele, TempVarChart.vue trebuie configurat pentru a primi și reda datele, așa cum se arată în codul de mai jos:

 <template> <div> <p>Temperature Information:</p> {{ tempVar }} </div> </template> <script> export default { props: ["tempVar"], data () { return { } }, methods: { }, computed: { }, } </script> <style> </style>

src/components/Highlights.vue

Această componentă va primi și date de la App.vue — componenta sa părinte. După aceea, ar trebui să fie conectat cu componentele sale secundare, iar datele relevante ar trebui să le fie transmise.

Să vedem mai întâi codul pentru primirea datelor de la părinte:

 <template> <div> <p>Weather Highlights:</p> {{ highlights }} </div> </template> <script> export default { props: ["highlights"], data () { return { } }, methods: { }, computed: { }, } </script> <style> </style>

În acest moment, pagina web arată ca imaginea de mai jos:

Rezultatul codului afișat în browser
(Previzualizare mare)

Acum trebuie să modificăm codul Highlights.vue pentru a înregistra și a cuibărește componentele sale copil, urmat de transmiterea datelor copiilor. Codul pentru acesta este următorul:

 <template> <div> <p>Weather Highlights:</p> {{ highlights }} <uv-index :highlights="highlights"></uv-index> <visibility :highlights="highlights"></visibility> <wind-status :highlights="highlights"></wind-status> </div> </template> <script> import UVIndex from './UVIndex.vue'; import Visibility from './Visibility.vue'; import WindStatus from './WindStatus.vue'; export default { props: ["highlights"], components: { 'uv-index': UVIndex, 'visibility': Visibility, 'wind-status': WindStatus, }, data () { return { } }, methods: { }, computed: { }, } </script> <style> </style>

Odată ce salvați codul și vedeți pagina web, este de așteptat să vedeți erori în instrumentul Developer Console furnizat de browser; acestea apar pentru că, deși Highlights.vue trimite date, nimeni nu le primește. Încă trebuie să scriem codul pentru copiii din Highlights.vue .

Observați că nu am făcut prea mult din procesarea datelor, adică nu am extras factorii individuali ai datelor meteo care intră sub secțiunea Repere a tabloului de bord. Am fi putut face asta în funcția data() , dar am preferat să păstrăm Highlights.vue o componentă stupidă care doar transmite întregul dump de date pe care îl primește fiecăruia dintre copii, care apoi dețin propriile extrase ceea ce este necesar pentru ei. . Cu toate acestea, vă încurajăm să încercați extragerea datelor în Highlights.vue și să trimiteți date relevante către fiecare componentă copil - este totuși un exercițiu de bună practică!

src/components/UVIndex.vue

Codul pentru această componentă primește descărcarea datelor de la Highlights.vue , extrage datele pentru UV Index și le redă pe pagină.

 <template> <div> <p>UV Index: {{ uvindex }}</p> </div> </template> <script> export default { props: ["highlights"], data () { return { uvindex: this.highlights.uvindex } }, methods: { }, computed: { }, } </script> <style> </style>

src/components/Visibility.vue

Codul pentru această componentă primește descărcarea datelor de la Highlights.vue , extrage datele pentru Vizibilitate și le redă pe pagină.

 <template> <div> <p>Visibility: {{ visibility }}</p> </div> </template> <script> export default { props: ["highlights"], data () { return { visibility: this.highlights.visibility, } }, methods: { }, computed: { }, } </script> <style> </style>

src/components/WindStatus.vue

Codul pentru această componentă primește descărcarea datelor de la Highlights.vue , extrage datele pentru starea vântului (viteză și direcție) și le redă pe pagină.

 <template> <div> <p>Wind Status:</p> <p>Speed — {{ speed }}; Direction — {{ direction }}</p> </div> </template> <script> export default { props: ["highlights"], data () { return { speed: this.highlights.windstatus.speed, direction: this.highlights.windstatus.direction } }, methods: { }, computed: { }, } </script> <style> </style>

După ce ați adăugat codul pentru toate componentele, aruncați o privire pe pagina web din browser.

Rezultatul codului afișat în browser
(Previzualizare mare)

Nu pentru a descuraja, dar toată această trudă a fost doar pentru a lega componentele în ierarhie și pentru a testa dacă fluxul de date are loc sau nu între ele! În secțiunea următoare, vom arunca cea mai mare parte a codului pe care l-am scris până acum și vom adăuga mult mai multe legate de proiectul real. Cu toate acestea, cu siguranță vom păstra structura și imbricarea componentelor; Învățăturile din această secțiune ne vor permite să construim un tablou de bord decent cu Vue.js.

4. Achiziția și prelucrarea datelor

Vă amintiți obiectul weather_data din App.vue ? Avea niște date codificate greu pe care le-am folosit pentru a testa dacă toate componentele funcționează corect și, de asemenea, pentru a vă ajuta să învățați câteva aspecte de bază ale aplicației Vue, fără să vă blocați în detaliile datelor din lumea reală. Cu toate acestea, acum este timpul să ne renunțăm și să ieșim în lumea reală, unde datele din API vor domina cea mai mare parte a codului nostru.

Pregătirea componentelor copil pentru a primi și procesa date reale

În această secțiune, veți obține descărcarea codului pentru toate componentele, cu excepția App.vue . Codul se va ocupa de primirea datelor reale de la App.vue (spre deosebire de codul pe care l-am scris în secțiunea anterioară pentru a primi și a reda date fictive).

Vă recomandăm să citiți cu atenție codul fiecărei componente, astfel încât să vă faceți o idee despre ce date se așteaptă fiecare dintre acele componente și, în cele din urmă, le va folosi în vizualizare.

O parte din cod și structura generală vor fi similare cu cele pe care le-ați văzut în structura anterioară - așa că nu vă veți confrunta cu ceva drastic diferit. Totuși, diavolul este în detalii! Așa că examinează cu atenție codul și, când le-ai înțeles rezonabil de bine, copiază codul în fișierele componente respective din proiectul tău.

Notă : Toate componentele din această secțiune se află în directorul src/components/ . Deci, de fiecare dată, calea nu va fi menționată - doar numele fișierului .vue va fi menționat pentru a identifica componenta.

Conținut.vue

 <template> <div> <temp-var-chart :tempVar="tempVar"></temp-var-chart> <today-highlights :highlights="highlights"></today-highlights> </div> </template> <script> import TempVarChart from './TempVarChart.vue'; import Highlights from './Highlights.vue'; export default { props: ['highlights', 'tempVar'], components: { 'temp-var-chart': TempVarChart, 'today-highlights': Highlights }, } </script>

Următoarele modificări au fost făcute față de codul anterior:

  • În <template> , textul și datele din {{ }} au fost eliminate, deoarece acum doar primim date și le transmitem copiilor, fără nicio redare specifică a acestei componente.
  • În export default {} :
    • Elementele de props au fost modificate pentru a se potrivi cu obiectele de date care vor fi trimise de părinte: App.vue . Motivul pentru schimbarea elementelor de recuzită este că App.vue în sine va afișa unele dintre datele pe care le achiziționează din API-ul meteo și din alte resurse online, pe baza interogării de căutare a utilizatorului, și va transmite restul datelor. În codul fals pe care l-am scris mai devreme, App.vue transmitea întregul dump de date simulat, fără nicio discriminare, iar elementele de recuzită ale Content.vue erau configurate în consecință.
    • Funcția data() nu returnează acum nimic, deoarece nu facem nicio manipulare a datelor în această componentă.

TempVarChart.vue

Această componentă ar trebui să primească proiecții detaliate de temperatură pentru restul zilei curente și, eventual, să le afișeze folosind FusionCharts. Dar pentru moment, le vom afișa doar ca text pe pagina web.

 <template> <div> {{ tempVar.tempToday }} </div> </template> <script> export default { props: ["tempVar"], components: {}, data() { return { }; }, methods: { }, }; </script> <style> </style>

Repere.vue

 <template> <div> <uv-index :highlights="highlights"></uv-index> <visibility :highlights="highlights"></visibility> <wind-status :highlights="highlights"></wind-status> </div> </template> <script> import UVIndex from './UVIndex.vue'; import Visibility from './Visibility.vue'; import WindStatus from './WindStatus.vue'; export default { props: ["highlights"], components: { 'uv-index': UVIndex, 'visibility': Visibility, 'wind-status': WindStatus, }, data () { return { } }, methods: { }, computed: { }, } </script> <style> </style>

Modificările făcute din codul anterior sunt:

  • În <template> , textul și datele din {{ }} au fost eliminate, deoarece aceasta este o componentă stupidă, la fel ca Content.vue , a cărui singură sarcină este să transmită datele copiilor menținând în același timp ierarhia structurală. Rețineți că componentele stupide precum Highlights.vue și Content.vue există pentru a menține paritatea între structura vizuală a tabloului de bord și codul pe care îl scriem.

UVIndex.vue

Modificările aduse codului anterior sunt următoarele:

  • În <template> și <style> , div id -ul a fost schimbat în uvIndex , care este mai ușor de citit.
  • În export default {} , funcția data() returnează acum un obiect șir uvIndex , a cărui valoare este extrasă din obiectul de evidențiere primit de componentă folosind props . Acest uvIndex este acum folosit temporar pentru a afișa valoarea ca text în <template> . Mai târziu, vom introduce această valoare la structura de date potrivită pentru redarea unei diagrame.

Vizibilitate.vue

 <template> <div> <p>Visibility: {{ visibility }}</p> </div> </template> <script> export default { props: ["highlights"], data () { return { visibility: this.highlights.visibility.toString() } }, methods: { }, computed: { }, } </script> <style> </style>

Singura modificare făcută în acest fișier (față de codul său anterior) este că definiția obiectului de visibility returnat de funcția data() conține acum toString() la sfârșitul său, deoarece valoarea primită de la părinte va fi flotantă. număr punct, care trebuie convertit în șir.

WindStatus.vue

 <template> <div> <p>Wind Speed — {{ windSpeed }}</p> <p>Wind Direction — {{ derivedWindDirection }}, or {{ windDirection }} degree clockwise with respect to true N as 0 degree.</p> </div> </template> <script> export default { props: ["highlights"], data () { return { windSpeed: this.highlights.windStatus.windSpeed, derivedWindDirection: this.highlights.windStatus.derivedWindDirection, windDirection: this.highlights.windStatus.windDirection } }, methods: { }, computed: { }, } </script> <style> </style>

Modificările aduse codului anterior sunt următoarele:

  • Pe tot parcursul fișierului, windstatus a fost redenumit windStatus , pentru a promova lizibilitatea și, de asemenea, pentru a fi sincronizat cu obiectul evidențiat pe care App.vue oferă cu datele reale.
  • S-au făcut modificări similare de denumire pentru viteză și direcție - cele noi sunt windSpeed ​​și windDirection .
  • A intrat în joc un nou obiect derivedWindDirection (furnizat și de App.vue în pachetul de evidențieri).

Pentru moment, datele primite sunt redate ca text; ulterior, va fi conectat la structura de date necesară pentru vizualizare.

Testarea cu date false

Apelarea în mod repetat la date fictive ar putea fi puțin frustrantă pentru dvs., dar există câteva motive întemeiate în spatele acesteia:

  • Am făcut o mulțime de modificări la codul fiecărei componente și este o idee bună să testăm dacă acele modificări încalcă codul. Cu alte cuvinte, ar trebui să verificăm dacă fluxul de date este intact, acum că suntem pe cale să trecem la părți mai complexe ale proiectului.
  • Datele reale din API-ul meteo online vor avea nevoie de multă masaj și ar putea fi copleșitor pentru dvs. să jonglați între codul pentru achiziția și procesarea datelor și codul pentru fluxul fluid al datelor de-a lungul componentelor. Ideea este să ținem sub control cuantumul complexității, astfel încât să avem o mai bună înțelegere a erorilor cu care ne-am putea confrunta.

În această secțiune, ceea ce facem este, în esență, să codificăm unele date json în App.vue , care vor fi, evident, înlocuite cu date live în viitorul apropiat. Există multe asemănări între structura json inactivă și structura json pe care o vom folosi pentru datele reale. Prin urmare, vă oferă, de asemenea, o idee aproximativă despre ce să vă așteptați de la datele reale, odată ce le întâlnim.

Cu toate acestea, admitem că aceasta este departe de abordarea ideală pe care s-ar putea adopta atunci când construiești un astfel de proiect de la zero. În lumea reală, veți începe adesea cu sursa reală de date, veți juca puțin cu ea pentru a înțelege ce poate și ar trebui făcut pentru a o îmblânzi și apoi vă veți gândi la structura de date json adecvată pentru a captura informațiile relevante. Te-am ferit în mod intenționat de toate acele lucrări murdare, deoarece te duce mai departe de obiectiv - învățând cum să folosești Vue.js și FusionCharts pentru a construi un tablou de bord.

Să trecem acum la noul cod pentru App.vue:

 <template> <div> <dashboard-content :highlights="highlights" :tempVar="tempVar"></dashboard-content> </div> </template> <script> import Content from './components/Content.vue' export default { name: 'app', components: { 'dashboard-content': Content }, data () { return { tempVar: { tempToday: [ {hour: '11.00 AM', temp: '35'}, {hour: '12.00 PM', temp: '36'}, {hour: '1.00 PM', temp: '37'}, {hour: '2.00 PM', temp: '38'}, {hour: '3.00 PM', temp: '36'}, {hour: '4.00 PM', temp: '35'}, ], }, highlights: { uvIndex: 4, visibility: 10, windStatus: { windSpeed: '30 km/h', windDirection: '30', derivedWindDirection: 'NNE', }, }, } }, methods: { }, computed: { }, } </script> <style> </style>

Modificările aduse codului față de versiunea anterioară sunt următoarele:

  • Numele componentei secundare a fost schimbat în tabloul de bord și, în consecință, elementul HTML personalizat din <template> a fost revizuit. Rețineți că acum avem două atribute — highlights și tempVar — în loc de un singur atribut pe care l-am folosit mai devreme cu elementul personalizat. În consecință, datele asociate cu aceste atribute s-au schimbat și ele. Ceea ce este interesant aici este că putem folosi directiva v-bind: sau prescurtarea acesteia : (cum am făcut aici), cu mai multe atribute ale unui element HTML personalizat!
  • Funcția data() returnează acum obiectul nume de filename (care exista mai devreme), împreună cu două obiecte noi (în loc de vechiul weather_data ): tempVar și highlights . Structura json este adecvată pentru codul pe care l-am scris în componentele copil, astfel încât acestea să poată extrage bucățile de date de care au nevoie din depozite. Structurile sunt destul de explicite și vă puteți aștepta să fie destul de asemănătoare atunci când ne ocupăm de date live. Cu toate acestea, schimbarea semnificativă pe care o veți întâlni este absența hardcoding-ului (evident, nu-i așa) — vom lăsa valorile necompletate ca stare implicită și vom scrie cod pentru a le actualiza dinamic pe baza valorilor pe care le vom primi de la API meteo.

Ați scris mult cod în această secțiune, fără a vedea rezultatul real. Înainte de a continua, aruncați o privire la browser (reporniți serverul cu npm run dev , dacă este necesar) și bucurați-vă de gloria realizării dvs. Pagina web pe care ar trebui să o vedeți în acest moment arată ca imaginea de mai jos:

Rezultatul codului afișat în browser
(Previzualizare mare)

Cod pentru achiziția și prelucrarea datelor

Această secțiune va fi punctul central al proiectului, cu tot codul care urmează să fie scris în App.vue pentru următoarele:

  • Introducerea locației de la utilizator — o casetă de introducere și un buton de îndemn sunt suficiente;
  • Funcții utilitare pentru diverse sarcini; aceste funcții vor fi apelate mai târziu în diferite părți ale codului componentei;
  • Obținerea de date detaliate de localizare geografică din API-ul Google Maps pentru JavaScript;
  • Obținerea de date detaliate despre vreme din API-ul Dark Sky;
  • Formatarea și procesarea datelor de geolocalizare și vreme, care vor fi transmise componentelor copil.

Subsecțiunile care urmează ilustrează modul în care putem implementa sarcinile stabilite pentru noi în punctele de mai sus. Cu unele excepții, majoritatea vor urma secvența.

Intrare de la utilizator

Este destul de evident că acțiunea începe atunci când utilizatorul furnizează numele locului pentru care trebuie afișate datele meteo. Pentru ca acest lucru să se întâmple, trebuie să implementăm următoarele:

  • O casetă de introducere pentru introducerea locației;
  • Un buton de trimitere care spune aplicației noastre că utilizatorul a intrat în locație și că este timpul să facem restul. De asemenea, vom implementa comportamentul atunci când procesarea începe la apăsarea Enter .

Codul pe care îl arătăm mai jos va fi limitat la partea șablonului HTML din App.vue . Vom menționa doar numele metodei asociate cu evenimentele de clic și le vom defini mai târziu în obiectul metode al <scriptului> din App.vue.

 <div> <input type="text" ref="input" placeholder="Location?" @keyup.enter="organizeAllDetails" > <button @click="organizeAllDetails"> <img src="./assets/Search.svg" width="24" height="24"> </button> </div>

Plasarea fragmentului de mai sus la locul potrivit este trivială - vă lăsăm pe seama dvs. Cu toate acestea, părțile interesante ale fragmentului sunt:

  • @keyup.enter="organizeAllDetails"
  • @click="organizeAllDetails"

După cum știți din secțiunile anterioare, @ este prescurtarea lui Vue pentru directiva v-on :, care este asociată cu un anumit eveniment. Lucrul nou este „ organizeAllDetails ” - nu este altceva decât metoda care se va declanșa odată ce evenimentele (apăsând Enter sau făcând clic pe butonul) au loc. Încă nu am definit metoda, iar puzzle-ul va fi complet până la sfârșitul acestei secțiuni.

Afișarea informațiilor text controlată de App.vue

Odată ce intrarea utilizatorului declanșează acțiunea și o mulțime de date sunt achiziționate de la API-uri, întâlnim întrebarea inevitabilă - „Ce să facem cu toate aceste date?”. Evident, este nevoie de ceva masaj de date, dar asta nu răspunde pe deplin la întrebarea noastră! Trebuie să decidem care este utilizarea finală a datelor sau, mai direct, care sunt entitățile care primesc diferite bucăți din datele achiziționate și prelucrate?

Componentele secundare ale App.vue , pe baza ierarhiei și scopului lor, sunt concurenții de primă linie pentru cea mai mare parte a datelor. Cu toate acestea, vom avea, de asemenea, unele date care nu aparțin niciunuia dintre acele componente secundare, dar sunt destul de informative și fac tabloul de bord complet. Le putem folosi bine dacă le afișăm ca informații text controlate direct de App.vue , în timp ce restul datelor sunt transmise copilului pentru a fi afișate în cele din urmă ca diagrame frumoase.

Având în vedere acest context, să ne concentrăm asupra codului pentru stabilirea stadiului utilizării datelor text. Este un șablon HTML simplu în acest moment, pe care datele vor veni în cele din urmă și vor sta.

 <div> <div class="wrapper-left"> <div> {{ currentWeather.temp }} <span>°C</span> </div> <div>{{ currentWeather.summary }}</div> <div class="temp-max-min"> <div class="max-desc"> <div> <i>▲</i> {{ currentWeather.todayHighLow.todayTempHigh }} <span>°C</span> </div> <div>at {{ currentWeather.todayHighLow.todayTempHighTime }}</div> </div> <div class="min-desc"> <div> <i>▼</i> {{ currentWeather.todayHighLow.todayTempLow }} <span>°C</span> </div> <div>at {{ currentWeather.todayHighLow.todayTempLowTime }}</div> </div> </div> </div> <div class="wrapper-right"> <div class="date-time-info"> <div> <img src="./assets/calendar.svg" width="20" height="20"> {{ currentWeather.time }} </div> </div> <div class="location-info"> <div> <img src="./assets/location.svg" width="10.83" height="15.83" > {{ currentWeather.full_location }} <div class="mt-1"> Lat: {{ currentWeather.formatted_lat }} <br> Long: {{ currentWeather.formatted_long }} </div> </div> </div> </div> </div>

În fragmentul de mai sus, ar trebui să înțelegeți următoarele:

  • Lucrurile din {{ }} — sunt modalitatea Vue de a insera date dinamice în șablonul HTML, înainte ca acesta să fie randat în browser. I-ai mai întâlnit și nu este nimic nou sau surprinzător. Rețineți că aceste obiecte de date provin din metoda data() din obiectul export default() al App.vue . Au valori implicite pe care le vom seta în funcție de cerințele noastre și apoi scriem anumite metode pentru a popula obiectele cu date API reale.

Nu vă faceți griji că nu vedeți modificările în browser - datele nu sunt încă definite și este firesc ca Vue să nu redea lucruri pe care nu le cunoaște. Cu toate acestea, odată ce datele sunt setate (și deocamdată, puteți chiar verifica prin codificarea datelor), datele text vor fi controlate de App.vue .

Metoda data()

Metoda data() este o construcție specială în fișierele .vue - conține și returnează obiecte de date care sunt atât de cruciale pentru aplicație. Rețineți structura generică a părții <script> în orice fișier .vue - conține aproximativ următoarele:

 <script> // import statements here export default { // name, components, props, etc. data() { return { // the data that is so crucial for the application is defined here. // the data objects will have certain default values chosen by us. // The methods that we define below will manipulate the data. // Since the data is bounded to various attributes and directives, they // will update as and when the values of the data objects change. } }, methods: { // methods (objects whose values are functions) here. // bulk of dynamic stuff (the black magic part) is controlled from here. }, computed: { // computed properties here }, // other objects, as necessary } </script>

Până acum, ați întâlnit numele unora dintre obiectele de date, dar sunt mult mai multe. Cele mai multe dintre ele sunt relevante pentru componentele copil, fiecare dintre ele gestionând un aspect diferit al depozitului de informații meteo. Mai jos este prezentată întreaga metodă data() de care vom avea nevoie pentru acest proiect — veți avea o idee corectă despre ce date așteptăm de la API-uri și cum le diseminăm, pe baza nomenclaturii obiectelor.

 data() { return { weatherDetails: false, location: '', // raw location from input lat: '', // raw latitude from google maps api response long: '', // raw longitude from google maps api response completeWeatherApi: '', // weather api string with lat and long rawWeatherData: '', // raw response from weather api currentWeather: { full_location: '', // for full address formatted_lat: '', // for N/S formatted_long: '', // for E/W time: '', temp: '', todayHighLow: { todayTempHigh: '', todayTempHighTime: '', todayTempLow: '', todayTempLowTime: '' }, summary: '', possibility: '' }, tempVar: { tempToday: [ // gets added dynamically by this.getSetHourlyTempInfoToday() ], }, highlights: { uvIndex: '', visibility: '', windStatus: { windSpeed: '', windDirection: '', derivedWindDirection: '' }, } }; },

După cum puteți vedea, în cele mai multe cazuri, valoarea implicită este goală, deoarece aceasta va fi suficientă în acest moment. Se vor scrie metode pentru manipularea datelor și completarea lor cu valori adecvate, înainte ca acestea să fie redate sau transmise componentelor copil.

Metode în App.vue

Pentru fișierele .vue , metodele sunt scrise în general ca valori ale cheilor imbricate în obiectul methods { } . Rolul lor principal este de a manipula obiectele de date ale componentei. Vom scrie metodele în App.vue ținând cont de aceeași filozofie. Cu toate acestea, în funcție de scopul lor, putem clasifica metodele App.vue în următoarele:

  • Metode de utilitate
  • Metode orientate către acțiune/eveniment
  • Metode de achizitie a datelor
  • Metode de prelucrare a datelor
  • Metode de lipici la nivel înalt

Este important să înțelegeți acest lucru - vă prezentăm metodele pe un platou, deoarece ne-am dat seama deja cum funcționează API-urile, ce date oferă și cum ar trebui să folosim datele în proiectul nostru. Nu este că am scos din aer metodele și am scris niște coduri arcane pentru a trata datele. În scopul învățării, este un exercițiu bun să citiți și să înțelegeți cu sârguință codul pentru metode și date. Cu toate acestea, atunci când te confrunți cu un nou proiect pe care trebuie să-l construiești de la zero, trebuie să faci singur toată munca murdară și asta înseamnă să experimentezi mult cu API-urile - accesul lor programatic și structura lor de date, înainte de a le lipi fără probleme cu datele. structura pe care o cere proiectul dumneavoastră. Nu veți avea nicio mână de mână și vor exista momente frustrante, dar toate acestea fac parte din maturizarea ca dezvoltator.

În următoarele subsecțiuni, vom explica fiecare dintre tipurile de metode și, de asemenea, vom arăta implementarea metodelor aparținând acelei categorii. Numele metodelor sunt destul de explicite în ceea ce privește scopul lor, la fel și implementarea lor, care credem că veți găsi a fi destul de ușor de urmat. Cu toate acestea, înainte de asta, amintiți-vă schema generală a metodelor de scriere în fișierele .vue :

 <script> // import statements here export default { // name, components, props, etc. data() { return { // the data that is so crucial for the application is defined here. } }, methods: { // methods (objects whose values are functions) here. // bulk of dynamic stuff (the black magic part) is controlled from here. method_1: function(arg_1) { }, method_2: function(arg_1, arg_2) { }, method_3: function(arg_1) { }, ……. }, computed: { // computed properties here }, // other objects, as necessary } </script>

Metode de utilitate

Metodele utilitare, așa cum sugerează și numele, sunt metode scrise în primul rând cu scopul de a modulariza codul repetitiv utilizat pentru sarcini marginale. Ele sunt apelate prin alte metode atunci când este necesar. Mai jos sunt prezentate metodele utilitare pentru App.vue :

 convertToTitleCase: function(str) { str = str.toLowerCase().split(' '); for (var i = 0; i < str.length; i++) { str[i] = str[i].charAt(0).toUpperCase() + str[i].slice(1); } return str.join(' '); },
 // To format the “possibility” (of weather) string obtained from the weather API formatPossibility: function(str) { str = str.toLowerCase().split('-'); for (var i = 0; i < str.length; i++) { str[i] = str[i].charAt(0).toUpperCase() + str[i].slice(1); } return str.join(' '); },
 // To convert Unix timestamps according to our convenience unixToHuman: function(timezone, timestamp) { /* READ THIS BEFORE JUDGING & DEBUGGING For any location beyond the arctic circle and the antarctic circle, the goddamn weather api does not return certain keys/values in each of this.rawWeatherData.daily.data[some_array_index]. Due to this, console throws up an error. The code is correct, the problem is with the API. May be later on I will add some padding to tackle missing values. */ var moment = require('moment-timezone'); // for handling date & time var decipher = new Date(timestamp * 1000); var human = moment(decipher) .tz(timezone) .format('llll'); var timeArray = human.split(' '); var timeNumeral = timeArray[4]; var timeSuffix = timeArray[5]; var justTime = timeNumeral + ' ' + timeSuffix; var monthDateArray = human.split(','); var monthDate = monthDateArray[1].trim(); return { fullTime: human, onlyTime: justTime, onlyMonthDate: monthDate }; },
 // To convert temperature from fahrenheit to celcius fahToCel: function(tempInFahrenheit) { var tempInCelcius = Math.round((5 / 9) * (tempInFahrenheit — 32)); return tempInCelcius; },
 // To convert the air pressure reading from millibar to kilopascal milibarToKiloPascal: function(pressureInMilibar) { var pressureInKPA = pressureInMilibar * 0.1; return Math.round(pressureInKPA); },
 // To convert distance readings from miles to kilometers mileToKilometer: function(miles) { var kilometer = miles * 1.60934; return Math.round(kilometer); },
 // To format the wind direction based on the angle deriveWindDir: function(windDir) { var wind_directions_array = [ { minVal: 0, maxVal: 30, direction: 'N' }, { minVal: 31, maxVal: 45, direction: 'NNE' }, { minVal: 46, maxVal: 75, direction: 'NE' }, { minVal: 76, maxVal: 90, direction: 'ENE' }, { minVal: 91, maxVal: 120, direction: 'E' }, { minVal: 121, maxVal: 135, direction: 'ESE' }, { minVal: 136, maxVal: 165, direction: 'SE' }, { minVal: 166, maxVal: 180, direction: 'SSE' }, { minVal: 181, maxVal: 210, direction: 'S' }, { minVal: 211, maxVal: 225, direction: 'SSW' }, { minVal: 226, maxVal: 255, direction: 'SW' }, { minVal: 256, maxVal: 270, direction: 'WSW' }, { minVal: 271, maxVal: 300, direction: 'W' }, { minVal: 301, maxVal: 315, direction: 'WNW' }, { minVal: 316, maxVal: 345, direction: 'NW' }, { minVal: 346, maxVal: 360, direction: 'NNW' } ]; var wind_direction = ''; for (var i = 0; i < wind_directions_array.length; i++) { if ( windDir >= wind_directions_array[i].minVal && windDir <= wind_directions_array[i].maxVal ) { wind_direction = wind_directions_array[i].direction; } } return wind_direction; },

Deși nu l-am implementat, puteți elimina metodele utilitare din fișierul .vue și le puteți pune într-un fișier JavaScript separat. Tot ce trebuie să faceți este să importați fișierul .js la începutul părții de script din fișierul .vue și ar trebui să fiți gata. O astfel de abordare funcționează foarte bine și menține codul curat, mai ales în aplicațiile mari în care ați putea folosi o mulțime de metode care sunt mai bine grupate în funcție de scopul lor. Puteți aplica această abordare la toate grupurile de metode enumerate în acest articol și puteți vedea efectul în sine. Cu toate acestea, vă sugerăm să faceți acest exercițiu odată ce ați urmat cursul prezentat aici, astfel încât să aveți o înțelegere generală a tuturor părților care lucrează în sincronizare completă și, de asemenea, să aveți o bucată de software de lucru la care vă puteți referi, odată ce ceva pauze în timpul experimentului.

Metode orientate pe acțiune/eveniment

Aceste metode sunt în general executate atunci când trebuie să întreprindem o acțiune corespunzătoare unui eveniment. În funcție de caz, evenimentul poate fi declanșat dintr-o interacțiune cu utilizatorul sau prin programare. În fișierul App.vue , aceste metode se află sub metodele utilitare.

 makeInputEmpty: function() { this.$refs.input.value = ''; },
 makeTempVarTodayEmpty: function() { this.tempVar.tempToday = []; },
 detectEnterKeyPress: function() { var input = this.$refs.input; input.addEventListener('keyup', function(event) { event.preventDefault(); var enterKeyCode = 13; if (event.keyCode === enterKeyCode) { this.setHitEnterKeyTrue(); } }); },
 locationEntered: function() { var input = this.$refs.input; if (input.value === '') { this.location = "New York"; } else { this.location = this.convertToTitleCase(input.value); } this.makeInputEmpty(); this.makeTempVarTodayEmpty(); },

Un lucru interesant în unele dintre fragmentele de cod de mai sus este utilizarea lui $ref . În termeni simpli, este modul Vue de a asocia instrucțiunea de cod care o conține, cu constructul HTML pe care se presupune că îl afectează (pentru mai multe informații, citiți ghidul oficial). De exemplu, metodele makeInputEmpty() și detectEnterKeyPress() afectează caseta de intrare, deoarece în HTML-ul casetei de intrare am menționat valoarea atributului ref ca input .

Metode de achizitie a datelor

Utilizăm următoarele două API-uri în proiectul nostru:

  • API-ul Google Maps Geocoder
    Acest API este pentru a obține coordonatele locației pe care o caută utilizatorul. Veți avea nevoie de o cheie API pentru dvs., pe care o puteți obține urmând documentația din linkul dat. Pentru moment, puteți utiliza cheia API folosită de FusionCharts, dar vă rugăm să nu abuzați de ea și să obțineți o cheie proprie. Ne referim la API-ul JavaScript din index.html al acestui proiect și vom folosi constructorii furnizați de acesta pentru codul nostru din fișierul App.vue .
  • API-ul Dark Sky Weather
    Acest API este pentru obținerea datelor meteo corespunzătoare coordonatelor. Cu toate acestea, nu îl vom folosi direct; îl vom include într-o adresă URL care redirecționează printr-unul dintre serverele FusionCharts. Motivul este că, dacă trimiteți o solicitare GET către API dintr-o aplicație în întregime client, cum ar fi a noastră, rezultă o eroare CORS frustrantă (mai multe informații aici și aici).

Notă importantă : Deoarece am folosit API-urile Google Maps și Dark Sky, ambele aceste API-uri au propriile lor chei API pe care vi le-am împărtășit în acest articol. Acest lucru vă va ajuta să vă concentrați mai degrabă pe evoluțiile la nivel client decât pe durerea de cap a implementării backend. Cu toate acestea, vă recomandăm să vă creați propriile chei , deoarece cheile noastre API-uri vor veni cu limite, iar dacă aceste limite depășesc, nu veți putea încerca singur aplicația.

Pentru Google Maps, accesați acest articol pentru a obține cheia API. Pentru Dark Sky API, vizitați https://darksky.net/dev pentru a vă crea cheia API și punctele finale respective.

Având în vedere contextul, să vedem implementarea metodelor de achiziție de date pentru proiectul nostru.

 getCoordinates: function() { this.locationEntered(); var loc = this.location; var coords; var geocoder = new google.maps.Geocoder(); return new Promise(function(resolve, reject) { geocoder.geocode({ address: loc }, function(results, status) { if (status == google.maps.GeocoderStatus.OK) { this.lat = results[0].geometry.location.lat(); this.long = results[0].geometry.location.lng(); this.full_location = results[0].formatted_address; coords = { lat: this.lat, long: this.long, full_location: this.full_location }; resolve(coords); } else { alert("Oops! Couldn't get data for the location"); } }); }); },
 /* The coordinates that Google Maps Geocoder API returns are way too accurate for our requirements. We need to bring it into shape before passing the coordinates on to the weather API. Although this is a data processing method in its own right, we can't help mentioning it right now, because the data acquisition method for the weather API has dependency on the output of this method. */ setFormatCoordinates: async function() { var coordinates = await this.getCoordinates(); this.lat = coordinates.lat; this.long = coordinates.long; this.currentWeather.full_location = coordinates.full_location; // Remember to beautify lat for N/S if (coordinates.lat > 0) { this.currentWeather.formatted_lat = (Math.round(coordinates.lat * 10000) / 10000).toString() + '°N'; } else if (coordinates.lat < 0) { this.currentWeather.formatted_lat = (-1 * (Math.round(coordinates.lat * 10000) / 10000)).toString() + '°S'; } else { this.currentWeather.formatted_lat = ( Math.round(coordinates.lat * 10000) / 10000 ).toString(); } // Remember to beautify long for N/S if (coordinates.long > 0) { this.currentWeather.formatted_long = (Math.round(coordinates.long * 10000) / 10000).toString() + '°E'; } else if (coordinates.long < 0) { this.currentWeather.formatted_long = (-1 * (Math.round(coordinates.long * 10000) / 10000)).toString() + '°W'; } else { this.currentWeather.formatted_long = ( Math.round(coordinates.long * 10000) / 10000 ).toString(); } },
 /* This method dynamically creates the the correct weather API query URL, based on the formatted latitude and longitude. The complete URL is then fed to the method querying for weather data. Notice that the base URL used in this method (without the coordinates) points towards a FusionCharts server — we must redirect our GET request to the weather API through a server to avoid the CORS error. */ fixWeatherApi: async function() { await this.setFormatCoordinates(); var weatherApi = 'https://csm.fusioncharts.com/files/assets/wb/wb-data.php?src=darksky&lat=' + this.lat + '&long=' + this.long; this.completeWeatherApi = weatherApi; },
 fetchWeatherData: async function() { await this.fixWeatherApi(); var axios = require('axios'); // for handling weather api promise var weatherApiResponse = await axios.get(this.completeWeatherApi); if (weatherApiResponse.status === 200) { this.rawWeatherData = weatherApiResponse.data; } else { alert('Hmm... Seems like our weather experts are busy!'); } },

Through these methods, we have introduced the concept of async-await in our code. If you have been a JavaScript developer for some time now, you must be familiar with the callback hell, which is a direct consequence of the asynchronous way JavaScript is written. ES6 allows us to bypass the cumbersome nested callbacks, and our code becomes much cleaner if we write JavaScript in a synchronous way, using the async-await technique. However, there is a downside. It takes away the speed that asynchronous code gives us, especially for the portions of the code that deals with data being exchanged over the internet. Since this is not a mission-critical application with low latency requirements, and our primary aim is to learn stuff, the clean code is much more preferable over the slightly fast code.

Data Processing Methods

Now that we have the methods that will bring the data to us, we need to prepare the ground for properly receiving and processing the data. Safety nets must be cast, and there should be no spills — data is the new gold (OK, that might be an exaggeration in our context)! Enough with the fuss, let's get to the point.

Technically, the methods we implement in this section are aimed at getting the data out of the acquisition methods and the data objects in App.vue , and sometimes setting the data objects to certain values that suits the purpose.

getTimezone: function() { return this.rawWeatherData.timezone; },
 getSetCurrentTime: function() { var currentTime = this.rawWeatherData.currently.time; var timezone = this.getTimezone(); this.currentWeather.time = this.unixToHuman( timezone, currentTime ).fullTime; },
 getSetSummary: function() { var currentSummary = this.convertToTitleCase( this.rawWeatherData.currently.summary ); if (currentSummary.includes(' And')) { currentSummary = currentSummary.replace(' And', ','); } this.currentWeather.summary = currentSummary; },
 getSetPossibility: function() { var possible = this.formatPossibility(this.rawWeatherData.daily.icon); if (possible.includes(' And')) { possible = possible.replace(' And', ','); } this.currentWeather.possibility = possible; },
 getSetCurrentTemp: function() { var currentTemp = this.rawWeatherData.currently.temperature; this.currentWeather.temp = this.fahToCel(currentTemp); },
 getTodayDetails: function() { return this.rawWeatherData.daily.data[0]; },
 getSetTodayTempHighLowWithTime: function() { var timezone = this.getTimezone(); var todayDetails = this.getTodayDetails(); this.currentWeather.todayHighLow.todayTempHigh = this.fahToCel( todayDetails.temperatureMax ); this.currentWeather.todayHighLow.todayTempHighTime = this.unixToHuman( timezone, todayDetails.temperatureMaxTime ).onlyTime; this.currentWeather.todayHighLow.todayTempLow = this.fahToCel( todayDetails.temperatureMin ); this.currentWeather.todayHighLow.todayTempLowTime = this.unixToHuman( timezone, todayDetails.temperatureMinTime ).onlyTime; },
 getHourlyInfoToday: function() { return this.rawWeatherData.hourly.data; },
 getSetHourlyTempInfoToday: function() { var unixTime = this.rawWeatherData.currently.time; var timezone = this.getTimezone(); var todayMonthDate = this.unixToHuman(timezone, unixTime).onlyMonthDate; var hourlyData = this.getHourlyInfoToday(); for (var i = 0; i < hourlyData.length; i++) { var hourlyTimeAllTypes = this.unixToHuman(timezone, hourlyData[i].time); var hourlyOnlyTime = hourlyTimeAllTypes.onlyTime; var hourlyMonthDate = hourlyTimeAllTypes.onlyMonthDate; if (todayMonthDate === hourlyMonthDate) { var hourlyObject = { hour: '', temp: '' }; hourlyObject.hour = hourlyOnlyTime; hourlyObject.temp = this.fahToCel(hourlyData[i].temperature).toString(); this.tempVar.tempToday.push(hourlyObject); /* Since we are using array.push(), we are just adding elements at the end of the array. Thus, the array is not getting emptied first when a new location is entered. to solve this problem, a method this.makeTempVarTodayEmpty() has been created, and called from this.locationEntered(). */ } } /* To cover the edge case where the local time is between 10 — 12 PM, and therefore there are only two elements in the array this.tempVar.tempToday. We need to add the points for minimum temperature and maximum temperature so that the chart gets generated with atleast four points. */ if (this.tempVar.tempToday.length <= 2) { var minTempObject = { hour: this.currentWeather.todayHighLow.todayTempHighTime, temp: this.currentWeather.todayHighLow.todayTempHigh }; var maxTempObject = { hour: this.currentWeather.todayHighLow.todayTempLowTime, temp: this.currentWeather.todayHighLow.todayTempLow }; /* Typically, lowest temp are at dawn, highest temp is around mid day. Thus we can safely arrange like min, max, temp after 10 PM. */ // array.unshift() adds stuff at the beginning of the array. // the order will be: min, max, 10 PM, 11 PM. this.tempVar.tempToday.unshift(maxTempObject, minTempObject); } },
 getSetUVIndex: function() { var uvIndex = this.rawWeatherData.currently.uvIndex; this.highlights.uvIndex = uvIndex; },
 getSetVisibility: function() { var visibilityInMiles = this.rawWeatherData.currently.visibility; this.highlights.visibility = this.mileToKilometer(visibilityInMiles); },
 getSetWindStatus: function() { var windSpeedInMiles = this.rawWeatherData.currently.windSpeed; this.highlights.windStatus.windSpeed = this.mileToKilometer( windSpeedInMiles ); var absoluteWindDir = this.rawWeatherData.currently.windBearing; this.highlights.windStatus.windDirection = absoluteWindDir; this.highlights.windStatus.derivedWindDirection = this.deriveWindDir( absoluteWindDir ); },

Metode de lipici la nivel înalt

Cu metodele de utilitate, achiziție și procesare din calea noastră, acum ne rămâne cu sarcina de a orchestra întregul lucru. Facem asta prin crearea unor metode de lipici de nivel înalt, care în esență apelează metodele scrise mai sus într-o anumită secvență, astfel încât întreaga operație să fie executată fără probleme.

 // Top level for info section // Data in this.currentWeather organizeCurrentWeatherInfo: function() { // data in this.currentWeather /* Coordinates and location is covered (get & set) in: — this.getCoordinates() — this.setFormatCoordinates() There are lots of async-await involved there. So it's better to keep them there. */ this.getSetCurrentTime(); this.getSetCurrentTemp(); this.getSetTodayTempHighLowWithTime(); this.getSetSummary(); this.getSetPossibility(); },
 // Top level for highlights organizeTodayHighlights: function() { // top level for highlights this.getSetUVIndex(); this.getSetVisibility(); this.getSetWindStatus(); },
 // Top level organization and rendering organizeAllDetails: async function() { // top level organization await this.fetchWeatherData(); this.organizeCurrentWeatherInfo(); this.organizeTodayHighlights(); this.getSetHourlyTempInfoToday(); },

montat

Vue oferă cârlige pentru ciclul de viață al instanței — proprietăți care sunt în esență metode și sunt declanșate atunci când ciclul de viață al instanței ajunge în acel stadiu. De exemplu, create, montate, beforeUpdate etc., sunt toate cârligele ciclului de viață foarte utile care permit programatorului să controleze instanța la un nivel mult mai granular decât ar fi fost posibil altfel.

În codul unei componente Vue, aceste cârlige ciclului de viață sunt implementate la fel ca și pentru orice altă prop . De exemplu:

 <template> </template> <script> // import statements export default { data() { return { // data objects here } }, methods: { // methods here }, mounted: function(){ // function body here }, } </script> <style> </style>

Înarmați cu această nouă înțelegere, aruncați o privire la codul de mai jos pentru suportul mounted al App.vue :

 mounted: async function() { this.location = "New York"; await this.organizeAllDetails(); }

Cod complet pentru App.vue

Am acoperit o mulțime de teren în această secțiune, iar ultimele secțiuni v-au oferit lucruri în bucăți. Cu toate acestea, este important să aveți codul complet asamblat pentru App.vue (supus modificărilor ulterioare în secțiunile ulterioare). Aici merge:

 <template> <div> <div class="container-fluid"> <div class="row"> <div class="col-md-3 col-sm-4 col-xs-12 sidebar"> <div> <input type="text" ref="input" placeholder="Location?" @keyup.enter="organizeAllDetails" > <button @click="organizeAllDetails"> <img src="./assets/Search.svg" width="24" height="24"> </button> </div> <div> <div class="wrapper-left"> <div> {{ currentWeather.temp }} <span>°C</span> </div> <div>{{ currentWeather.summary }}</div> <div class="temp-max-min"> <div class="max-desc"> <div> <i>▲</i> {{ currentWeather.todayHighLow.todayTempHigh }} <span>°C</span> </div> <div>at {{ currentWeather.todayHighLow.todayTempHighTime }}</div> </div> <div class="min-desc"> <div> <i>▼</i> {{ currentWeather.todayHighLow.todayTempLow }} <span>°C</span> </div> <div>at {{ currentWeather.todayHighLow.todayTempLowTime }}</div> </div> </div> </div> <div class="wrapper-right"> <div class="date-time-info"> <div> <img src="./assets/calendar.svg" width="20" height="20"> {{ currentWeather.time }} </div> </div> <div class="location-info"> <div> <img src="./assets/location.svg" width="10.83" height="15.83" > {{ currentWeather.full_location }} <div class="mt-1"> Lat: {{ currentWeather.formatted_lat }} <br> Long: {{ currentWeather.formatted_long }} </div> </div> </div> </div> </div> </div> <dashboard-content class="col-md-9 col-sm-8 col-xs-12 content" :highlights="highlights" :tempVar="tempVar" ></dashboard-content> </div> </div> </div> </template> <script> import Content from './components/Content.vue'; export default { name: 'app', props: [], components: { 'dashboard-content': Content }, data() { return { weatherDetails: false, location: '', // raw location from input lat: '', // raw latitude from google maps api response long: '', // raw longitude from google maps api response completeWeatherApi: '', // weather api string with lat and long rawWeatherData: '', // raw response from weather api currentWeather: { full_location: '', // for full address formatted_lat: '', // for N/S formatted_long: '', // for E/W time: '', temp: '', todayHighLow: { todayTempHigh: '', todayTempHighTime: '', todayTempLow: '', todayTempLowTime: '' }, summary: '', possibility: '' }, tempVar: { tempToday: [ // gets added dynamically by this.getSetHourlyTempInfoToday() ], }, highlights: { uvIndex: '', visibility: '', windStatus: { windSpeed: '', windDirection: '', derivedWindDirection: '' }, } }; }, methods: { // Some utility functions convertToTitleCase: function(str) { str = str.toLowerCase().split(' '); for (var i = 0; i < str.length; i++) { str[i] = str[i].charAt(0).toUpperCase() + str[i].slice(1); } return str.join(' '); }, formatPossibility: function(str) { str = str.toLowerCase().split('-'); for (var i = 0; i < str.length; i++) { str[i] = str[i].charAt(0).toUpperCase() + str[i].slice(1); } return str.join(' '); }, unixToHuman: function(timezone, timestamp) { /* READ THIS BEFORE JUDGING & DEBUGGING For any location beyond the arctic circle and the antarctic circle, the goddamn weather api does not return certain keys/values in each of this.rawWeatherData.daily.data[some_array_index]. Due to this, console throws up an error. The code is correct, the problem is with the API. May be later on I will add some padding to tackle missing values. */ var moment = require('moment-timezone'); // for handling date & time var decipher = new Date(timestamp * 1000); var human = moment(decipher) .tz(timezone) .format('llll'); var timeArray = human.split(' '); var timeNumeral = timeArray[4]; var timeSuffix = timeArray[5]; var justTime = timeNumeral + ' ' + timeSuffix; var monthDateArray = human.split(','); var monthDate = monthDateArray[1].trim(); return { fullTime: human, onlyTime: justTime, onlyMonthDate: monthDate }; }, fahToCel: function(tempInFahrenheit) { var tempInCelcius = Math.round((5 / 9) * (tempInFahrenheit — 32)); return tempInCelcius; }, milibarToKiloPascal: function(pressureInMilibar) { var pressureInKPA = pressureInMilibar * 0.1; return Math.round(pressureInKPA); }, mileToKilometer: function(miles) { var kilometer = miles * 1.60934; return Math.round(kilometer); }, deriveWindDir: function(windDir) { var wind_directions_array = [ { minVal: 0, maxVal: 30, direction: 'N' }, { minVal: 31, maxVal: 45, direction: 'NNE' }, { minVal: 46, maxVal: 75, direction: 'NE' }, { minVal: 76, maxVal: 90, direction: 'ENE' }, { minVal: 91, maxVal: 120, direction: 'E' }, { minVal: 121, maxVal: 135, direction: 'ESE' }, { minVal: 136, maxVal: 165, direction: 'SE' }, { minVal: 166, maxVal: 180, direction: 'SSE' }, { minVal: 181, maxVal: 210, direction: 'S' }, { minVal: 211, maxVal: 225, direction: 'SSW' }, { minVal: 226, maxVal: 255, direction: 'SW' }, { minVal: 256, maxVal: 270, direction: 'WSW' }, { minVal: 271, maxVal: 300, direction: 'W' }, { minVal: 301, maxVal: 315, direction: 'WNW' }, { minVal: 316, maxVal: 345, direction: 'NW' }, { minVal: 346, maxVal: 360, direction: 'NNW' } ]; var wind_direction = ''; for (var i = 0; i < wind_directions_array.length; i++) { if ( windDir >= wind_directions_array[i].minVal && windDir <= wind_directions_array[i].maxVal ) { wind_direction = wind_directions_array[i].direction; } } return wind_direction; }, // Some basic action oriented functions makeInputEmpty: function() { this.$refs.input.value = ''; }, makeTempVarTodayEmpty: function() { this.tempVar.tempToday = []; }, detectEnterKeyPress: function() { var input = this.$refs.input; input.addEventListener('keyup', function(event) { event.preventDefault(); var enterKeyCode = 13; if (event.keyCode === enterKeyCode) { this.setHitEnterKeyTrue(); } }); }, locationEntered: function() { var input = this.$refs.input; if (input.value === '') { this.location = "New York"; } else { this.location = this.convertToTitleCase(input.value); } this.makeInputEmpty(); this.makeTempVarTodayEmpty(); }, getCoordinates: function() { this.locationEntered(); var loc = this.location; var coords; var geocoder = new google.maps.Geocoder(); return new Promise(function(resolve, reject) { geocoder.geocode({ address: loc }, function(results, status) { if (status == google.maps.GeocoderStatus.OK) { this.lat = results[0].geometry.location.lat(); this.long = results[0].geometry.location.lng(); this.full_location = results[0].formatted_address; coords = { lat: this.lat, long: this.long, full_location: this.full_location }; resolve(coords); } else { alert("Oops! Couldn't get data for the location"); } }); }); }, // Some basic asynchronous functions setFormatCoordinates: async function() { var coordinates = await this.getCoordinates(); this.lat = coordinates.lat; this.long = coordinates.long; this.currentWeather.full_location = coordinates.full_location; // Remember to beautify lat for N/S if (coordinates.lat > 0) { this.currentWeather.formatted_lat = (Math.round(coordinates.lat * 10000) / 10000).toString() + '°N'; } else if (coordinates.lat < 0) { this.currentWeather.formatted_lat = (-1 * (Math.round(coordinates.lat * 10000) / 10000)).toString() + '°S'; } else { this.currentWeather.formatted_lat = ( Math.round(coordinates.lat * 10000) / 10000 ).toString(); } // Remember to beautify long for N/S if (coordinates.long > 0) { this.currentWeather.formatted_long = (Math.round(coordinates.long * 10000) / 10000).toString() + '°E'; } else if (coordinates.long < 0) { this.currentWeather.formatted_long = (-1 * (Math.round(coordinates.long * 10000) / 10000)).toString() + '°W'; } else { this.currentWeather.formatted_long = ( Math.round(coordinates.long * 10000) / 10000 ).toString(); } }, fixWeatherApi: async function() { await this.setFormatCoordinates(); var weatherApi = 'https://csm.fusioncharts.com/files/assets/wb/wb-data.php?src=darksky&lat=' + this.lat + '&long=' + this.long; this.completeWeatherApi = weatherApi; }, fetchWeatherData: async function() { await this.fixWeatherApi(); var axios = require('axios'); // for handling weather api promise var weatherApiResponse = await axios.get(this.completeWeatherApi); if (weatherApiResponse.status === 200) { this.rawWeatherData = weatherApiResponse.data; } else { alert('Hmm... Seems like our weather experts are busy!'); } }, // Get and set functions; often combined, because they are short // For basic info — left panel/sidebar getTimezone: function() { return this.rawWeatherData.timezone; }, getSetCurrentTime: function() { var currentTime = this.rawWeatherData.currently.time; var timezone = this.getTimezone(); this.currentWeather.time = this.unixToHuman( timezone, currentTime ).fullTime; }, getSetSummary: function() { var currentSummary = this.convertToTitleCase( this.rawWeatherData.currently.summary ); if (currentSummary.includes(' And')) { currentSummary = currentSummary.replace(' And', ','); } this.currentWeather.summary = currentSummary; }, getSetPossibility: function() { var possible = this.formatPossibility(this.rawWeatherData.daily.icon); if (possible.includes(' And')) { possible = possible.replace(' And', ','); } this.currentWeather.possibility = possible; }, getSetCurrentTemp: function() { var currentTemp = this.rawWeatherData.currently.temperature; this.currentWeather.temp = this.fahToCel(currentTemp); }, getTodayDetails: function() { return this.rawWeatherData.daily.data[0]; }, getSetTodayTempHighLowWithTime: function() { var timezone = this.getTimezone(); var todayDetails = this.getTodayDetails(); this.currentWeather.todayHighLow.todayTempHigh = this.fahToCel( todayDetails.temperatureMax ); this.currentWeather.todayHighLow.todayTempHighTime = this.unixToHuman( timezone, todayDetails.temperatureMaxTime ).onlyTime; this.currentWeather.todayHighLow.todayTempLow = this.fahToCel( todayDetails.temperatureMin ); this.currentWeather.todayHighLow.todayTempLowTime = this.unixToHuman( timezone, todayDetails.temperatureMinTime ).onlyTime; }, getHourlyInfoToday: function() { return this.rawWeatherData.hourly.data; }, getSetHourlyTempInfoToday: function() { var unixTime = this.rawWeatherData.currently.time; var timezone = this.getTimezone(); var todayMonthDate = this.unixToHuman(timezone, unixTime).onlyMonthDate; var hourlyData = this.getHourlyInfoToday(); for (var i = 0; i < hourlyData.length; i++) { var hourlyTimeAllTypes = this.unixToHuman(timezone, hourlyData[i].time); var hourlyOnlyTime = hourlyTimeAllTypes.onlyTime; var hourlyMonthDate = hourlyTimeAllTypes.onlyMonthDate; if (todayMonthDate === hourlyMonthDate) { var hourlyObject = { hour: '', temp: '' }; hourlyObject.hour = hourlyOnlyTime; hourlyObject.temp = this.fahToCel(hourlyData[i].temperature).toString(); this.tempVar.tempToday.push(hourlyObject); /* Since we are using array.push(), we are just adding elements at the end of the array. Thus, the array is not getting emptied first when a new location is entered. to solve this problem, a method this.makeTempVarTodayEmpty() has been created, and called from this.locationEntered(). */ } } /* To cover the edge case where the local time is between 10 — 12 PM, and therefore there are only two elements in the array this.tempVar.tempToday. We need to add the points for minimum temperature and maximum temperature so that the chart gets generated with atleast four points. */ if (this.tempVar.tempToday.length <= 2) { var minTempObject = { hour: this.currentWeather.todayHighLow.todayTempHighTime, temp: this.currentWeather.todayHighLow.todayTempHigh }; var maxTempObject = { hour: this.currentWeather.todayHighLow.todayTempLowTime, temp: this.currentWeather.todayHighLow.todayTempLow }; /* Typically, lowest temp are at dawn, highest temp is around mid day. Thus we can safely arrange like min, max, temp after 10 PM. */ // array.unshift() adds stuff at the beginning of the array. // the order will be: min, max, 10 PM, 11 PM. this.tempVar.tempToday.unshift(maxTempObject, minTempObject); } }, // For Today Highlights getSetUVIndex: function() { var uvIndex = this.rawWeatherData.currently.uvIndex; this.highlights.uvIndex = uvIndex; }, getSetVisibility: function() { var visibilityInMiles = this.rawWeatherData.currently.visibility; this.highlights.visibility = this.mileToKilometer(visibilityInMiles); }, getSetWindStatus: function() { var windSpeedInMiles = this.rawWeatherData.currently.windSpeed; this.highlights.windStatus.windSpeed = this.mileToKilometer( windSpeedInMiles ); var absoluteWindDir = this.rawWeatherData.currently.windBearing; this.highlights.windStatus.windDirection = absoluteWindDir; this.highlights.windStatus.derivedWindDirection = this.deriveWindDir( absoluteWindDir ); }, // top level for info section organizeCurrentWeatherInfo: function() { // data in this.currentWeather /* Coordinates and location is covered (get & set) in: — this.getCoordinates() — this.setFormatCoordinates() There are lots of async-await involved there. So it's better to keep them there. */ this.getSetCurrentTime(); this.getSetCurrentTemp(); this.getSetTodayTempHighLowWithTime(); this.getSetSummary(); this.getSetPossibility(); }, organizeTodayHighlights: function() { // top level for highlights this.getSetUVIndex(); this.getSetVisibility(); this.getSetWindStatus(); }, // topmost level orchestration organizeAllDetails: async function() { // top level organization await this.fetchWeatherData(); this.organizeCurrentWeatherInfo(); this.organizeTodayHighlights(); this.getSetHourlyTempInfoToday(); }, }, mounted: async function() { this.location = "New York"; await this.organizeAllDetails(); } }; </script>

Și, în sfârșit, după atâta răbdare și muncă grea, puteți vedea fluxul de date cu puterea sa brută! Vizitați aplicația în browser, reîmprospătați pagina, căutați o locație în caseta de căutare a aplicației și apăsați Enter !

Aplicația așa cum este afișată în browser
(Previzualizare mare)

Acum că am terminat cu toate sarcinile grele, luați o pauză. Secțiunile ulterioare se concentrează pe utilizarea datelor pentru a crea diagrame care sunt frumoase și informative, urmate de a oferi aplicației noastre cu aspect urat o sesiune de îngrijire mult meritată folosind CSS.

5. Vizualizarea datelor cu FusionCharts

Considerații fundamentale pentru diagrame

Pentru utilizatorul final, esența unui tablou de bord este, în esență, aceasta: o colecție de informații filtrate și îngrijite cu grijă pe un anumit subiect, transmise prin instrumente vizuale/grafice pentru o ingerare rapidă. Nu le pasă de subtilitățile ingineriei pipelinei dvs. de date sau de cât de estetic este codul dvs. - tot ce doresc este o vizualizare la nivel înalt în 3 secunde. Prin urmare, aplicația noastră brută care afișează date text nu înseamnă nimic pentru ei și este timpul să implementăm mecanisme pentru a încheia datele cu diagrame.

Cu toate acestea, înainte de a ne aprofunda în implementarea diagramelor, să luăm în considerare câteva întrebări pertinente și posibilele răspunsuri din perspectiva noastră:

  • Ce tip de diagrame sunt potrivite pentru tipul de date cu care avem de-a face?
    Ei bine, răspunsul are două aspecte - contextul și scopul. Prin context, ne referim la tipul de date și se încadrează în general în schema lucrurilor mai mari, limitate de sfera și publicul proiectului. Și prin scop, înțelegem în esență „pe ce vrem să subliniem?”. De exemplu, putem reprezenta temperatura de astăzi în diferite momente ale zilei utilizând o diagramă Coloane (coloane verticale de lățime egală, cu înălțimea proporțională cu valoarea pe care o reprezintă coloana). Cu toate acestea, rareori ne interesează valorile individuale, ci mai degrabă variația generală și tendința de-a lungul datelor. Pentru a se potrivi scopului, este în interesul nostru să folosim o diagramă cu linii și o vom face în curând.
  • Ce ar trebui să țineți cont înainte de a selecta o bibliotecă de diagrame?
    Întrucât realizăm un proiect predominant folosind tehnologii bazate pe JavaScript, este deloc o idee că orice bibliotecă de diagrame pe care o alegem pentru proiectul nostru ar trebui să fie nativă din lumea JavaScript. Având în vedere această premisă de bază, ar trebui să luăm în considerare următoarele înainte de a reduce la zero orice bibliotecă anume:
    • Suport pentru cadrele alese de noi , care, în acest caz, este Vue.js. Un proiect poate fi dezvoltat în alte cadre JavaScript populare, cum ar fi React sau Angular - verificați suportul bibliotecii de diagrame pentru cadrul dvs. preferat. De asemenea, trebuie luat în considerare suportul pentru alte limbaje de programare populare, cum ar fi Python, Java, C++, .Net (AS și VB), mai ales atunci când proiectul implică niște chestii backend serioase.
    • Disponibilitatea tipurilor și caracteristicilor de diagrame , deoarece este aproape imposibil să știți în prealabil care va fi forma finală și scopul datelor din proiect (mai ales dacă cerințele sunt reglementate de clienții dvs. într-un cadru profesional). În acest caz, ar trebui să vă extindeți rețeaua și să alegeți o bibliotecă de diagrame care are cea mai largă colecție de diagrame. Mai important, pentru a vă diferenția proiectul de alții, biblioteca ar trebui să aibă suficiente caracteristici sub formă de atribute de diagramă configurabile, astfel încât să puteți ajusta și personaliza cele mai multe aspecte ale diagramelor și nivelul corect de granularitate. De asemenea, configurațiile implicite ale diagramelor ar trebui să fie sensibile, iar documentația bibliotecii trebuie să fie de top, din motive care sunt evidente pentru dezvoltatorii profesioniști.
    • Curba de învățare, comunitatea de asistență și echilibrul trebuie de asemenea luate în considerare, mai ales atunci când sunteți nou în vizualizarea datelor. La un capăt al spectrului, aveți instrumente proprietare, cum ar fi Tableau și Qlickview, care costă o bombă, au o curbă de învățare lină, dar vine și cu atât de multe limitări în ceea ce privește personalizarea, integrarea și implementarea. La celălalt capăt se află d3.js — vast, gratuit (sursă deschisă) și personalizabil la bază, dar trebuie să plătiți prețul unei curbe de învățare foarte abrupte pentru a putea face ceva productiv cu biblioteca.

Ceea ce aveți nevoie este punctul ideal - echilibrul potrivit între productivitate, acoperire, personalizare, curba de învățare și, desigur, cost. Vă invităm să aruncați o privire la FusionCharts — cea mai cuprinzătoare și mai pregătită bibliotecă de diagrame JavaScript din lume pentru web și mobil, pe care o vom folosi în acest proiect pentru a crea diagrame.

Introducere în FusionCharts

FusionCharts este folosit în întreaga lume ca bibliotecă de diagrame JavaScript de acces de milioane de dezvoltatori răspândiți în sute de țări de pe tot globul. Din punct de vedere tehnic, este cât se poate de încărcat și configurabil, cu suport pentru integrarea acestuia cu aproape orice stivă de tehnologie populară utilizată pentru proiecte bazate pe web. Utilizarea FusionCharts în mod comercial necesită o licență și trebuie să plătiți pentru licență în funcție de cazul dvs. de utilizare (vă rugăm să contactați vânzările dacă sunteți curios). Cu toate acestea, folosim FusionCharts în aceste proiecte doar pentru a încerca câteva lucruri și, prin urmare, versiunea fără licență (vine cu un mic filigran în diagramele dvs. și alte câteva restricții). Utilizarea versiunii fără licență este perfectă atunci când încercați diagramele și o utilizați în proiectele dvs. non-comerciale sau personale. Dacă intenționați să implementați aplicația comercial, vă rugăm să vă asigurați că aveți o licență de la FusionCharts.

Deoarece acesta este un proiect care implică Vue.js, vom avea nevoie de două module care trebuie instalate, dacă nu sunt făcute mai devreme:

  • Modulul fusioncharts , deoarece conține tot ce aveți nevoie pentru a crea diagramele
  • Modulul vue-fusioncharts , care este în esență un înveliș pentru fusioncharts, astfel încât să poată fi utilizat într-un proiect Vue.js

Dacă nu le-ați instalat mai devreme (conform instrucțiunilor din a treia secțiune), instalați-le executând următoarea comandă din directorul rădăcină al proiectului:

 npm install fusioncharts vue-fusioncharts --save

Apoi, asigurați-vă că fișierul src/main.js al proiectului are următorul cod (menționat și în secțiunea 3):

 import Vue from 'vue'; import App from './App.vue'; import FusionCharts from 'fusioncharts'; import Charts from 'fusioncharts/fusioncharts.charts'; import Widgets from 'fusioncharts/fusioncharts.widgets'; import PowerCharts from 'fusioncharts/fusioncharts.powercharts'; import FusionTheme from 'fusioncharts/themes/fusioncharts.theme.fusion'; import VueFusionCharts from 'vue-fusioncharts'; Charts(FusionCharts); PowerCharts(FusionCharts); Widgets(FusionCharts); FusionTheme(FusionCharts); Vue.use(VueFusionCharts, FusionCharts); new Vue({ el: '#app', render: h => h(App) })

Poate cea mai critică linie din fragmentul de mai sus este următoarea:

 Vue.use(VueFusionCharts, FusionCharts)

Îi cere lui Vue să folosească modulul vue-fusioncharts pentru a înțelege multe lucruri din proiect care aparent nu sunt definite în mod explicit de noi, dar sunt definite în modulul însuși. De asemenea, acest tip de declarație implică declarație globală , prin care vrem să spunem că oriunde Vue întâlnește ceva ciudat în codul proiectului nostru (lucruri pe care nu le-am definit în mod explicit despre utilizarea FusionCharts), va apărea cel puțin o dată în vue-fusioncharts. și modulele nodului fusioncharts pentru definițiile lor, înainte de a arunca erori. Dacă am fi folosit FusionCharts într-o parte izolată a proiectului nostru (nefolosind-o în aproape toate fișierele componente), atunci poate că declarația locală ar fi avut mai mult sens.

Cu asta, sunteți gata să utilizați FusionCharts în proiect. Vom folosi o varietate destul de mare de diagrame, alegerea fiind dependentă de aspectul datelor meteo pe care dorim să le vizualizăm. De asemenea, vom putea vedea interacțiunea dintre legarea datelor, componentele personalizate și observatorii în acțiune.

Schemă generală pentru utilizarea Fusioncharts în fișierele .vue

În această secțiune, vom explica ideea generală a utilizării FusionCharts pentru a crea diferite diagrame în fișierele .vue . Dar mai întâi, să vedem pseudocodul care ilustrează schematic ideea de bază.

 <template> <div> <fusioncharts :attribute_1="data_object_1" :attribute_2="data_object_2" … … ... > </fusioncharts> </div> </template> <script> export default { props: ["data_prop_received_by_the_component"], components: {}, data() { return { data_object_1: "value_1", data_object_2: "value_2", … … }; }, methods: {}, computed: {}, watch: { data_prop_received_by_the_component: { handler: function() { // some code/logic, mainly data manipulation based }, deep: true } } }; </script> <style> // component specific special CSS code here </style>

Să înțelegem diferitele părți ale pseudocodului de mai sus:

  • În <template> , în cadrul <div> de nivel superior (acesta este destul de obligatoriu pentru codul HTML șablon al fiecărei componente), avem componenta personalizată <fusioncharts> . Avem definiția componentei conținută în modulul vue-fusioncharts Node pe care l-am instalat pentru acest proiect. Pe plan intern, vue-fusioncharts se bazează pe modulul fusioncharts , care a fost de asemenea instalat. Am importat modulele necesare și le-am rezolvat dependențele, am instruit Vue să folosească wrapper-ul la nivel global (pe tot parcursul proiectului) în fișierul src/main.js și, prin urmare, nu există lipsă de definiție pentru componenta personalizată <fusioncharts> pe care am folosit-o. Aici. De asemenea, componenta personalizată are atribute personalizate, iar fiecare dintre atributele personalizate este legat la un obiect de date (și, la rândul său, la valorile acestora), prin directiva v-bind , pentru care prescurtarea este simbolul două puncte ( : ). Vom afla mai detaliat despre atributele și obiectele de date asociate acestora, când vom discuta unele dintre diagramele specifice utilizate în acest proiect.
  • În <script> , mai întâi declarați elementele de recuzită pe care ar trebui să le primească componenta, apoi continuați definirea obiectelor de date care sunt legate de atributele <fusioncharts> . Valorile atribuite obiectelor de date sunt valorile pe care atributele <fusioncharts> , iar diagramele sunt create pe baza valorilor extrase. În afară de acestea, cea mai interesantă parte a codului este obiectul watch { } . Acesta este un obiect foarte special în schema de lucruri a lui Vue - în esență îi indică lui Vue să supravegheze orice modificări care au loc anumitor date și apoi să ia acțiuni bazate pe modul în care a fost definită funcția de handler a datelor respective. De exemplu, dorim ca Vue să urmărească prop primit, adică data_prop_received_by_the_component în pseudocod. Elementul prop devine o cheie în obiectul watch { } , iar valoarea cheii este un alt obiect - o metodă de gestionare care descrie ce trebuie făcut ori de câte ori prop se schimbă. Cu mecanisme atât de elegante pentru a gestiona schimbările, aplicația își menține reactivitatea. deep: true reprezintă un steag boolean pe care îl puteți asocia cu observatori, astfel încât obiectul urmărit este urmărit destul de profund, adică chiar și modificările făcute în nivelurile imbricate ale obiectului sunt urmărite.
    ( Pentru mai multe informații despre observatori, consultați documentația oficială ).

Acum că sunteți echipat cu o înțelegere a schemei generale a lucrurilor, să ne aprofundăm în implementările specifice ale diagramelor din fișierele componente .vue . Codul va fi destul de explicit și ar trebui să încercați să înțelegeți cum se potrivesc specificul în schema generală a lucrurilor descrise mai sus.

Implementarea diagramelor în fișiere .vue

În timp ce specificul implementării variază de la o diagramă la alta, următoarea explicație este aplicabilă pentru toate acestea:

  • <template>
    După cum sa explicat anterior, componenta personalizată <fusioncharts> are mai multe atribute, fiecare dintre ele fiind legată de obiectul de date corespunzător definit în funcția data() prin utilizarea directivei v-bind :. Numele atributelor se explică de la sine pentru ceea ce înseamnă, iar descoperirea obiectelor de date corespunzătoare este, de asemenea, banală.
  • <script>
    În funcția data() , obiectele de date și valorile lor sunt ceea ce face ca diagramele să funcționeze, din cauza legăturii efectuate de directivele v-bind ( : ) utilizate pe atributele <fusioncharts> . Înainte de a ne aprofunda în obiectele de date individuale, merită menționat câteva caracteristici generale:
    • Obiectele de date ale căror valori sunt fie 0 , fie 1 sunt de natură booleană, unde 0 reprezintă ceva care nu este disponibil/dezactivat, iar 1 reprezintă starea disponibilitate/activată. Cu toate acestea, fiți atenți că obiectele de date non-booleene pot avea și 0 sau 1 ca valori, pe lângă alte valori posibile - depinde de context. De exemplu, containerbackgroundopacity cu valoarea implicită ca 0 este booleană, în timp ce lowerLimit cu valoarea implicită ca 0 înseamnă pur și simplu că numărul zero este valoarea sa literală.
    • Unele obiecte de date se ocupă de proprietăți CSS cum ar fi marginea, padding, dimensiunea fontului etc. - valoarea are o unitate implicită de „px” sau pixel. În mod similar, alte obiecte de date pot avea unități implicite asociate cu valorile lor. Pentru informații detaliate, vă rugăm să consultați pagina respectivă cu atributele diagramei din FusionCharts Dev Center.
  • În funcția data() , poate cel mai interesant și neevident obiect este dataSource. Acest obiect are trei obiecte principale imbricate în el:
    • diagramă : acest obiect încapsulează o mulțime de atribute de diagramă legate de configurația și elementele cosmetice ale diagramei. Este aproape un construct obligatoriu pe care îl veți găsi în toate diagramele pe care le veți crea pentru acest proiect.
    • gama de culori : Acest obiect este oarecum specific diagramei luate în considerare și este prezent în principal în diagrame care se ocupă de mai multe culori/nuanțe pentru a delimita diferite sub-gamale ale scalei utilizate în diagramă.
    • valoare: Acest obiect, din nou, este prezent în diagrame care are o valoare specifică care trebuie evidențiată în intervalul scalei.
  • Obiectul watch { } este poate cel mai important lucru care face ca această diagramă și celelalte diagrame utilizate în acest proiect să prindă viață. Reactivitatea diagramelor, adică diagramele care se actualizează pe baza noilor valori rezultate dintr-o nouă interogare de utilizator este controlată de observatorii definiți în acest obiect. De exemplu, am definit un observator pentru highlights prop primite de componentă și apoi am definit o funcție de gestionare pentru a instrui Vue despre acțiunile necesare pe care ar trebui să le întreprindă, atunci când ceva se schimbă în legătură cu obiectul urmărit în întregul proiect. Aceasta înseamnă că, ori de câte ori App.vue oferă o nouă valoare pentru oricare dintre obiectele din highlights , informațiile se scurg până la această componentă, iar noua valoare este actualizată în obiectele de date ale acestei componente. Graficul fiind legat de valori, este de asemenea actualizat ca urmare a acestui mecanism.

Explicațiile de mai sus sunt destul de mari pentru a ne ajuta să dezvoltăm o înțelegere intuitivă a imaginii de ansamblu. Odată ce înțelegeți intuitiv conceptele, puteți consulta oricând documentația Vue.js și FusionCharts, atunci când ceva nu vă este clar din codul în sine. Vă lăsăm exercițiul în seama dvs. și, începând cu următoarea subsecțiune, nu vom explica lucrurile pe care le-am acoperit în această subsecțiune.

src/components/TempVarChart.vue

O diagramă care arată temperatura orară
(Previzualizare mare)
 <template> <div class="custom-card header-card card"> <div class="card-body pt-0"> <fusioncharts type="spline" width="100%" height="100%" dataformat="json" dataEmptyMessage="i-https://i.postimg.cc/R0QCk9vV/Rolling-0-9s-99px.gif" dataEmptyMessageImageScale=39 :datasource="tempChartData" > </fusioncharts> </div> </div> </template> <script> export default { props: ["tempVar"], components: {}, data() { return { tempChartData: { chart: { caption: "Hourly Temperature", captionFontBold: "0", captionFontColor: "#000000", captionPadding: "30", baseFont: "Roboto", chartTopMargin: "30", showHoverEffect: "1", theme: "fusion", showaxislines: "1", numberSuffix: "°C", anchorBgColor: "#6297d9", paletteColors: "#6297d9", drawCrossLine: "1", plotToolText: "$label<br><hr><b>$dataValue</b>", showAxisLines: "0", showYAxisValues: "0", anchorRadius: "4", divLineAlpha: "0", labelFontSize: "13", labelAlpha: "65", labelFontBold: "0", rotateLabels: "1", slantLabels: "1", canvasPadding: "20" }, data: [], }, }; }, methods: { setChartData: function() { var data = []; for (var i = 0; i < this.tempVar.tempToday.length; i++) { var dataObject = { label: this.tempVar.tempToday[i].hour, value: this.tempVar.tempToday[i].temp }; data.push(dataObject); } this.tempChartData.data = data; }, }, mounted: function() { this.setChartData(); }, watch: { tempVar: { handler: function() { this.setChartData(); }, deep: true }, }, }; </script> <style> </style>

src/components/UVIndex.vue

Această componentă conține o diagramă extrem de utilă - Ecartamentul unghiular.

Index UV
(Previzualizare mare)

Codul pentru componentă este dat mai jos. Pentru informații detaliate despre atributele grafice ale Angular Gauge, consultați pagina FusionCharts Dev Center pentru Angular Gauge.

 <template> <div class="highlights-item col-md-4 col-sm-6 col-xs-12 border-top"> <div> <fusioncharts :type="type" :width="width" :height="height" :containerbackgroundopacity="containerbackgroundopacity" :dataformat="dataformat" :datasource="datasource" ></fusioncharts> </div> </div> </template> <script> export default { props: ["highlights"], components: {}, data() { return { type: "angulargauge", width: "100%", height: "100%", containerbackgroundopacity: 0, dataformat: "json", datasource: { chart: { caption: "UV Index", captionFontBold: "0", captionFontColor: "#000000", captionPadding: "30", lowerLimit: "0", upperLimit: "15", lowerLimitDisplay: "1", upperLimitDisplay: "1", showValue: "0", theme: "fusion", baseFont: "Roboto", bgAlpha: "0", canvasbgAlpha: "0", gaugeInnerRadius: "75", gaugeOuterRadius: "110", pivotRadius: "0", pivotFillAlpha: "0", valueFontSize: "20", valueFontColor: "#000000", valueFontBold: "1", tickValueDistance: "3", autoAlignTickValues: "1", majorTMAlpha: "20", chartTopMargin: "30", chartBottomMargin: "40" }, colorrange: { color: [ { minvalue: "0", maxvalue: this.highlights.uvIndex.toString(), code: "#7DA9E0" }, { minvalue: this.highlights.uvIndex.toString(), maxvalue: "15", code: "#D8EDFF" } ] }, annotations: { groups: [ { items: [ { id: "val-label", type: "text", text: this.highlights.uvIndex.toString(), fontSize: "20", font: "Source Sans Pro", fontBold: "1", fillcolor: "#212529", x: "$gaugeCenterX", y: "$gaugeCenterY" } ] } ] }, dials: { dial: [ { value: this.highlights.uvIndex.toString(), baseWidth: "0", radius: "0", borderThickness: "0", baseRadius: "0" } ] } } }; }, methods: {}, computed: {}, watch: { highlights: { handler: function() { this.datasource.colorrange.color[0].maxvalue = this.highlights.uvIndex.toString(); this.datasource.colorrange.color[1].minvalue = this.highlights.uvIndex.toString(); this.datasource.annotations.groups[0].items[0].text = this.highlights.uvIndex.toString(); }, deep: true } } }; </script>

src/components/Visibility.vue

În această componentă, folosim un indicator liniar orizontal pentru a reprezenta vizibilitatea, așa cum se arată în imaginea de mai jos:

O captură de ecran a ecartamentului liniar orizontal care reprezintă vizibilitatea aerului (16 km)
(Previzualizare mare)

Codul pentru componentă este dat mai jos. Pentru o înțelegere aprofundată a diferitelor atribute ale acestui tip de diagramă, vă rugăm să consultați pagina Centrului de dezvoltare FusionCharts pentru Gauge liniar orizontal.

 <template> <div class="highlights-item col-md-4 col-sm-6 col-xs-12 border-left border-right border-top"> <div> <fusioncharts :type="type" :width="width" :height="height" :containerbackgroundopacity="containerbackgroundopacity" :dataformat="dataformat" :datasource="datasource" > </fusioncharts> </div> </div> </template> <script> export default { props: ["highlights"], components: {}, methods: {}, computed: {}, data() { return { type: "hlineargauge", width: "100%", height: "100%", containerbackgroundopacity: 0, dataformat: "json", creditLabel: false, datasource: { chart: { caption: "Air Visibility", captionFontBold: "0", captionFontColor: "#000000", baseFont: "Roboto", numberSuffix: " km", lowerLimit: "0", upperLimit: "40", showPointerShadow: "1", animation: "1", transposeAnimation: "1", theme: "fusion", bgAlpha: "0", canvasBgAlpha: "0", valueFontSize: "20", valueFontColor: "#000000", valueFontBold: "1", pointerBorderAlpha: "0", chartBottomMargin: "40", captionPadding: "30", chartTopMargin: "30" }, colorRange: { color: [ { minValue: "0", maxValue: "4", label: "Fog", code: "#6297d9" }, { minValue: "4", maxValue: "10", label: "Haze", code: "#7DA9E0" }, { minValue: "10", maxValue: "40", label: "Clear", code: "#D8EDFF" } ] }, pointers: { pointer: [ { value: this.highlights.visibility.toString() } ] } } }; }, watch: { highlights: { handler: function() { this.datasource.pointers.pointer[0].value = this.highlights.visibility.toString(); }, deep: true } } }; </script>

src/components/WindStatus.vue

Această componentă afișează viteza și direcția vântului (viteza vântului, dacă sunteți cunoscător în fizică) și este foarte dificil să reprezentați un vector folosind o diagramă. Pentru astfel de cazuri, vă sugerăm să le reprezentați cu ajutorul unor imagini frumoase și valori de text. Deoarece reprezentarea la care ne-am gândit depinde în întregime de CSS, o vom implementa în următoarea secțiune care se ocupă de CSS. Cu toate acestea, aruncați o privire la ceea ce ne propunem să creăm:

Starea vântului: direcția vântului (stânga) și viteza vântului (dreapta)
(Previzualizare mare)
 <template> <div class="highlights-item col-md-4 col-sm-6 col-xs-12 border-top"> <div> <div class="card-heading pt-5">Wind Status</div> <div class="row pt-4 mt-4"> <div class="col-sm-6 col-md-6 mt-2 text-center align-middle"> <p class="card-sub-heading mt-3">Wind Direction</p> <p class="mt-4"><img src="../assets/winddirection.svg" height="40" width="40"></p> <p class="card-value mt-4">{{ highlights.windStatus.derivedWindDirection }}</p> </div> <div class="col-sm-6 col-md-6 mt-2"> <p class="card-sub-heading mt-3">Wind Speed</p> <p class="mt-4"><img src="../assets/windspeed.svg" height="40" width="40"></p> <p class="card-value mt-4">{{ highlights.windStatus.windSpeed }} km/h</p> </div> </div> </div> </div> </template> <script> export default { props: ["highlights"], components: {}, data() { return {}; }, methods: {}, computed: {} }; </script>

Încheierea cu Highlights.vue

Amintiți-vă că am implementat deja cod cu CSS pentru toate componentele - cu excepția Content.vue și Highlights.vue . Deoarece Content.vue este o componentă stupidă care doar transmite date, stilul minim de care are nevoie a fost deja acoperit. De asemenea, am scris deja codul adecvat pentru stilarea barei laterale și a cardurilor care conțin diagramele. Prin urmare, tot ce ne rămâne de făcut este să adăugăm câteva elemente stilistice la Highlights.vue , care implică în primul rând utilizarea claselor CSS:

 <template> <div class="custom-content-card content-card card"> <div class="card-body pb-0"> <div class="content-header h4 text-center pt-2 pb-3">Highlights</div> <div class="row"> <uv-index :highlights="highlights"></uv-index> <visibility :highlights="highlights"></visibility> <wind-status :highlights="highlights"></wind-status> </div> </div> </div> </template> <script> import UVIndex from "./UVIndex.vue"; import Visibility from "./Visibility.vue"; import WindStatus from "./WindStatus.vue"; export default { props: ["highlights"], components: { "uv-index": UVIndex, "visibility": Visibility, "wind-status": WindStatus, }, }; </script>

Implementare și cod sursă

Cu graficele și stilul în ordine, am terminat! Fă-ți un moment pentru a aprecia frumusețea creației tale.

Rezultatul
(Previzualizare mare)

Acum este timpul să vă implementați aplicația și să o distribuiți colegilor dvs. Dacă nu aveți prea multe idei despre implementare și vă așteptați să vă ajutăm, vedeți aici despre ideile noastre de implementare. Articolul linkat conține, de asemenea, sugestii despre cum să eliminați filigranul FusionCharts din partea stângă jos a fiecărei diagrame.

Dacă te încurci undeva și vrei un punct de referință, codul sursă este disponibil pe Github.