Menggunakan Vue.js Untuk Membuat Dasbor Cuaca Interaktif Dengan API

Diterbitkan: 2022-03-10
Ringkasan cepat Membuat dasbor dengan data API seringkali merupakan urusan yang rumit. Memilih tumpukan teknologi Anda, mengintegrasikan API, memilih bagan yang tepat, dan mempercantik dengan gaya CSS bisa menjadi rumit. Tutorial ini adalah panduan langkah demi langkah tentang cara membantu Anda membuat dasbor cuaca di Vue.js menggunakan data API.

(Ini adalah artikel bersponsor.) Dalam tutorial ini, Anda akan membuat dasbor cuaca sederhana dari awal. Ini akan menjadi aplikasi klien-end yang bukan contoh "Hello World", atau terlalu menakutkan dalam ukuran dan kompleksitasnya.

Seluruh proyek akan dikembangkan menggunakan alat dari ekosistem Node.js + npm. Secara khusus, kami akan sangat bergantung pada Dark Sky API untuk data, Vue.js untuk semua pekerjaan berat, dan FusionCharts untuk visualisasi data.

Prasyarat

Kami berharap Anda mengetahui hal-hal berikut:

  • HTML5 dan CSS3 (kami juga akan menggunakan fitur dasar yang disediakan oleh Bootstrap;
  • JavaScript (terutama cara menggunakan bahasa ES6);
  • Node.js dan npm (dasar-dasar lingkungan dan manajemen paket baik-baik saja).

Terlepas dari yang disebutkan di atas, alangkah baiknya jika Anda terbiasa dengan Vue.js , atau kerangka JavaScript serupa lainnya. Kami tidak berharap Anda tahu tentang FusionCharts — sangat mudah digunakan sehingga Anda akan mempelajarinya dengan cepat!

Pembelajaran yang Diharapkan

Pembelajaran utama Anda dari proyek ini adalah:

  1. Bagaimana merencanakan penerapan dasbor yang baik?
  2. Bagaimana mengembangkan aplikasi dengan Vue.js
  3. Cara membuat aplikasi berbasis data
  4. Cara memvisualisasikan data menggunakan FusionCharts

Secara khusus, setiap bagian membawa Anda selangkah lebih dekat ke tujuan pembelajaran:

  1. Pengantar Dasbor Cuaca
    Bab ini memberi Anda gambaran umum tentang berbagai aspek usaha.
  2. Buat Proyek
    Di bagian ini, Anda belajar tentang membuat proyek dari awal menggunakan alat baris perintah Vue.
  3. Sesuaikan Struktur Proyek Default
    Perancah proyek default yang Anda dapatkan di bagian sebelumnya tidak cukup; di sini Anda mempelajari hal-hal tambahan yang diperlukan untuk proyek dari sudut pandang struktural.
  4. Akuisisi dan Pemrosesan Data
    Bagian ini adalah inti dari proyek; semua kode penting untuk memperoleh dan memproses data dari API ditampilkan di sini. Berharap untuk menghabiskan waktu maksimal di bagian ini.
  5. Visualisasi Data Dengan FusionCharts
    Setelah semua data dan bagian bergerak lainnya dari proyek stabil, bagian ini didedikasikan untuk memvisualisasikan data menggunakan FusionCharts dan sedikit CSS.

1. Alur Kerja Dasbor

Sebelum kita menyelami implementasinya, penting untuk memperjelas rencana kita. Kami membagi rencana kami menjadi empat aspek berbeda:

Persyaratan

Apa persyaratan kami untuk proyek ini? Dengan kata lain, hal-hal apa yang ingin kami tampilkan melalui Dasbor Cuaca kami? Mengingat bahwa audiens yang kami tuju mungkin hanyalah manusia biasa dengan selera sederhana, kami ingin menunjukkan kepada mereka hal-hal berikut:

  • Detail lokasi yang ingin mereka lihat cuacanya, bersama dengan beberapa informasi utama tentang cuaca. Karena tidak ada persyaratan yang ketat, kami akan mencari tahu detail yang membosankan nanti. Namun, pada tahap ini, penting untuk dicatat bahwa kita harus menyediakan kotak pencarian kepada penonton, sehingga mereka dapat memberikan masukan untuk lokasi yang mereka minati.
  • Informasi grafis tentang cuaca di lokasi yang mereka minati, seperti:
    • Variasi suhu untuk hari permintaan
    • Sorotan cuaca hari ini:
      • Kecepatan dan Arah Angin
      • Visibilitas
      • Indeks uv

Catatan : Data yang diperoleh dari API memberikan informasi mengenai banyak aspek cuaca lainnya. Kami memilih untuk tidak menggunakan semuanya demi menjaga kode seminimal mungkin.

Struktur

Berdasarkan persyaratan, kami dapat menyusun dasbor kami seperti yang ditunjukkan di bawah ini:

Struktur dasbor
(Pratinjau besar)

Data

Dasbor kami sebaik data yang kami dapatkan, karena tidak akan ada visualisasi yang bagus tanpa data yang tepat. Ada banyak API publik yang menyediakan data cuaca — beberapa di antaranya gratis, dan beberapa tidak. Untuk proyek kami, kami akan mengumpulkan data dari Dark Sky API. Namun, kami tidak akan dapat melakukan polling titik akhir API dari ujung klien secara langsung. Jangan khawatir, kami memiliki solusi yang akan diungkapkan pada waktu yang tepat! Setelah kami mendapatkan data untuk lokasi yang dicari, kami akan melakukan beberapa pemrosesan dan pemformatan data — Anda tahu, jenis teknis yang membantu kami membayar tagihan.

visualisasi

Setelah kami mendapatkan data yang bersih dan terformat, kami menghubungkannya ke FusionCharts. Ada sangat sedikit perpustakaan JavaScript di dunia yang mampu seperti FusionCharts. Dari sekian banyak penawaran dari FusionCharts, kami hanya akan menggunakan beberapa — semuanya ditulis dalam JavaScript, tetapi bekerja dengan mulus saat diintegrasikan dengan pembungkus Vue untuk FusionCharts.

Berbekal gambaran yang lebih besar, mari kita mengotori tangan kita — saatnya untuk membuat segalanya menjadi nyata! Di bagian berikutnya, Anda akan membuat proyek Vue dasar, di atasnya kita akan membangun lebih lanjut.

2. Membuat Proyek

Untuk membuat proyek, jalankan langkah-langkah berikut:

  1. Instal Node.js + npm
    ( Jika Anda telah menginstal Node.js di komputer Anda, lewati langkah ini. )
    Node.js hadir dengan npm yang dibundel dengannya, jadi Anda tidak perlu menginstal npm secara terpisah. Tergantung pada sistem operasinya, unduh dan instal Node.js sesuai dengan instruksi yang diberikan di sini.

    Setelah diinstal, mungkin ada baiknya untuk memverifikasi apakah perangkat lunak tersebut bekerja dengan benar, dan apa versinya. Untuk mengujinya, buka baris perintah/terminal dan jalankan perintah berikut:
     node --version npm --version
  2. Instal paket dengan npm
    Setelah Anda menjalankan dan menjalankan npm, jalankan perintah berikut untuk menginstal paket dasar yang diperlukan untuk proyek kami.
     npm install -g vue@2 vue-cli@2
  3. Inisialisasi scaffolding proyek dengan vue-cli
    Dengan asumsi bahwa langkah sebelumnya telah berjalan dengan baik, langkah selanjutnya adalah menggunakan vue-cli — alat baris perintah dari Vue.js, untuk menginisialisasi proyek. Untuk melakukannya, jalankan yang berikut ini:
    • Inisialisasi scaffolding dengan template webpack-simple.
       vue init webpack-simple vue_weather_dashboard
      Anda akan ditanyai banyak pertanyaan — menerima default untuk semua tetapi pertanyaan terakhir akan cukup baik untuk proyek ini; jawab N untuk yang terakhir.
      Tangkapan layar dari baris perintah/terminal
      (Pratinjau besar)
      Perlu diingat bahwa meskipun webpack-simple sangat baik untuk pembuatan prototipe cepat dan aplikasi ringan seperti milik kami, ini tidak terlalu cocok untuk aplikasi serius atau penyebaran produksi. Jika Anda ingin menggunakan template lain (walaupun kami tidak menyarankannya jika Anda seorang pemula), atau ingin menamai proyek Anda dengan nama lain, sintaksnya adalah:
       vue init [template-name] [project-name]
    • Arahkan ke direktori yang dibuat oleh vue-cli untuk proyek tersebut.
       cd vue_weather_dashboard
    • Instal semua paket yang disebutkan dalam package.json , yang telah dibuat oleh alat vue-cli untuk template webpack-simple .
       npm install
    • Mulai server pengembangan dan lihat proyek Vue default Anda bekerja di browser!
       npm run dev

Jika Anda baru mengenal Vue.js, luangkan waktu sejenak untuk menikmati pencapaian terbaru Anda — Anda telah membuat aplikasi Vue kecil dan menjalankannya di localhost:8080!

Tangkapan layar situs web Vue.js
(Pratinjau besar)

Penjelasan Singkat Tentang Struktur Proyek Default

Saatnya untuk melihat struktur di dalam direktori vue_weather_dashboard , sehingga Anda memiliki pemahaman dasar sebelum kita mulai memodifikasinya.

Strukturnya terlihat seperti ini:

 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

Meskipun mungkin tergoda untuk tidak mengenal file dan direktori default, jika Anda baru mengenal Vue, kami sangat menyarankan setidaknya melihat konten file. Ini bisa menjadi sesi pendidikan yang baik dan pertanyaan pemicu yang harus Anda kejar sendiri, terutama file-file berikut:

  • package.json , dan sekilas melihat sepupunya package-lock.json
  • webpack.config.js
  • index.html
  • src/main.js
  • src/App.vue

Penjelasan singkat dari masing-masing file dan direktori yang ditunjukkan pada diagram pohon diberikan di bawah ini:

  • README.md
    Tidak ada hadiah untuk menebak — terutama bagi manusia untuk membaca dan memahami langkah-langkah yang diperlukan untuk membuat perancah proyek.
  • simpul_modul/
    Ini adalah direktori tempat npm mengunduh paket-paket yang diperlukan untuk memulai proyek. Informasi tentang paket-paket yang diperlukan tersedia di file package.json .
  • package.json
    File ini dibuat oleh alat vue-cli berdasarkan persyaratan template webpack-simple , dan berisi informasi tentang paket npm (termasuk dengan versinya dan detail lainnya) yang harus diinstal. Perhatikan baik-baik isi file ini — di sinilah Anda harus mengunjungi dan mungkin mengedit untuk menambah/menghapus paket yang diperlukan untuk proyek tersebut, dan kemudian jalankan npm install. Baca lebih lanjut tentang package.json di sini.
  • paket-lock.json
    File ini dibuat oleh npm itu sendiri, dan terutama dimaksudkan untuk menyimpan log hal-hal yang diunduh dan diinstal oleh npm.
  • webpack.config.js
    Ini adalah file JavaScript yang berisi konfigurasi webpack — alat yang menggabungkan berbagai aspek proyek kita (kode, aset statis, konfigurasi, lingkungan, mode penggunaan, dll.), dan diperkecil sebelum disajikan kepada pengguna. Manfaatnya adalah semua hal diikat bersama secara otomatis, dan pengalaman pengguna meningkat pesat karena peningkatan kinerja aplikasi (halaman disajikan dengan cepat dan dimuat lebih cepat di browser). Seperti yang mungkin Anda temui nanti, ini adalah file yang perlu diperiksa ketika sesuatu dalam sistem pembangunan tidak berfungsi sebagaimana mestinya. Juga, ketika Anda ingin men-deploy aplikasi, ini adalah salah satu file kunci yang perlu diedit (baca selengkapnya di sini).
  • index.html
    File HTML ini berfungsi sebagai matriks (atau Anda dapat mengatakan, templat) di mana data dan kode akan disematkan secara dinamis (itulah yang terutama dilakukan Vue), dan kemudian disajikan kepada pengguna.
  • src/main.js
    File JavaScript ini berisi kode yang terutama mengelola dependensi tingkat atas/proyek, dan mendefinisikan komponen Vue tingkat paling atas. Singkatnya, ini mengatur JavaScript untuk seluruh proyek, dan berfungsi sebagai titik masuk aplikasi. Edit file ini ketika Anda perlu mendeklarasikan dependensi seluruh proyek pada modul node tertentu, atau Anda ingin sesuatu diubah tentang komponen Vue paling atas dalam proyek.
  • src/App.vue
    Pada poin sebelumnya, ketika kita berbicara tentang "komponen Vue teratas", kita pada dasarnya berbicara tentang file ini. Setiap file .vue dalam proyek adalah komponen, dan komponen terkait secara hierarkis. Pada awalnya, kami hanya memiliki satu file .vue , yaitu App.vue , sebagai satu-satunya komponen kami. Tetapi segera kami akan menambahkan lebih banyak komponen ke proyek kami (terutama mengikuti struktur dasbor), dan menautkannya sesuai dengan hierarki yang kami inginkan, dengan App.vue menjadi nenek moyang dari semuanya. File .vue ini akan berisi kode dalam format yang Vue ingin kita tulis. Jangan khawatir, itu adalah kode JavaScript yang ditulis untuk mempertahankan struktur yang dapat membuat kita tetap waras dan teratur. Anda telah diperingatkan — pada akhir proyek ini, jika Anda baru mengenal Vue, Anda mungkin kecanduan template — script — style template — script — style template — script — style cara mengatur kode!

Sekarang kita telah membuat fondasi, saatnya untuk:

  • Modifikasi template dan tweak file konfigurasi sedikit, sehingga proyek berperilaku seperti yang kita inginkan.
  • Buat file .vue baru, dan terapkan struktur dasbor dengan kode Vue.

Kita akan mempelajarinya di bagian berikutnya, yang akan agak panjang dan menuntut perhatian. Jika Anda membutuhkan kafein atau air, atau ingin buang air besar — ​​sekaranglah waktunya!

3. Menyesuaikan Struktur Proyek Default

Saatnya untuk mengutak-atik fondasi yang diberikan oleh proyek scaffolded kepada kita. Sebelum Anda mulai, pastikan server pengembangan yang disediakan oleh webpack sedang berjalan. Keuntungan menjalankan server ini terus- menerus adalah bahwa setiap perubahan yang Anda buat dalam kode sumber — yang Anda simpan dan segarkan halaman web — itu akan segera tercermin di browser.

Jika Anda ingin memulai server pengembangan, jalankan saja perintah berikut dari terminal (dengan asumsi direktori Anda saat ini adalah direktori proyek):

 npm run dev

Di bagian berikut, kami akan memodifikasi beberapa file yang ada, dan menambahkan beberapa file baru. Ini akan diikuti dengan penjelasan singkat tentang isi file-file itu, sehingga Anda memiliki gagasan tentang apa yang dimaksudkan untuk dilakukan perubahan itu.

Ubah File yang Ada

index.html

Aplikasi kami secara harfiah adalah aplikasi satu halaman, karena hanya ada satu halaman web yang ditampilkan di browser. Kita akan membicarakannya nanti, tapi pertama-tama mari kita buat perubahan pertama kita — mengubah teks di dalam tag <title> .

Dengan revisi kecil ini, file HTML terlihat seperti berikut:

 <!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>

Luangkan waktu sejenak untuk me-refresh halaman web di localhost:8080 , dan lihat perubahan yang tercermin pada bilah judul tab di browser — seharusnya tertulis “Vue Weather Dashboard”. Namun, ini hanya untuk menunjukkan kepada Anda proses membuat perubahan dan memverifikasi apakah itu berfungsi. Kami memiliki lebih banyak hal untuk dilakukan!

Halaman HTML sederhana ini kekurangan banyak hal yang kami inginkan dalam proyek kami, terutama yang berikut:

  • Beberapa informasi meta
  • Tautan CDN ke Bootstrap (kerangka CSS)
  • tautan ke lembar gaya khusus (belum ditambahkan dalam proyek)
  • Pointer ke Google Maps Geolocation API dari <script>

Setelah menambahkan hal-hal itu, index.html akhir memiliki konten berikut:

 <!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>

Simpan file, dan segarkan halaman web. Anda mungkin telah melihat sedikit tonjolan saat halaman sedang dimuat — ini terutama karena fakta bahwa gaya halaman sekarang dikendalikan oleh Bootstrap, dan elemen gaya seperti font, spasi, dll. berbeda dari default yang kami miliki sebelumnya (jika Anda tidak yakin, putar kembali ke default dan lihat perbedaannya).

Tangkapan layar saat Anda menyegarkan halaman web dengan localhost:8080
(Pratinjau besar)

Catatan : Satu hal penting sebelum kita melanjutkan — URL untuk Google Maps API berisi kunci yang merupakan properti FusionCharts. Untuk saat ini, Anda dapat menggunakan kunci ini untuk membangun proyek, karena kami tidak ingin Anda terjebak oleh jenis detail kecil ini (yang dapat menjadi gangguan saat Anda baru). Namun, kami sangat menyarankan Anda untuk membuat dan menggunakan kunci Google Maps API Anda sendiri setelah Anda membuat beberapa kemajuan dan merasa nyaman untuk memperhatikan detail kecil ini.

package.json

Pada saat penulisan ini, kami menggunakan versi tertentu dari paket npm untuk proyek kami, dan kami tahu pasti bahwa hal-hal itu bekerja sama. Namun, pada saat Anda menjalankan proyek, sangat mungkin bahwa versi stabil terbaru dari paket yang diunduh npm untuk Anda tidak sama dengan yang kami gunakan, dan ini mungkin merusak kode (atau melakukan hal-hal yang melampaui kendali kami). Jadi, sangat penting untuk memiliki file package.json yang sama persis dengan yang digunakan untuk membangun proyek ini, sehingga kode/penjelasan kami dan hasil yang Anda dapatkan konsisten.

Isi file package.json harus:

 { "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" } }

