Menggunakan Vue.js Untuk Membuat Dasbor Cuaca Interaktif Dengan API
Diterbitkan: 2022-03-10(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:
- Bagaimana merencanakan penerapan dasbor yang baik?
- Bagaimana mengembangkan aplikasi dengan Vue.js
- Cara membuat aplikasi berbasis data
- Cara memvisualisasikan data menggunakan FusionCharts
Secara khusus, setiap bagian membawa Anda selangkah lebih dekat ke tujuan pembelajaran:
- Pengantar Dasbor Cuaca
Bab ini memberi Anda gambaran umum tentang berbagai aspek usaha. - Buat Proyek
Di bagian ini, Anda belajar tentang membuat proyek dari awal menggunakan alat baris perintah Vue. - 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. - 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. - 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:
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:
- 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
- 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
- Inisialisasi scaffolding proyek dengan
vue-cli
Dengan asumsi bahwa langkah sebelumnya telah berjalan dengan baik, langkah selanjutnya adalah menggunakanvue-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
N
untuk yang terakhir. Perlu diingat bahwa meskipunwebpack-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 alatvue-cli
untuk templatewebpack-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!
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 sepupunyapackage-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 filepackage.json
. - package.json
File ini dibuat oleh alat vue-cli berdasarkan persyaratan templatewebpack-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 tentangpackage.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
, yaituApp.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 kecanduantemplate — 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).
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
danvue-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.
- Data
- 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.
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.svg
—vlocation.svg
—search.svg
—winddirection.svg
—windspeed.svg
-
src/components/
—Content.vue
—Highlights.vue
—TempVarChart.vue
—UVIndex.vue
—Visibility.vue
—WindStatus.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 fungsidata()
, yang mengembalikan objek array yang disebutchildComponents
, 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 menulisfor
-loop, bukan? Kami melakukannya dengan menggunakan direktifv-for
disediakan oleh Vue.js. Direktifv-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).
- Bertindak sebagai atribut dari tag
- Di dalam daftar yang tidak berurutan, setiap item daftar harus berupa nama komponen turunan yang dimaksud, seperti yang didefinisikan dalam objek larik
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 bernamaweather_data
dan mengembalikannya dari fungsidata()
App.vue
. Objekweather_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 komponenContent.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>
- Tetapkan objek
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>
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 iniContent.vue
, harus menerima objekweather_data
yang dikirimkan kepadanya oleh komponen indukApp.vue
. Vue.js menyediakan mekanisme untuk melakukannya — yang Anda butuhkan hanyalah objek array bernamaprops
, yang didefinisikan dalam objek default yang diekspor olehContent.vue
. Setiap elemen dari arrayprops
adalah nama dari objek data yang ingin diterima dari induknya. Untuk saat ini, satu-satunya objek data yang seharusnya diterima adalahweather_data
dari App.vue. Dengan demikian, arrayprops
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>
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 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:
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.
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 karenaApp.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 dariContent.vue
telah diatur sesuai dengan itu. - Fungsi data() sekarang tidak mengembalikan apa pun, karena kami tidak melakukan manipulasi data apa pun dalam komponen ini.
- Alat
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, sepertiContent.vue
, yang tugasnya hanya meneruskan data ke anak-anak sambil mempertahankan hierarki struktural. Ingatlah bahwa komponen bodoh sepertiHighlights.vue
danContent.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 menjadiuvIndex
, yang lebih mudah dibaca. - Dalam
export default {}
, fungsidata()
sekarang mengembalikan objek stringuvIndex
, yang nilainya diekstraksi dari objek sorotan yang diterima oleh komponen menggunakanprops
.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 menjadiwindStatus
, untuk meningkatkan keterbacaan dan juga agar sinkron dengan objek sorotan yangApp.vue
dengan data aktual. - Perubahan penamaan serupa telah dibuat untuk kecepatan dan arah — yang baru adalah
windSpeed
danwindDirection
. - Sebuah objek baru yang
derivedWindDirection
telah ikut bermain (juga disediakan olehApp.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
dantempVar
— 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 direktifv-bind:
:, atau singkatannya:
(seperti yang telah kita lakukan di sini), dengan banyak atribut dari elemen HTML khusus! - Fungsi
data()
sekarang mengembalikan objekfilename
(yang ada sebelumnya), bersama dengan dua objek baru (bukan yang lamaweather_data
):tempVar
danhighlights
. 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:
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 metodedata()
di objekexport 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 fileApp.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 kesalahanCORS
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 !
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 Nodevue-fusioncharts
yang telah kami instal untuk proyek ini. Secara internal,vue-fusioncharts
bergantung pada modulfusioncharts
, yang juga telah diinstal. Kami mengimpor modul yang diperlukan dan menyelesaikan dependensinya, menginstruksikan Vue untuk menggunakan pembungkus secara global (di seluruh proyek) di filesrc/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 direktifv-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 objekwatch { }
. 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 fungsihandler
untuk data tersebut telah didefinisikan. Sebagai contoh, kita ingin Vue mengawasiprop
yang diterima, yaitudata_prop_received_by_the_component
dalam pseudocode.prop
menjadi kunci dalam objekwatch { }
, dan nilai kuncinya adalah objek lain — metode handler yang menjelaskan apa yang perlu dilakukan setiap kaliprop
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 fungsidata()
dengan menggunakan direktifv-bind
:. Nama atribut cukup jelas untuk apa artinya, dan mencari tahu objek data yang sesuai juga sepele. -
<script>
Dalam fungsidata()
, objek data dan nilainya adalah yang membuat bagan berfungsi, karena pengikatan dilakukan oleh arahanv-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
atau1
bersifat boolean, di mana0
mewakili sesuatu yang tidak tersedia/dimatikan, dan1
mewakili ketersediaan/status dihidupkan. Namun, berhati-hatilah karena objek data non-boolean juga dapat memiliki0
atau1
sebagai nilainya, selain nilai lain yang mungkin — itu tergantung pada konteksnya. Misalnya,containerbackgroundopacity
dengan nilai defaultnya sebagai0
adalah boolean, sedangkanlowerLimit
dengan nilai defaultnya sebagai0
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.
- Objek data yang nilainya
- 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 untukhighlights
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 kaliApp.vue
menghasilkan nilai baru untuk objek apa pun dalamhighlights
, 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
<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.
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:
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:
<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.
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.