Kami mendorong Anda untuk menelusuri package.json baru, dan mencari tahu apa fungsi dari objek yang berbeda di json. Anda mungkin lebih suka mengubah nilai kunci “ author ” menjadi nama Anda. Juga, paket-paket yang disebutkan dalam dependensi akan muncul sendiri pada waktu yang tepat dalam kode. Untuk saat ini, cukup diketahui bahwa:

  • paket terkait babel adalah untuk menangani kode gaya ES6 dengan benar oleh browser;
  • axios menangani permintaan HTTP berbasis Janji;
  • moment dan zona waktu momen adalah untuk manipulasi tanggal/waktu;
  • fusioncharts dan vue-fusioncharts bertanggung jawab untuk merender grafik:
  • vue , untuk alasan yang jelas.

webpack.config.js

Seperti package.json , kami menyarankan Anda untuk memelihara file webpack.config.js yang konsisten dengan yang kami gunakan untuk membangun proyek. Namun, sebelum melakukan perubahan apa pun, kami menyarankan Anda untuk membandingkan dengan cermat kode default di webpack.config.js , dan kode yang telah kami sediakan di bawah ini. Anda akan melihat beberapa perbedaan — google mereka dan memiliki ide dasar tentang apa artinya. Karena menjelaskan konfigurasi webpack secara mendalam berada di luar cakupan artikel ini, Anda bertanggung jawab sendiri dalam hal ini.

File webpack.config.js yang disesuaikan adalah sebagai berikut:

 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 }) ]) }

Dengan perubahan yang dibuat pada webpack.config.js proyek, Anda harus menghentikan server pengembangan yang sedang berjalan ( Ctrl + C ), dan memulai kembali dengan perintah berikut yang dijalankan dari direktori proyek setelah menginstal semua paket yang disebutkan dalam package.json file package.json :

 npm install npm run dev

Dengan ini, cobaan mengutak-atik konfigurasi dan memastikan bahwa paket yang tepat sudah ada berakhir. Namun, ini juga menandai perjalanan memodifikasi dan menulis kode, yang agak panjang tetapi juga sangat bermanfaat!

src/main.js

File ini adalah kunci untuk orkestrasi tingkat atas proyek — di sinilah kami mendefinisikan:

  • Apa dependensi tingkat atas (di mana mendapatkan paket npm paling penting yang diperlukan);
  • Cara mengatasi dependensi, bersama dengan instruksi ke Vue tentang penggunaan plugin/pembungkus, jika ada;
  • Instance Vue yang mengelola komponen teratas dalam proyek: src/App.vue (file .vue nodal).

Sejalan dengan tujuan kami untuk file src/main.js , kodenya harus:

 // 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

Ini adalah salah satu file terpenting di seluruh proyek, dan mewakili komponen teratas dalam hierarki — seluruh aplikasi itu sendiri, secara keseluruhan. Untuk proyek kami, komponen ini akan melakukan semua pengangkatan berat, yang akan kami jelajahi nanti. Untuk saat ini, kami ingin menyingkirkan boilerplate default, dan menempatkan sesuatu milik kami sendiri.

Jika Anda baru mengenal cara Vue dalam mengatur kode, akan lebih baik untuk mendapatkan gambaran tentang struktur umum di dalam file .vue . File .vue terdiri dari tiga bagian:

  • Templat
    Di sinilah template HTML untuk halaman ditentukan. Selain HTML statis, bagian ini juga berisi cara Vue menyematkan konten dinamis, menggunakan kurung kurawal ganda {{ }} .
  • Naskah
    JavaScript mengatur bagian ini, dan bertanggung jawab untuk menghasilkan konten dinamis yang berjalan dan berada di dalam template HTML di tempat yang sesuai. Bagian ini terutama merupakan objek yang diekspor, dan terdiri dari:
    • Data
      Ini adalah fungsi itu sendiri, dan biasanya mengembalikan beberapa data yang diinginkan yang dienkapsulasi dalam struktur data yang bagus.
    • Metode
      Objek yang terdiri dari satu atau lebih fungsi/metode, yang masing-masing biasanya memanipulasi data dengan cara tertentu, dan juga mengontrol konten dinamis dari template HTML.
    • dihitung
      Sama seperti objek metode yang dibahas di atas dengan satu perbedaan penting — sementara semua fungsi dalam objek metode dieksekusi setiap kali salah satu dari mereka dipanggil, fungsi dalam objek yang dihitung berperilaku jauh lebih masuk akal, dan dijalankan jika dan hanya jika telah ditelepon.
  • Gaya
    Bagian ini untuk penataan CSS yang berlaku untuk HTML halaman (ditulis dalam template) — letakkan CSS lama yang bagus di sini untuk membuat halaman Anda indah!

Dengan mengingat paradigma di atas, mari kita sesuaikan kode secara minimal di 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>

Ingat bahwa cuplikan kode di atas hanya untuk menguji bahwa App.vue bekerja dengan kode kita sendiri di dalamnya. Nantinya akan melalui banyak perubahan, tetapi pertama-tama simpan file dan segarkan halaman di browser.

Tangkapan layar browser dengan pesan "Kode komponen ini ada di App.vue"
(Pratinjau besar)

Pada titik ini, mungkin ide yang baik untuk mendapatkan bantuan dalam perkakas. Lihat Vue devtools untuk Chrome, dan jika Anda tidak memiliki banyak masalah dalam menggunakan Google Chrome sebagai browser default untuk pengembangan, instal alat dan mainkan sedikit. Ini akan sangat berguna untuk pengembangan dan debugging lebih lanjut, ketika segalanya menjadi lebih rumit.

Direktori dan File Tambahan

Langkah selanjutnya adalah menambahkan file tambahan, sehingga struktur proyek kita menjadi lengkap. Kami akan menambahkan direktori dan file berikut:

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

Catatan : Simpan file .svg hyperlink di proyek Anda.

Buat direktori dan file yang disebutkan di atas. Struktur proyek akhir akan terlihat seperti (ingat untuk menghapus folder dan file dari struktur default yang sekarang tidak diperlukan):

 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

Mungkin ada beberapa file lain, seperti .babelrc , .gitignore , .editorconfig , dll. di folder root proyek. Anda dapat mengabaikannya dengan aman untuk saat ini.

Di bagian berikut, kami akan menambahkan konten minimal ke file yang baru ditambahkan, dan menguji apakah mereka berfungsi dengan baik.

src/css/style.css

Meskipun tidak akan banyak berguna segera, salin kode berikut ke file:

 @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/aset/

Di direktori ini, unduh dan simpan file .svg yang disebutkan di bawah ini:

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

src/components/Content.vue

Inilah yang kami sebut "komponen bodoh" (yaitu pengganti) yang ada hanya untuk mempertahankan hierarki, dan pada dasarnya meneruskan data ke komponen turunannya.

Ingatlah bahwa tidak ada bilah teknis untuk menulis semua kode kami di file App.vue , tetapi kami mengambil pendekatan untuk memisahkan kode dengan menyusun komponen karena dua alasan:

  • Untuk menulis kode bersih, yang membantu keterbacaan dan pemeliharaan;
  • Untuk mereplikasi struktur yang sama yang akan kita lihat di layar, yaitu hierarki.

Sebelum kita menyarangkan komponen yang didefinisikan di Content.vue di dalam komponen root App.vue , mari kita tulis beberapa kode mainan (tetapi mendidik) untuk 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>

Dalam kode, perhatikan dan pahami dengan cermat hal-hal berikut:

  • Di dalam <script> (di mana kita jelas menulis beberapa kode JavaScript), kita mendefinisikan objek yang diekspor (disediakan untuk file lain) secara default. Objek ini berisi fungsi data() , yang mengembalikan objek array yang disebut childComponents , dengan elemennya adalah nama file komponen yang harus disarangkan lebih lanjut.
  • Di dalam <template> (tempat kita menulis beberapa template HTML), hal yang menarik adalah <ul> .
    • Di dalam daftar yang tidak berurutan, setiap item daftar harus berupa nama komponen turunan yang dimaksud, seperti yang didefinisikan dalam objek larik childComponents . Selain itu, daftar harus secara otomatis diperpanjang hingga elemen terakhir dari array. Sepertinya kita harus menulis for -loop, bukan? Kami melakukannya dengan menggunakan direktif v-for disediakan oleh Vue.js. Direktif v-for :
      • Bertindak sebagai atribut dari tag <li> , melakukan iterasi melalui array, membuat nama komponen turunan di mana iterator disebutkan dalam tanda kurung {{ }} (di mana kita menulis teks untuk item daftar).

Kode dan penjelasan di atas membentuk dasar pemahaman Anda selanjutnya tentang bagaimana skrip dan templat saling terkait, dan bagaimana kami dapat menggunakan arahan yang disediakan oleh Vue.js.

Kami telah belajar cukup banyak, tetapi bahkan setelah semua ini, kami memiliki satu hal yang tersisa untuk dipelajari tentang menghubungkan komponen dalam hierarki dengan mulus — meneruskan data dari komponen induk ke turunannya. Untuk saat ini, kita perlu mempelajari cara meneruskan beberapa data dari src/App.vue ke src/components/Content.vue , sehingga kita dapat menggunakan teknik yang sama untuk sisa komponen yang bersarang dalam proyek ini.

Data yang mengalir turun dari komponen induk ke komponen anak mungkin terdengar sederhana, tetapi detailnya sangat buruk! Seperti yang dijelaskan secara singkat di bawah ini, ada beberapa langkah yang terlibat dalam membuatnya bekerja:

  • Mendefinisikan dan data
    Untuk saat ini, kami ingin beberapa data statis untuk dimainkan — objek yang berisi nilai hard-code tentang berbagai aspek cuaca akan baik-baik saja! Kami membuat objek bernama weather_data dan mengembalikannya dari fungsi data() App.vue . Objek weather_data diberikan dalam cuplikan di bawah ini:
 weather_data: { location: "California", temperature: { current: "35 C", }, highlights: { uvindex: "3", windstatus: { speed: "20 km/h", direction: "NE", }, visibility: "12 km", }, },
  • Melewati data dari orang tua
    Untuk melewatkan data, kita memerlukan tujuan di mana kita ingin mengirim data! Dalam hal ini, tujuannya adalah komponen Content.vue , dan cara mengimplementasikannya adalah dengan:
    • Tetapkan objek weather_data ke atribut khusus dari tag <Content>
    • Ikat atribut dengan data menggunakan direktif v-bind : yang disediakan oleh Vue.js, yang membuat nilai atribut menjadi dinamis (responsif terhadap perubahan yang dibuat pada data asli).
       <Content v-bind:weather_data=“weather_data”></Content>

Mendefinisikan dan meneruskan data ditangani di sisi sumber jabat tangan, yang dalam kasus kami adalah file App.vue .

Kode untuk file App.vue , pada statusnya saat ini, diberikan di bawah ini:

 <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> 
Tangkapan layar browser dengan pesan “Kode komponen ini ada di App.vue. Komponen turunan dari Content.vue ini adalah: TempVarChart.vue, Highlights.vue”
(Pratinjau besar)

Dengan data yang ditentukan dan diteruskan dari sumber (komponen induk), sekarang tanggung jawab anak untuk menerima data dan merendernya dengan tepat, seperti yang dijelaskan dalam dua langkah berikutnya.

  • Menerima data oleh anak
    Komponen turunan, dalam hal ini Content.vue , harus menerima objek weather_data yang dikirimkan kepadanya oleh komponen induk App.vue . Vue.js menyediakan mekanisme untuk melakukannya — yang Anda butuhkan hanyalah objek array bernama props , yang didefinisikan dalam objek default yang diekspor oleh Content.vue . Setiap elemen dari array props adalah nama dari objek data yang ingin diterima dari induknya. Untuk saat ini, satu-satunya objek data yang seharusnya diterima adalah weather_data dari App.vue. Dengan demikian, array props terlihat seperti:
 <template> // HTML template code here </template> <script> export default { props: ["weather_data"], data () { return { // data here } }, } </script> <style> // component specific CSS here </style>
  • Merender data di halaman
    Sekarang setelah kita memastikan menerima data, tugas terakhir yang perlu kita selesaikan adalah merender data. Untuk contoh ini, kami akan langsung membuang data yang diterima di halaman web, hanya untuk mengilustrasikan tekniknya. Namun, dalam aplikasi nyata (seperti yang akan kita buat), data biasanya melewati banyak pemrosesan, dan hanya bagian yang relevan yang ditampilkan dengan cara yang sesuai dengan tujuannya. Misalnya, dalam proyek ini kita pada akhirnya akan mendapatkan data mentah dari API cuaca, membersihkan dan memformatnya, memasukkan data ke struktur data yang diperlukan untuk bagan, dan kemudian memvisualisasikannya. Bagaimanapun, untuk menampilkan dump data mentah, kita hanya akan menggunakan tanda kurung {{ }} yang dipahami Vue, seperti yang ditunjukkan pada cuplikan di bawah ini:
 <template> <div> // other template code here {{ weather_data }} </div> </template>

Sekarang saatnya untuk mengasimilasi semua bagian. Kode untuk Content.vue — pada statusnya saat ini — diberikan di bawah ini:

 <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> 
Tangkapan layar browser dengan hasil kode yang diberikan
(Pratinjau besar)

Setelah melakukan perubahan yang dibahas di atas, segarkan halaman web di browser dan lihat tampilannya. Luangkan waktu sejenak untuk menghargai kerumitan yang ditangani Vue — jika Anda memodifikasi objek weather_data di App.vue , objek tersebut akan dikirimkan secara diam-diam ke Content.vue , dan akhirnya ke browser yang menampilkan halaman web! Coba dengan mengubah nilai untuk lokasi kunci.

Meskipun kita telah mempelajari tentang props dan pengikatan data menggunakan data statis, kita akan menggunakan data dinamis yang dikumpulkan menggunakan API web dalam aplikasi, dan akan mengubah kode yang sesuai .

Ringkasan

Sebelum kita melanjutkan ke file .vue , mari kita rangkum apa yang telah kita pelajari saat menulis kode untuk App.vue dan components/Content.vue :

  • File App.vue adalah apa yang kami sebut komponen root — yang berada di bagian atas hierarki komponen. Sisa file .vue mewakili komponen yang merupakan turunan langsungnya, cucunya, dan seterusnya.
  • File Content.vue adalah komponen dummy — tanggung jawabnya adalah meneruskan data ke level di bawahnya dan mempertahankan hierarki struktural, sehingga kode kita tetap konsisten dengan filosofi "*apa yang kita lihat adalah apa yang kita terapkan*".
  • Hubungan induk-anak dari komponen tidak terjadi begitu saja — Anda harus mendaftarkan komponen (baik secara global maupun lokal, tergantung pada tujuan penggunaan komponen), dan kemudian menyusunnya menggunakan tag HTML khusus (yang ejaannya sama persis sama dengan nama-nama yang komponennya telah didaftarkan).
  • Setelah didaftarkan dan disarangkan, data diteruskan dari komponen induk ke komponen anak, dan alurnya tidak pernah terbalik (hal buruk akan terjadi jika arsitektur proyek mengizinkan arus balik). Komponen induk adalah sumber relatif data, dan komponen tersebut menurunkan data yang relevan kepada turunannya menggunakan direktif v-bind untuk atribut elemen HTML khusus. Anak menerima data yang dimaksudkan untuk itu menggunakan alat peraga, dan kemudian memutuskan sendiri apa yang harus dilakukan dengan data tersebut.

Untuk komponen lainnya, kami tidak akan memberikan penjelasan rinci — kami hanya akan menulis kode berdasarkan pembelajaran dari ringkasan di atas. Kode akan jelas, dan jika Anda bingung tentang hierarki, lihat diagram di bawah ini:

Diagram yang menjelaskan hierarki kode
(Pratinjau besar)

Diagram mengatakan bahwa TempVarChart.vue dan Highlights.vue adalah anak langsung dari Content.vue . Oleh karena itu, ada baiknya menyiapkan Content.vue untuk mengirimkan data ke komponen tersebut, yang kami lakukan dengan menggunakan kode di bawah ini:

 <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>

Setelah Anda menyimpan kode ini, Anda akan mendapatkan kesalahan — jangan khawatir, itu sudah diperkirakan. Ini akan diperbaiki setelah Anda menyiapkan file komponen lainnya. Jika itu mengganggu Anda karena tidak dapat melihat hasilnya, beri komentar pada baris yang berisi tag elemen khusus <temp-var-chart> dan <today-highlights> .

Untuk bagian ini, ini adalah kode terakhir dari Content.vue . Untuk sisa bagian ini, kami akan merujuk ke kode ini , dan bukan yang sebelumnya kami tulis untuk dipelajari.

src/components/TempVarChart.vue

Dengan komponen induknya Content.vue meneruskan data, TempVarChart.vue harus diatur untuk menerima dan merender data, seperti yang ditunjukkan pada kode di bawah ini:

 <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

Komponen ini juga akan menerima data dari App.vue — komponen induknya. Setelah itu, itu harus ditautkan dengan komponen turunannya, dan data yang relevan harus diteruskan ke mereka.

Pertama-tama mari kita lihat kode untuk menerima data dari induknya:

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

Pada titik ini, halaman web terlihat seperti gambar di bawah ini:

Hasil kode yang ditampilkan di browser
(Pratinjau besar)

Sekarang kita perlu memodifikasi kode Highlights.vue untuk mendaftar dan menyarangkan komponen turunannya, diikuti dengan meneruskan data ke turunan. Kode untuknya adalah sebagai berikut:

 <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>

Setelah Anda menyimpan kode dan melihat halaman web, Anda diharapkan melihat kesalahan di alat Konsol Pengembang yang disediakan oleh browser; mereka muncul karena meskipun Highlights.vue mengirim data, tidak ada yang menerimanya. Kami belum menulis kode untuk anak- anak dari Highlights.vue .

Perhatikan bahwa kami belum melakukan banyak pemrosesan data, yaitu, kami belum mengekstrak faktor individual dari data cuaca yang berada di bawah bagian Sorotan di dasbor. Kita bisa melakukannya di fungsi data() , tapi kita lebih suka untuk menjaga Highlights.vue sebagai komponen bodoh yang hanya meneruskan seluruh dump data yang diterimanya ke masing-masing anak, yang kemudian memiliki ekstrak mereka sendiri apa yang diperlukan untuk mereka . Namun, kami mendorong Anda untuk mencoba mengekstrak data di Highlights.vue , dan mengirimkan data yang relevan ke setiap komponen turunan — ini tetap merupakan latihan praktik yang baik!

src/components/UVIndex.vue

Kode untuk komponen ini menerima dump data sorotan dari Highlights.vue , mengekstrak data untuk Indeks UV, dan menampilkannya di halaman.

 <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

Kode untuk komponen ini menerima dump data sorotan dari Highlights.vue , mengekstrak data untuk Visibility, dan menampilkannya di halaman.

 <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

Kode untuk komponen ini menerima dump data sorotan dari Highlights.vue , mengekstrak data untuk Status Angin (kecepatan dan arah), dan menampilkannya di halaman.

 <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>

Setelah menambahkan kode untuk semua komponen, lihat halaman web di browser.

Hasil kode yang ditampilkan di browser
(Pratinjau besar)

Bukan untuk berkecil hati, tetapi semua kerja keras ini hanya untuk menghubungkan komponen-komponen dalam hierarki, dan menguji apakah aliran data terjadi di antara mereka atau tidak! Di bagian selanjutnya, kita akan membuang sebagian besar kode yang telah kita tulis sejauh ini , dan menambahkan lebih banyak lagi yang berkaitan dengan proyek yang sebenarnya. Namun, kami pasti akan mempertahankan struktur dan susunan komponen; pembelajaran dari bagian ini akan memungkinkan kita membangun dasbor yang layak dengan Vue.js.

4. Akuisisi dan Pemrosesan Data

Ingat objek weather_data di App.vue ? Itu memiliki beberapa data hard-code yang kami gunakan untuk menguji apakah semua komponen berfungsi dengan benar, dan juga untuk membantu Anda mempelajari beberapa aspek dasar aplikasi Vue tanpa terjebak dalam detail data dunia nyata. Namun, sekarang saatnya kita melepaskan cangkang kita, dan melangkah ke dunia nyata, di mana data dari API akan mendominasi sebagian besar kode kita.

Mempersiapkan Komponen Anak Untuk Menerima Dan Memproses Data Nyata

Di bagian ini, Anda akan mendapatkan kode dump untuk semua komponen kecuali App.vue . Kode akan menangani penerimaan data nyata dari App.vue (tidak seperti kode yang kami tulis di bagian sebelumnya untuk menerima dan merender data dummy).

Kami sangat menganjurkan untuk membaca kode setiap komponen dengan saksama, sehingga Anda membentuk gagasan tentang data apa yang diharapkan oleh masing-masing komponen tersebut, dan pada akhirnya akan digunakan dalam visualisasi.

Beberapa kode, dan struktur keseluruhan, akan mirip dengan yang Anda lihat di struktur sebelumnya — jadi Anda tidak akan menghadapi sesuatu yang berbeda secara drastis. Namun, iblis ada dalam detailnya! Jadi, periksa kodenya dengan cermat, dan ketika Anda telah memahaminya dengan cukup baik, salin kode tersebut ke masing-masing file komponen dalam proyek Anda.

Catatan : Semua komponen di bagian ini ada di direktori src/components/ . Jadi setiap kali, path tidak akan disebutkan — hanya nama file .vue yang akan disebutkan untuk mengidentifikasi komponen.

Konten.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>

Perubahan berikut telah dibuat dari kode sebelumnya:

  • Dalam <template> , teks dan data dalam {{ }} telah dihapus, karena kami sekarang hanya menerima data dan meneruskannya ke turunan, tanpa merender komponen ini secara spesifik.
  • Dalam export default {} :
    • Alat props telah diubah agar sesuai dengan objek data yang akan dikirim oleh induknya: App.vue . Alasan untuk mengubah alat peraga adalah karena App.vue sendiri akan menampilkan beberapa data yang diperolehnya dari API cuaca dan sumber daya online lainnya, berdasarkan permintaan pencarian pengguna, dan meneruskan data lainnya. Dalam kode dummy yang kami tulis sebelumnya, App.vue meneruskan seluruh dump data dummy, tanpa diskriminasi apa pun, dan props dari Content.vue telah diatur sesuai dengan itu.
    • Fungsi data() sekarang tidak mengembalikan apa pun, karena kami tidak melakukan manipulasi data apa pun dalam komponen ini.

TempVarChart.vue

Komponen ini seharusnya menerima proyeksi suhu terperinci untuk sisa hari ini, dan akhirnya menampilkannya menggunakan FusionCharts. Namun untuk saat ini, kami hanya akan menampilkannya sebagai teks di halaman web.

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

Sorotan.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>

Perubahan yang dilakukan dari kode sebelumnya adalah:

  • Dalam <template> , teks dan data di dalam {{ }} telah dihapus, karena ini adalah komponen bodoh, seperti Content.vue , yang tugasnya hanya meneruskan data ke anak-anak sambil mempertahankan hierarki struktural. Ingatlah bahwa komponen bodoh seperti Highlights.vue dan Content.vue ada untuk menjaga keseimbangan antara struktur visual dasbor, dan kode yang kita tulis.

UVIndex.vue

Perubahan yang dilakukan pada kode sebelumnya adalah sebagai berikut:

  • Di <template> dan <style> , div id telah diubah menjadi uvIndex , yang lebih mudah dibaca.
  • Dalam export default {} , fungsi data() sekarang mengembalikan objek string uvIndex , yang nilainya diekstraksi dari objek sorotan yang diterima oleh komponen menggunakan props . uvIndex ini sekarang digunakan sementara untuk menampilkan nilai sebagai teks di dalam <template> . Nanti, kami akan memasukkan nilai ini ke struktur data yang cocok untuk merender bagan.

Visibilitas.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>

Satu-satunya perubahan yang dibuat dalam file ini (berkenaan dengan kode sebelumnya) adalah bahwa definisi objek visibility yang dikembalikan oleh fungsi data() sekarang berisi toString() di ujungnya, karena nilai yang diterima dari induk akan menjadi floating nomor titik, yang perlu diubah menjadi string.

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>

Perubahan yang dilakukan pada kode sebelumnya adalah sebagai berikut:

  • Di seluruh file, windstatus telah diubah namanya menjadi windStatus , untuk meningkatkan keterbacaan dan juga agar sinkron dengan objek sorotan yang App.vue dengan data aktual.
  • Perubahan penamaan serupa telah dibuat untuk kecepatan dan arah — yang baru adalah windSpeed ​​dan windDirection .
  • Sebuah objek baru yang derivedWindDirection telah ikut bermain (juga disediakan oleh App.vue dalam bundel sorotan).

Untuk saat ini, data yang diterima ditampilkan sebagai teks; nanti, itu akan dicolokkan ke struktur data yang diperlukan untuk visualisasi.

Menguji Dengan Data Dummy

Menggunakan data dummy berulang kali mungkin sedikit membuat Anda frustrasi, tetapi ada beberapa alasan bagus di baliknya:

  • Kami telah membuat banyak perubahan pada kode setiap komponen, dan ada baiknya untuk menguji apakah perubahan tersebut melanggar kode. Dengan kata lain, kita harus memeriksa apakah aliran datanya utuh, sekarang kita akan pindah ke bagian proyek yang lebih kompleks.
  • Data nyata dari API cuaca online akan membutuhkan banyak pemijatan, dan mungkin akan sangat merepotkan bagi Anda untuk beralih antara kode untuk akuisisi dan pemrosesan data, dan kode untuk kelancaran aliran data ke komponen. Idenya adalah untuk menjaga kuantum kompleksitas di bawah kendali, sehingga kita memiliki pemahaman yang lebih baik tentang kesalahan yang mungkin kita hadapi.

Di bagian ini, apa yang kami lakukan pada dasarnya adalah hardcode beberapa data json di App.vue , yang jelas akan diganti dengan data langsung dalam waktu dekat. Ada banyak kesamaan antara struktur json dummy, dan struktur json yang akan kita gunakan untuk data aktual. Jadi ini juga memberi Anda gambaran kasar tentang apa yang diharapkan dari data nyata, begitu kami menemukannya.

Namun, kami akui bahwa ini jauh dari pendekatan ideal yang mungkin diterapkan seseorang saat membangun proyek semacam itu dari awal. Di dunia nyata, Anda akan sering memulai dengan sumber data nyata, bermain-main dengannya sedikit untuk memahami apa yang dapat dan harus dilakukan untuk menjinakkannya, dan kemudian memikirkan struktur data json yang sesuai untuk menangkap informasi yang relevan. Kami sengaja melindungi Anda dari semua pekerjaan kotor itu, karena itu membawa Anda lebih jauh dari tujuan — mempelajari cara menggunakan Vue.js dan FusionCharts untuk membuat dasbor.

Sekarang mari kita masuk ke kode baru untuk 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>

Perubahan yang dilakukan pada kode sehubungan dengan versi sebelumnya adalah sebagai berikut:

  • Nama komponen turunan telah diubah menjadi konten-dasbor, dan karenanya elemen HTML khusus di <template> telah direvisi. Perhatikan bahwa sekarang kami memiliki dua atribut — highlights dan tempVar — alih-alih satu atribut yang kami gunakan sebelumnya dengan elemen khusus. Dengan demikian, data yang terkait dengan atribut tersebut juga telah berubah. Yang menarik di sini adalah kita dapat menggunakan direktif v-bind: :, atau singkatannya : (seperti yang telah kita lakukan di sini), dengan banyak atribut dari elemen HTML khusus!
  • Fungsi data() sekarang mengembalikan objek filename (yang ada sebelumnya), bersama dengan dua objek baru (bukan yang lama weather_data ): tempVar dan highlights . Struktur json sesuai untuk kode yang telah kita tulis di komponen turunan, sehingga mereka dapat mengekstrak potongan data yang mereka butuhkan dari dump. Strukturnya cukup jelas, dan Anda dapat mengharapkannya menjadi sangat mirip ketika kita berurusan dengan data langsung. Namun, perubahan signifikan yang akan Anda temui adalah tidak adanya hardcoding (jelas, bukan) — kami akan membiarkan nilai kosong sebagai status default, dan menulis kode untuk memperbaruinya secara dinamis berdasarkan nilai yang akan kami terima dari API cuaca.

Anda telah menulis banyak kode di bagian ini, tanpa melihat output yang sebenarnya. Sebelum Anda melangkah lebih jauh, lihat browser (restart server dengan npm run dev , jika perlu), dan nikmati kejayaan pencapaian Anda. Halaman web yang akan Anda lihat pada saat ini terlihat seperti gambar di bawah ini:

Hasil kode yang ditampilkan di browser
(Pratinjau besar)

Kode Untuk Akuisisi Dan Pemrosesan Data

Bagian ini akan menjadi inti dari proyek, dengan semua kode yang akan ditulis di App.vue sebagai berikut:

  • Masukan lokasi dari pengguna — kotak masukan dan tombol ajakan bertindak sudah cukup;
  • Fungsi utilitas untuk berbagai tugas; fungsi-fungsi ini akan dipanggil nanti di berbagai bagian kode komponen;
  • Mendapatkan data geolokasi terperinci dari Google Maps API untuk JavaScript;
  • Mendapatkan data cuaca terperinci dari Dark Sky API;
  • Memformat dan memproses data geolokasi dan cuaca, yang akan diteruskan ke komponen turunan.

Subbagian berikut mengilustrasikan bagaimana kita dapat mengimplementasikan tugas-tugas yang ditetapkan untuk kita dalam poin-poin di atas. Dengan beberapa pengecualian, kebanyakan dari mereka akan mengikuti urutan.

Masukan Dari Pengguna

Cukup jelas bahwa tindakan dimulai ketika pengguna memberikan nama tempat data cuaca perlu ditampilkan. Agar hal ini terjadi, kita perlu menerapkan hal-hal berikut:

  • Kotak input untuk memasuki lokasi;
  • Tombol kirim yang memberi tahu aplikasi kita bahwa pengguna telah memasuki lokasi dan saatnya untuk melakukan sisanya. Kami juga akan menerapkan perilaku saat pemrosesan dimulai setelah menekan Enter .

Kode yang kami tampilkan di bawah ini akan dibatasi pada bagian template HTML dari App.vue . Kami hanya akan menyebutkan nama metode yang terkait dengan peristiwa klik, dan mendefinisikannya nanti di objek metode <script> di 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>

Menempatkan cuplikan di atas di tempat yang tepat adalah hal yang sepele — kami serahkan kepada Anda. Namun, bagian yang menarik dari cuplikan tersebut adalah:

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

Seperti yang Anda ketahui dari bagian sebelumnya, @ adalah singkatan dari Vue untuk directive v-on :, yang diasosiasikan dengan beberapa event. Hal baru adalah “ organizeAllDetails ” — tidak lain adalah metode yang akan diaktifkan setelah peristiwa (menekan Enter atau mengklik tombol) terjadi. Kami belum menentukan metode, dan teka-teki akan selesai pada akhir bagian ini.

Tampilan Informasi Teks Dikendalikan Oleh App.vue

Setelah input pengguna memicu tindakan dan banyak data diperoleh dari API, kami menghadapi pertanyaan yang tak terhindarkan — “Apa yang harus dilakukan dengan semua data ini?”. Jelas diperlukan beberapa pemijatan data, tetapi itu tidak menjawab pertanyaan kami sepenuhnya! Kita perlu memutuskan apa kegunaan akhir dari data, atau lebih tepatnya, entitas mana yang menerima potongan berbeda dari data yang diperoleh dan diproses?

Komponen turunan dari App.vue , berdasarkan hierarki dan tujuannya, adalah pesaing terdepan untuk sebagian besar data. Namun, kami juga akan memiliki beberapa data yang bukan milik salah satu komponen turunan tersebut, namun cukup informatif dan melengkapi dasbor. Kita dapat memanfaatkannya dengan baik jika kita menampilkannya sebagai informasi teks yang dikontrol langsung oleh App.vue , sementara data lainnya diteruskan ke anak untuk ditampilkan sebagai grafik cantik pada akhirnya.

Dengan mengingat konteks ini, mari kita fokus pada kode untuk mengatur tahapan penggunaan data teks. Ini adalah template HTML sederhana pada titik ini, di mana data pada akhirnya akan datang dan duduk.

 <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>

Dalam cuplikan di atas, Anda harus memahami hal berikut:

  • Hal-hal di dalam {{ }} — itu adalah cara Vue memasukkan data dinamis ke dalam template HTML, sebelum dirender di browser. Anda pernah bertemu mereka sebelumnya, dan tidak ada yang baru atau mengejutkan. Perlu diingat bahwa objek data ini berasal dari metode data() di objek export default() App.vue . Mereka memiliki nilai default yang akan kami atur sesuai dengan kebutuhan kami, dan kemudian menulis metode tertentu untuk mengisi objek dengan data API nyata.

Jangan khawatir karena tidak melihat perubahan pada browser — data belum ditentukan, dan wajar jika Vue tidak merender hal-hal yang tidak diketahuinya. Namun, setelah data disetel (dan untuk saat ini, Anda bahkan dapat memeriksa dengan mengkodekan data secara hard-coding), data teks akan dikontrol oleh App.vue .

data() Metode

Metode data() adalah konstruksi khusus dalam file .vue — metode ini berisi dan mengembalikan objek data yang sangat penting untuk aplikasi. Ingat kembali struktur generik dari bagian <script> di file .vue apa pun — kira-kira berisi yang berikut ini:

 <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>

Sejauh ini, Anda telah menemukan nama beberapa objek data, tetapi lebih banyak lagi. Kebanyakan dari mereka relevan untuk komponen anak, yang masing-masing menangani aspek yang berbeda dari dump informasi cuaca. Diberikan di bawah ini adalah seluruh metode data() yang kita perlukan untuk proyek ini — Anda akan memiliki gagasan yang adil tentang data apa yang kita harapkan dari API, dan bagaimana kita menyebarkan data, berdasarkan nomenklatur objek.

 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: '' }, } }; },

Seperti yang Anda lihat, dalam kebanyakan kasus, nilai defaultnya kosong, karena itu sudah cukup untuk saat ini. Metode akan ditulis untuk memanipulasi data dan mengisinya dengan nilai yang sesuai, sebelum dirender atau diteruskan ke komponen turunan.

Metode di App.vue

Untuk file .vue , metode umumnya ditulis sebagai nilai kunci yang disarangkan dalam objek methods { } . Peran utama mereka adalah untuk memanipulasi objek data dari komponen. Kami akan menulis metode di App.vue dengan tetap mengingat filosofi yang sama. Namun, berdasarkan tujuannya, kami dapat mengkategorikan metode App.vue menjadi berikut:

  • Metode utilitas
  • Metode berorientasi aksi/acara
  • Metode akuisisi data
  • Metode pemrosesan data
  • Metode lem tingkat tinggi

Penting bagi Anda untuk memahami hal ini — kami menyajikan metode ini kepada Anda di atas piring karena kami telah mengetahui cara kerja API, data apa yang mereka berikan, dan bagaimana kami harus menggunakan data dalam proyek kami. Bukannya kami menarik metode itu keluar dari udara tipis, dan menulis beberapa kode misterius untuk menangani data. Untuk tujuan pembelajaran, merupakan latihan yang baik untuk rajin membaca dan memahami kode untuk metode dan data. Namun, ketika dihadapkan dengan proyek baru yang harus Anda bangun dari awal, Anda harus melakukan semua pekerjaan kotor itu sendiri, dan itu berarti banyak bereksperimen dengan API — akses terprogram dan struktur datanya, sebelum merekatkannya secara mulus dengan data struktur yang dituntut proyek Anda. Anda tidak akan memiliki pegangan tangan, dan akan ada saat-saat yang membuat frustrasi, tetapi itu semua adalah bagian dari pendewasaan sebagai pengembang.

Dalam subbagian berikut, kami akan menjelaskan masing-masing jenis metode, dan juga menunjukkan penerapan metode yang termasuk dalam kategori tersebut. Nama metode cukup jelas tentang tujuannya, dan begitu juga implementasinya, yang menurut kami cukup mudah untuk diikuti. Namun, sebelum itu, ingat kembali skema umum metode penulisan dalam file .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 Utilitas

Metode utilitas, seperti namanya, adalah metode yang ditulis terutama untuk tujuan memodulasi kode berulang yang digunakan untuk tugas sampingan. Mereka dipanggil dengan metode lain bila perlu. Diberikan di bawah ini adalah metode utilitas untuk 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; },

Meskipun kami belum menerapkannya, Anda dapat mengambil metode utilitas dari file .vue , dan meletakkannya di file JavaScript terpisah. Yang perlu Anda lakukan adalah mengimpor file .js di awal bagian skrip di file .vue , dan Anda harus melakukannya dengan baik. Pendekatan seperti itu bekerja dengan sangat baik dan menjaga kode tetap bersih, terutama dalam aplikasi besar di mana Anda mungkin menggunakan banyak metode yang lebih baik dikelompokkan berdasarkan tujuannya. Anda dapat menerapkan pendekatan ini ke semua grup metode yang tercantum dalam artikel ini, dan melihat efeknya sendiri. Namun, kami menyarankan Anda melakukan latihan itu setelah Anda mengikuti kursus yang disajikan di sini, sehingga Anda memiliki pemahaman gambaran besar tentang semua bagian yang bekerja dalam sinkronisasi lengkap, dan juga memiliki perangkat lunak yang berfungsi yang dapat Anda rujuk, setelah sesuatu istirahat saat bereksperimen.

Metode Berorientasi Tindakan/Acara

Metode-metode ini umumnya dijalankan ketika kita perlu mengambil tindakan yang sesuai dengan suatu peristiwa. Bergantung pada kasusnya, peristiwa tersebut mungkin dipicu dari interaksi pengguna, atau secara terprogram. Dalam file App.vue , metode ini berada di bawah metode utilitas.

 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(); },

Satu hal yang menarik dari beberapa cuplikan kode di atas adalah penggunaan $ref . Secara sederhana, ini adalah cara Vue mengaitkan pernyataan kode yang memuatnya, dengan konstruksi HTML yang seharusnya terpengaruh (untuk informasi lebih lanjut, baca panduan resmi). Misalnya, metode makeInputEmpty() dan detectEnterKeyPress() mempengaruhi kotak input, karena dalam HTML kotak input kita telah menyebutkan nilai atribut ref sebagai input .

Metode Akuisisi Data

Kami menggunakan dua API berikut dalam proyek kami:

  • API Geocoder Google Maps
    API ini untuk mendapatkan koordinat lokasi yang dicari pengguna. Anda akan memerlukan kunci API untuk diri Anda sendiri, yang bisa Anda dapatkan dengan mengikuti dokumentasi di tautan yang diberikan. Untuk saat ini, Anda dapat menggunakan kunci API yang digunakan oleh FusionCharts, tetapi kami meminta Anda untuk tidak menyalahgunakannya dan mendapatkan kunci Anda sendiri. Kami merujuk ke API JavaScript dari index.html proyek ini, dan kami akan menggunakan konstruktor yang disediakan olehnya untuk kode kami di file App.vue .
  • API Cuaca Langit Gelap
    API ini untuk mendapatkan data cuaca yang sesuai dengan koordinat. Namun, kami tidak akan menggunakannya secara langsung; kami akan membungkusnya dalam URL yang dialihkan melalui salah satu server FusionCharts. Alasannya adalah jika Anda mengirim permintaan GET ke API dari aplikasi yang sepenuhnya klien-end seperti milik kami, itu menghasilkan kesalahan CORS yang membuat frustrasi (informasi lebih lanjut di sini dan di sini).

Catatan Penting : Karena kami telah menggunakan Google Maps dan Dark Sky API, Kedua API ini memiliki kunci API sendiri yang telah kami bagikan kepada Anda di artikel ini. Ini akan membantu Anda fokus pada pengembangan sisi klien daripada pusing dengan implementasi backend. Namun, kami menyarankan Anda untuk membuat kunci Anda sendiri , karena kunci API kami akan memiliki batas dan jika batas ini melebihi Anda tidak akan dapat mencoba aplikasi sendiri.

Untuk Google Maps, buka artikel ini untuk mendapatkan kunci API Anda. Untuk Dark Sky API, kunjungi https://darksky.net/dev untuk membuat kunci API Anda dan titik akhir masing-masing.

Dengan mengingat konteksnya, mari kita lihat implementasi metode akuisisi data untuk proyek kita.

 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 Lem Tingkat Tinggi

Dengan tidak adanya utilitas, akuisisi, dan metode pemrosesan, sekarang kita memiliki tugas untuk mengatur semuanya. Kami melakukannya dengan membuat metode lem tingkat tinggi, yang pada dasarnya memanggil metode yang ditulis di atas dalam urutan tertentu, sehingga seluruh operasi dijalankan dengan mulus.

 // 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(); },

dipasang

Vue menyediakan kait siklus hidup instans — properti yang pada dasarnya adalah metode, dan terpicu saat siklus hidup instans mencapai tahap itu. Misalnya, dibuat, dipasang, sebelum Pembaruan, dll., semuanya merupakan kait siklus hidup yang sangat berguna yang memungkinkan pemrogram untuk mengontrol instans pada tingkat yang jauh lebih terperinci daripada yang mungkin dilakukan sebaliknya.

Dalam kode komponen Vue, kait siklus hidup ini diimplementasikan seperti yang Anda lakukan untuk prop lainnya. Sebagai contoh:

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

Berbekal pemahaman baru ini, lihat kode di bawah ini untuk prop mounted App.vue :

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

Kode Lengkap Untuk App.vue

Kami telah membahas banyak hal di bagian ini, dan beberapa bagian terakhir telah memberi Anda hal-hal sedikit demi sedikit. Namun, penting bagi Anda untuk memiliki kode lengkap yang telah dirakit untuk App.vue (tergantung modifikasi lebih lanjut di bagian selanjutnya). Ini dia:

 <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>

Dan akhirnya, setelah begitu banyak kesabaran dan kerja keras, Anda dapat melihat aliran data dengan kekuatan mentahnya! Kunjungi aplikasi di browser, segarkan halaman, cari lokasi di kotak pencarian aplikasi, dan tekan Enter !

Aplikasi seperti yang ditunjukkan di browser
(Pratinjau besar)

Sekarang setelah kita selesai dengan semua angkat berat, istirahatlah. Bagian selanjutnya berfokus pada penggunaan data untuk membuat bagan yang indah dan informatif, diikuti dengan memberikan sesi perawatan yang sangat layak bagi aplikasi kita yang tampak jelek menggunakan CSS.

5. Visualisasi Data Dengan FusionCharts

Pertimbangan Mendasar Untuk Grafik

Untuk pengguna akhir, esensi dasbor pada dasarnya adalah ini: kumpulan informasi yang disaring dan dikuratori dengan cermat tentang topik tertentu, yang disampaikan melalui instrumen visual/grafik untuk diserap dengan cepat. Mereka tidak peduli dengan seluk-beluk rekayasa saluran data Anda, atau seberapa estetis kode Anda — yang mereka inginkan hanyalah tampilan tingkat tinggi dalam 3 detik. Oleh karena itu, aplikasi kasar kami yang menampilkan data teks tidak berarti apa-apa bagi mereka, dan sudah saatnya kami menerapkan mekanisme untuk membungkus data dengan grafik.

Namun, sebelum kita menyelam jauh ke dalam implementasi grafik, mari kita pertimbangkan beberapa pertanyaan terkait dan kemungkinan jawaban dari sudut pandang kita:

  • Jenis grafik apa yang sesuai untuk jenis data yang kita hadapi?
    Jawabannya memiliki dua aspek — konteks, dan tujuannya. Yang kami maksud dengan konteks adalah jenis data, dan secara keseluruhan cocok dengan skema hal-hal yang lebih besar, dibatasi oleh ruang lingkup dan audiens proyek. Dan dengan tujuan, kami pada dasarnya berarti "apa yang ingin kami tekankan?". Misalnya, kita dapat mewakili suhu hari ini pada waktu yang berbeda dalam sehari dengan menggunakan Bagan kolom (kolom vertikal dengan lebar yang sama, dengan tinggi proporsional dengan nilai yang diwakili kolom). Namun, kami jarang tertarik pada nilai individu, melainkan variasi dan tren keseluruhan di seluruh data. Untuk menyesuaikan dengan tujuan tersebut, adalah kepentingan terbaik kami untuk menggunakan grafik Garis, dan kami akan segera melakukannya.
  • Apa yang harus diingat sebelum memilih perpustakaan charting?
    Karena kami melakukan proyek yang sebagian besar menggunakan teknologi berbasis JavaScript, tidak perlu dipikirkan lagi bahwa pustaka bagan apa pun yang kami pilih untuk proyek kami harus asli dari dunia JavaScript. Dengan pemikiran dasar itu, kita harus mempertimbangkan hal berikut sebelum memusatkan perhatian pada perpustakaan tertentu:
    • Dukungan untuk kerangka pilihan kami , yang dalam hal ini adalah Vue.js. Sebuah proyek dapat dikembangkan dalam kerangka kerja JavaScript populer lainnya seperti React, atau Angular — periksa dukungan pustaka grafik untuk kerangka kerja favorit Anda. Juga, dukungan untuk bahasa pemrograman populer lainnya seperti Python, Java, C++, .Net (AS dan VB), terutama ketika proyek melibatkan beberapa hal backend yang serius, harus dipertimbangkan.
    • Ketersediaan jenis dan fitur grafik , karena hampir tidak mungkin untuk mengetahui sebelumnya apa bentuk akhir dan tujuan data dalam proyek (terutama jika persyaratan diatur oleh klien Anda dalam pengaturan profesional). Dalam hal ini, Anda harus menyebarkan jaring Anda lebar-lebar, dan memilih perpustakaan bagan yang memiliki koleksi bagan terluas. Lebih penting lagi, untuk membedakan proyek Anda dari yang lain, perpustakaan harus memiliki fitur yang cukup dalam bentuk atribut bagan yang dapat dikonfigurasi, sehingga Anda dapat menyempurnakan dan menyesuaikan sebagian besar aspek bagan dan tingkat perincian yang tepat. Juga, konfigurasi bagan default harus masuk akal, dan dokumentasi perpustakaan harus menjadi yang terbaik, untuk alasan yang jelas bagi pengembang profesional.
    • Kurva pembelajaran, komunitas pendukung, dan keseimbangan juga harus dipertimbangkan, terutama jika Anda baru mengenal visualisasi data. Di satu sisi spektrum, Anda memiliki alat berpemilik seperti Tableau dan Qlickview yang harganya mahal, memiliki kurva pembelajaran yang mulus, tetapi juga dilengkapi dengan begitu banyak keterbatasan dalam hal kemampuan penyesuaian, integrasi, dan penerapan. Di sisi lain ada d3.js — luas, gratis (sumber terbuka), dan dapat disesuaikan dengan intinya, tetapi Anda harus membayar harga kurva belajar yang sangat curam untuk dapat melakukan sesuatu yang produktif dengan perpustakaan.

Yang Anda butuhkan adalah sweet spot — keseimbangan yang tepat antara produktivitas, cakupan, kemampuan penyesuaian, kurva pembelajaran, dan tentu saja, biaya. Kami mendorong Anda untuk melihat FusionCharts — perpustakaan pembuatan bagan JavaScript paling komprehensif dan siap untuk perusahaan di dunia untuk web dan seluler, yang akan kami gunakan dalam proyek ini untuk membuat bagan.

Pengantar FusionCharts

FusionCharts digunakan di seluruh dunia sebagai pustaka pembuatan bagan JavaScript oleh jutaan pengembang yang tersebar di ratusan negara di seluruh dunia. Secara teknis, ini dimuat dan dapat dikonfigurasi sebaik mungkin, dengan dukungan untuk mengintegrasikannya dengan hampir semua tumpukan teknologi populer yang digunakan untuk proyek berbasis web. Menggunakan FusionCharts secara komersial memerlukan lisensi, dan Anda harus membayar lisensi tergantung pada kasus penggunaan Anda (harap hubungi bagian penjualan jika Anda penasaran). Namun, kami menggunakan FusionCharts dalam proyek ini hanya untuk mencoba beberapa hal, dan oleh karena itu versi non-lisensi (dilengkapi dengan tanda air kecil di bagan Anda, dan beberapa batasan lainnya). Menggunakan versi non-lisensi baik-baik saja ketika Anda mencoba grafik dan menggunakannya dalam proyek non-komersial atau pribadi Anda. Jika Anda memiliki rencana untuk menyebarkan aplikasi secara komersial, pastikan Anda memiliki lisensi dari FusionCharts.

Karena ini adalah proyek yang melibatkan Vue.js, kita memerlukan dua modul yang perlu diinstal, jika tidak dilakukan sebelumnya:

  • Modul fusioncharts , karena berisi semua yang Anda perlukan untuk membuat bagan
  • Modul vue-fusioncharts , yang pada dasarnya adalah pembungkus untuk fusioncharts, sehingga dapat digunakan dalam proyek Vue.js

Jika Anda belum menginstalnya sebelumnya (seperti yang diinstruksikan di bagian ketiga), instal dengan menjalankan perintah berikut dari direktori root proyek:

 npm install fusioncharts vue-fusioncharts --save

Selanjutnya, pastikan file src/main.js dari proyek memiliki kode berikut (juga disebutkan di bagian 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) })

Mungkin baris paling kritis dalam cuplikan di atas adalah sebagai berikut:

 Vue.use(VueFusionCharts, FusionCharts)

Ini menginstruksikan Vue untuk menggunakan modul vue-fusioncharts untuk memahami banyak hal dalam proyek yang tampaknya tidak secara eksplisit didefinisikan oleh kami, tetapi didefinisikan dalam modul itu sendiri. Juga, jenis pernyataan ini menyiratkan deklarasi global , yang kami maksudkan bahwa di mana pun Vue menemukan sesuatu yang aneh dalam kode proyek kami (hal-hal yang belum kami definisikan secara eksplisit tentang menggunakan FusionCharts), itu setidaknya akan terlihat sekali di vue-fusioncharts. dan modul node fusioncharts untuk definisinya, sebelum memunculkan kesalahan. Jika kita akan menggunakan FusionCharts di bagian yang terisolasi dari proyek kita (tidak menggunakannya di hampir semua file komponen), maka mungkin deklarasi lokal akan lebih masuk akal.

Dengan itu, Anda siap untuk menggunakan FusionCharts dalam proyek. Kami akan menggunakan beberapa variasi grafik, pilihannya tergantung pada aspek data cuaca yang ingin kami visualisasikan. Juga, kita akan melihat interaksi pengikatan data, komponen khusus, dan pengamat beraksi.

Skema Umum Untuk Menggunakan Fusioncharts Dalam File .vue

Di bagian ini, kami akan menjelaskan ide umum menggunakan FusionCharts untuk membuat berbagai bagan di file .vue . Tapi pertama-tama, mari kita lihat pseudocode yang secara skematis menggambarkan ide inti.

 <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>

Mari kita pahami bagian yang berbeda dari pseudocode di atas:

  • Di <template> , di dalam <div> tingkat atas (itu cukup banyak wajib untuk kode HTML template setiap komponen), kami memiliki komponen kustom <fusioncharts> . Kami memiliki definisi komponen yang terkandung dalam modul Node vue-fusioncharts yang telah kami instal untuk proyek ini. Secara internal, vue-fusioncharts bergantung pada modul fusioncharts , yang juga telah diinstal. Kami mengimpor modul yang diperlukan dan menyelesaikan dependensinya, menginstruksikan Vue untuk menggunakan pembungkus secara global (di seluruh proyek) di file src/main.js , dan oleh karena itu tidak ada kekurangan definisi untuk komponen <fusioncharts> khusus yang telah kami gunakan di sini. Juga, komponen kustom memiliki atribut kustom, dan masing-masing atribut kustom terikat ke objek data (dan pada gilirannya, nilainya), oleh direktif v-bind , yang singkatannya adalah simbol titik dua ( : ). Kita akan mempelajari tentang atribut dan objek data terkait secara lebih rinci, ketika kita membahas beberapa bagan khusus yang digunakan dalam proyek ini.
  • Dalam <script> , pertama-tama Anda mendeklarasikan props yang seharusnya diterima oleh komponen, dan kemudian melanjutkan mendefinisikan objek data yang terikat pada atribut <fusioncharts> . Nilai yang ditetapkan ke objek data adalah nilai yang ditarik oleh atribut <fusioncharts> , dan bagan dibuat berdasarkan nilai yang ditarik. Selain itu, bagian kode yang paling menarik adalah objek watch { } . Ini adalah objek yang sangat khusus dalam skema Vue — ini pada dasarnya menginstruksikan Vue untuk mengawasi setiap perubahan yang terjadi pada data tertentu, dan kemudian mengambil tindakan berdasarkan bagaimana fungsi handler untuk data tersebut telah didefinisikan. Sebagai contoh, kita ingin Vue mengawasi prop yang diterima, yaitu data_prop_received_by_the_component dalam pseudocode. prop menjadi kunci dalam objek watch { } , dan nilai kuncinya adalah objek lain — metode handler yang menjelaskan apa yang perlu dilakukan setiap kali prop berubah. Dengan mekanisme yang elegan untuk menangani perubahan, aplikasi mempertahankan reaktivitasnya. deep: true mewakili flag boolean yang dapat Anda kaitkan dengan pengamat, sehingga objek yang sedang ditonton diamati lebih dalam, yaitu, bahkan perubahan yang dibuat di level objek yang disarang dilacak.
    ( Untuk informasi lebih lanjut tentang pengamat, lihat dokumentasi resmi ).

Sekarang setelah Anda dilengkapi dengan pemahaman tentang skema umum, mari selami implementasi spesifik bagan di file komponen .vue . Kodenya akan cukup jelas, dan Anda harus mencoba memahami bagaimana spesifikasinya cocok dengan skema umum hal-hal yang dijelaskan di atas.

Implementasi Grafik Dalam File .vue

Meskipun spesifikasi implementasinya bervariasi dari satu bagan ke bagan lainnya, penjelasan berikut berlaku untuk semuanya:

  • <template>
    Seperti yang dijelaskan sebelumnya, komponen kustom <fusioncharts> memiliki beberapa atribut, masing-masing terikat ke objek data terkait yang didefinisikan dalam fungsi data() dengan menggunakan direktif v-bind :. Nama atribut cukup jelas untuk apa artinya, dan mencari tahu objek data yang sesuai juga sepele.
  • <script>
    Dalam fungsi data() , objek data dan nilainya adalah yang membuat bagan berfungsi, karena pengikatan dilakukan oleh arahan v-bind ( : ) yang digunakan pada atribut <fusioncharts> . Sebelum kita menyelam jauh ke dalam objek data individual, ada baiknya menyebutkan beberapa karakteristik umum:
    • Objek data yang nilainya 0 atau 1 bersifat boolean, di mana 0 mewakili sesuatu yang tidak tersedia/dimatikan, dan 1 mewakili ketersediaan/status dihidupkan. Namun, berhati-hatilah karena objek data non-boolean juga dapat memiliki 0 atau 1 sebagai nilainya, selain nilai lain yang mungkin — itu tergantung pada konteksnya. Misalnya, containerbackgroundopacity dengan nilai defaultnya sebagai 0 adalah boolean, sedangkan lowerLimit dengan nilai defaultnya sebagai 0 berarti angka nol adalah nilai literalnya.
    • Beberapa objek data berhubungan dengan properti CSS seperti margin, padding, ukuran font, dll. — nilainya memiliki unit tersirat "px" atau piksel. Demikian pula, objek data lain dapat memiliki unit implisit yang terkait dengan nilainya. Untuk informasi terperinci, silakan merujuk ke halaman atribut bagan masing-masing di FusionCharts Dev Center.
  • Dalam fungsi data() , mungkin objek yang paling menarik dan tidak jelas adalah dataSource. Objek ini memiliki tiga objek utama yang bersarang di dalamnya:
    • bagan : Objek ini merangkum banyak atribut bagan yang terkait dengan konfigurasi dan kosmetik bagan. Ini hampir merupakan konstruksi wajib yang akan Anda temukan di semua bagan yang akan Anda buat untuk proyek ini.
    • colorrange : Objek ini agak spesifik untuk bagan yang sedang dipertimbangkan, dan terutama hadir dalam bagan yang berhubungan dengan banyak warna/bayangan untuk membatasi sub-rentang yang berbeda dari skala yang digunakan dalam bagan.
    • nilai: Objek ini, sekali lagi, hadir dalam bagan yang memiliki nilai spesifik yang perlu disorot dalam rentang skala.
  • Objek watch { } mungkin adalah hal terpenting yang membuat bagan ini, dan bagan lain yang digunakan dalam proyek ini, menjadi hidup. Reaktivitas grafik, yaitu, grafik memperbarui diri berdasarkan nilai baru yang dihasilkan dari permintaan pengguna baru dikendalikan oleh pengamat yang ditentukan dalam objek ini. Sebagai contoh, kita telah mendefinisikan watcher untuk highlights prop yang diterima oleh komponen, dan kemudian mendefinisikan fungsi handler untuk menginstruksikan Vue tentang tindakan yang diperlukan yang harus diambil, ketika ada perubahan tentang objek yang sedang ditonton di seluruh proyek. Ini berarti bahwa setiap kali App.vue menghasilkan nilai baru untuk objek apa pun dalam highlights , informasi mengalir ke bawah hingga ke komponen ini, dan nilai baru diperbarui di objek data komponen ini. Bagan yang terikat pada nilai, juga diperbarui sebagai hasil dari mekanisme ini.

Penjelasan di atas adalah goresan yang cukup luas untuk membantu kita mengembangkan pemahaman intuitif tentang gambaran yang lebih besar. Setelah Anda memahami konsep secara intuitif, Anda selalu dapat berkonsultasi dengan dokumentasi Vue.js dan FusionCharts, ketika ada sesuatu yang tidak jelas bagi Anda dari kode itu sendiri. Kami menyerahkan latihannya kepada Anda, dan mulai dari subbagian berikutnya, kami tidak akan menjelaskan hal-hal yang telah kami bahas di subbagian ini.

src/components/TempVarChart.vue

Diagram yang menunjukkan Suhu Per Jam
(Pratinjau besar)
 <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

Komponen ini berisi bagan yang sangat berguna — Pengukur Sudut.

Indeks uv
(Pratinjau besar)

Kode untuk komponen diberikan di bawah ini. Untuk informasi mendetail tentang atribut bagan Pengukur Sudut, lihat halaman Pusat Pengembangan FusionCharts untuk Pengukur Sudut.

 <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

Dalam komponen ini, kami menggunakan Horizontal Linear Gauge untuk mewakili visibilitas, seperti yang ditunjukkan pada gambar di bawah ini:

Tangkapan layar Pengukur Linier Horizontal yang mewakili Visibilitas Udara (16km)
(Pratinjau besar)

Kode untuk komponen diberikan di bawah ini. Untuk pemahaman mendalam tentang atribut yang berbeda dari jenis bagan ini, silakan merujuk ke halaman Pusat Pengembangan FusionCharts untuk Pengukur Linier Horizontal.

 <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

Komponen ini menampilkan kecepatan dan arah angin (kecepatan angin, jika Anda ahli fisika), dan sangat sulit untuk merepresentasikan vektor menggunakan grafik. Untuk kasus seperti itu, kami sarankan untuk mewakilinya dengan bantuan beberapa gambar dan nilai teks yang bagus. Karena representasi yang telah kami pikirkan sepenuhnya bergantung pada CSS, kami akan menerapkannya di bagian selanjutnya yang berhubungan dengan CSS. Namun, lihat apa yang ingin kami buat:

Status Angin: Arah Angin (kiri) dan Kecepatan Angin (kanan)
(Pratinjau besar)
 <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>

Mengakhiri Dengan Sorotan.vue

Ingatlah bahwa kita telah mengimplementasikan kode dengan CSS untuk semua komponen — kecuali Content.vue dan Highlights.vue . Karena Content.vue adalah komponen bodoh yang hanya menyampaikan data, gaya minimal yang dibutuhkannya telah dibahas. Selain itu, kami telah menulis kode yang sesuai untuk menata bilah sisi dan kartu yang berisi bagan. Oleh karena itu, yang tersisa untuk kita lakukan adalah menambahkan beberapa bit gaya ke Highlights.vue , yang terutama melibatkan penggunaan kelas 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>

Penerapan Dan Kode Sumber

Dengan grafik dan gaya yang berurutan, kita selesai! Luangkan waktu sejenak untuk menghargai keindahan ciptaan Anda.

Hasil
(Pratinjau besar)

Sekarang saatnya bagi Anda untuk menerapkan aplikasi Anda, dan membaginya dengan rekan-rekan Anda. Jika Anda tidak memiliki banyak gagasan tentang penerapan dan mengharapkan kami untuk membantu Anda, lihat di sini tentang pendapat kami tentang gagasan penerapan. Artikel tertaut juga berisi saran tentang cara menghapus tanda air FusionCharts di kiri bawah setiap bagan.

Jika Anda mengacaukan suatu tempat dan menginginkan titik referensi, kode sumber tersedia di Github.