Utilisation de Vue.js pour créer un tableau de bord météo interactif avec des API

Publié: 2022-03-10
Résumé rapide ↬ Créer un tableau de bord avec des données d'API est souvent une affaire complexe. Choisir votre pile technologique, intégrer des API, sélectionner les bons graphiques et embellir avec des styles CSS peut devenir délicat. Ce didacticiel est un guide étape par étape sur la façon de vous aider à créer un tableau de bord météo dans Vue.js à l'aide des données de l'API.

(Ceci est un article sponsorisé.) Dans ce didacticiel, vous allez créer un tableau de bord météo simple à partir de zéro. Ce sera une application côté client qui n'est ni un exemple « Hello World », ni trop intimidante par sa taille et sa complexité.

L'ensemble du projet sera développé à l'aide des outils de l'écosystème Node.js + npm. En particulier, nous nous appuierons fortement sur l'API Dark Sky pour les données, Vue.js pour tout le gros du travail et FusionCharts pour la visualisation des données.

Conditions préalables

Nous attendons de vous que vous connaissiez les éléments suivants :

  • HTML5 et CSS3 (nous utiliserons également les fonctionnalités de base fournies par Bootstrap ;
  • JavaScript (en particulier la manière ES6 d'utiliser le langage) ;
  • Node.js et npm (les bases de l'environnement et de la gestion des packages sont très bien).

En dehors de ceux mentionnés ci-dessus, ce serait formidable si vous connaissiez Vue.js ou tout autre framework JavaScript similaire. Nous ne nous attendons pas à ce que vous connaissiez FusionCharts - il est si facile à utiliser que vous l'apprendrez à la volée !

Apprentissages attendus

Vos principaux apprentissages de ce projet seront :

  1. Comment planifier la mise en place d'un bon tableau de bord
  2. Comment développer des applications avec Vue.js
  3. Comment créer des applications pilotées par les données
  4. Comment visualiser les données à l'aide de FusionCharts

En particulier, chacune des sections vous rapproche un peu plus des objectifs d'apprentissage :

  1. Une introduction au tableau de bord météo
    Ce chapitre vous donne un aperçu des différents aspects de l'entreprise.
  2. Créer le projet
    Dans cette section, vous apprendrez à créer un projet à partir de zéro à l'aide de l'outil de ligne de commande Vue.
  3. Personnaliser la structure de projet par défaut
    L'échafaudage de projet par défaut que vous obtenez dans la section précédente n'est pas suffisant ; Ici, vous apprenez les éléments supplémentaires nécessaires au projet d'un point de vue structurel.
  4. Acquisition et traitement des données
    Cette section est la viande du projet; tout le code critique pour l'acquisition et le traitement des données de l'API est présenté ici. Attendez-vous à passer un maximum de temps sur cette section.
  5. Visualisation des données avec FusionCharts
    Une fois que toutes les données et autres parties mobiles du projet sont stabilisées, cette section est dédiée à la visualisation des données à l'aide de FusionCharts et d'un peu de CSS.

1. Le flux de travail du tableau de bord

Avant de plonger dans la mise en œuvre, il est important d'être clair sur notre plan. Nous divisons notre plan en quatre aspects distincts :

Conditions

Quelles sont nos exigences pour ce projet ? En d'autres termes, quelles sont les choses que nous voulons mettre en valeur à travers notre tableau de bord météo ? Gardant à l'esprit que notre public cible est probablement de simples mortels aux goûts simples, nous aimerions leur montrer ce qui suit :

  • Détails de l'emplacement pour lequel ils veulent voir la météo, ainsi que des informations primaires sur la météo. Puisqu'il n'y a pas d'exigences strictes, nous découvrirons les détails ennuyeux plus tard. Cependant, à ce stade, il est important de noter que nous devrons fournir au public un champ de recherche, afin qu'il puisse fournir des informations sur l'emplacement de son intérêt.
  • Des informations graphiques sur la météo de leur lieu d'intérêt, telles que :
    • Variation de température pour le jour de la requête
    • Faits saillants de la météo d'aujourd'hui :
      • Vitesse et direction du vent
      • Visibilité
      • L'indice UV

Remarque : Les données obtenues à partir de l'API fournissent des informations sur de nombreux autres aspects de la météo. Nous choisissons de ne pas les utiliser tous pour garder le code au minimum.

Structure

En fonction des besoins, nous pouvons structurer notre tableau de bord comme indiqué ci-dessous :

Structure du tableau de bord
( Grand aperçu )

Données

Notre tableau de bord est aussi bon que les données que nous obtenons, car il n'y aura pas de jolies visualisations sans données appropriées. Il existe de nombreuses API publiques qui fournissent des données météorologiques - certaines d'entre elles sont gratuites, d'autres non. Pour notre projet, nous collecterons des données de l'API Dark Sky. Cependant, nous ne pourrons pas interroger directement le point de terminaison de l'API à partir du côté client. Ne vous inquiétez pas, nous avons une solution de contournement qui sera révélée juste au bon moment ! Une fois que nous aurons obtenu les données de l'emplacement recherché, nous effectuerons un traitement et un formatage des données - vous savez, le type de détails techniques qui nous aide à payer les factures.

Visualisation

Une fois que nous obtenons des données propres et formatées, nous les connectons à FusionCharts. Il existe très peu de bibliothèques JavaScript dans le monde aussi performantes que FusionCharts. Parmi le grand nombre d'offres de FusionCharts, nous n'en utiliserons que quelques-unes - toutes écrites en JavaScript, mais fonctionnent de manière transparente lorsqu'elles sont intégrées au wrapper Vue pour FusionCharts.

Armés d'une vue d'ensemble, mettons les mains dans le cambouis - il est temps de concrétiser les choses ! Dans la section suivante, vous allez créer le projet Vue de base, sur lequel nous allons développer davantage.

2. Création du projet

Pour créer le projet, exécutez les étapes suivantes :

  1. Installer Node.js + npm
    ( Si Node.js est installé sur votre ordinateur, ignorez cette étape. )
    Node.js est fourni avec npm, vous n'avez donc pas besoin d'installer npm séparément. Selon le système d'exploitation, téléchargez et installez Node.js en suivant les instructions données ici.

    Une fois installé, c'est probablement une bonne idée de vérifier si le logiciel fonctionne correctement, et quelles sont leurs versions. Pour tester cela, ouvrez la ligne de commande/le terminal et exécutez les commandes suivantes :
     node --version npm --version
  2. Installer des packages avec npm
    Une fois que vous avez npm opérationnel, exécutez la commande suivante pour installer les packages de base nécessaires à notre projet.
     npm install -g vue@2 vue-cli@2
  3. Initialiser l'échafaudage du projet avec vue-cli
    En supposant que l'étape précédente s'est bien déroulée, l'étape suivante consiste à utiliser vue-cli - un outil de ligne de commande de Vue.js, pour initialiser le projet. Pour ce faire, exécutez ce qui suit :
    • Initialisez l'échafaudage avec le modèle webpack-simple.
       vue init webpack-simple vue_weather_dashboard
      On vous posera un tas de questions - accepter les valeurs par défaut pour tous sauf la dernière question sera assez bonne pour ce projet ; répondez N pour le dernier.
      Une capture d'écran de la ligne de commande/du terminal
      ( Grand aperçu )
      Gardez à l'esprit que bien que webpack-simple soit excellent pour le prototypage rapide et les applications légères comme la nôtre, il n'est pas particulièrement adapté aux applications sérieuses ou au déploiement en production. Si vous souhaitez utiliser un autre modèle (bien que nous vous le déconseillions si vous êtes un débutant), ou si vous souhaitez nommer votre projet autrement, la syntaxe est la suivante :
       vue init [template-name] [project-name]
    • Accédez au répertoire créé par vue-cli pour le projet.
       cd vue_weather_dashboard
    • Installez tous les packages mentionnés dans le package.json , qui a été créé par l'outil vue-cli pour le webpack-simple .
       npm install
    • Démarrez le serveur de développement et voyez votre projet Vue par défaut fonctionner dans le navigateur !
       npm run dev

Si vous êtes nouveau sur Vue.js, prenez un moment pour savourer votre dernière réalisation : vous avez créé une petite application Vue et elle s'exécute sur localhost : 8080 !

Une capture d'écran du site Web Vue.js
( Grand aperçu )

Brève explication de la structure de projet par défaut

Il est temps de jeter un œil à la structure à l'intérieur du répertoire vue_weather_dashboard , afin que vous compreniez les bases avant de commencer à le modifier.

La structure ressemble à ceci :

 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

Bien qu'il puisse être tentant de ne pas se familiariser avec les fichiers et répertoires par défaut, si vous êtes nouveau sur Vue, nous vous recommandons fortement de jeter au moins un coup d'œil au contenu des fichiers. Cela peut être une bonne session éducative et déclencher des questions que vous devriez poursuivre par vous-même, en particulier les fichiers suivants :

  • package.json , et juste un coup d'œil à son cousin package-lock.json
  • webpack.config.js
  • index.html
  • src/main.js
  • src/App.vue

Une brève explication de chacun des fichiers et répertoires présentés dans l'arborescence est donnée ci-dessous :

  • LISEZMOI.md
    Pas de prix pour deviner - il s'agit principalement pour les humains de lire et de comprendre les étapes nécessaires à la création de l'échafaudage du projet.
  • node_modules/
    Il s'agit du répertoire dans lequel npm télécharge les packages nécessaires au démarrage du projet. Les informations sur les packages nécessaires sont disponibles dans le fichier package.json .
  • package.json
    Ce fichier est créé par l'outil vue-cli en fonction des exigences du modèle webpack-simple et contient des informations sur les packages npm (y compris leurs versions et autres détails) qui doivent être installés. Examinez attentivement le contenu de ce fichier - c'est là que vous devriez visiter et peut-être modifier pour ajouter/supprimer les packages nécessaires au projet, puis exécutez npm install. En savoir plus sur package.json ici.
  • package-lock.json
    Ce fichier est créé par npm lui-même et est principalement destiné à conserver un journal des éléments téléchargés et installés par npm.
  • webpack.config.js
    Il s'agit d'un fichier JavaScript contenant la configuration de Webpack, un outil qui regroupe différents aspects de notre projet (code, actifs statiques, configuration, environnements, mode d'utilisation, etc.) et le minimise avant de le servir à l'utilisateur. L'avantage est que toutes les choses sont liées automatiquement et que l'expérience utilisateur s'améliore considérablement en raison de l'amélioration des performances de l'application (les pages sont servies rapidement et se chargent plus rapidement sur le navigateur). Comme vous pourriez le rencontrer plus tard, c'est le fichier qui doit être inspecté lorsque quelque chose dans le système de construction ne fonctionne pas comme prévu. De plus, lorsque vous souhaitez déployer l'application, c'est l'un des fichiers clés qui doit être modifié (en savoir plus ici).
  • index.html
    Ce fichier HTML sert de matrice (ou vous pouvez dire, de modèle) où les données et le code doivent être intégrés dynamiquement (c'est ce que fait principalement Vue), puis servis à l'utilisateur.
  • src/main.js
    Ce fichier JavaScript contient du code qui gère principalement les dépendances de niveau supérieur/projet et définit le composant Vue de niveau supérieur. En bref, il orchestre le JavaScript pour l'ensemble du projet et sert de point d'entrée de l'application. Modifiez ce fichier lorsque vous devez déclarer des dépendances à l'échelle du projet sur certains modules de nœud, ou si vous souhaitez que quelque chose soit modifié concernant le composant Vue le plus élevé du projet.
  • src/App.vue
    Dans le point précédent, lorsque nous parlions du "composant Vue le plus élevé", nous parlions essentiellement de ce fichier. Chaque fichier .vue du projet est un composant et les composants sont liés hiérarchiquement. Au départ, nous n'avons qu'un seul fichier .vue , c'est-à-dire App.vue , comme seul composant. Mais bientôt, nous ajouterons plus de composants à notre projet (en suivant principalement la structure du tableau de bord) et les relierons conformément à notre hiérarchie souhaitée, App.vue étant l'ancêtre de tous. Ces fichiers .vue contiendront du code dans un format que Vue veut que nous écrivions. Ne vous inquiétez pas, ce sont des codes JavaScript écrits qui maintiennent une structure qui peut nous garder sain d'esprit et organisé. Vous avez été averti - à la fin de ce projet, si vous êtes nouveau sur Vue, vous pourriez devenir accro au template — script — style template — script — style template — script — style stylée d'organiser le code !

Maintenant que nous avons créé la fondation, il est temps de :

  • Modifiez les modèles et modifiez un peu les fichiers de configuration, afin que le projet se comporte exactement comme nous le souhaitons.
  • Créez de nouveaux fichiers .vue et implémentez la structure du tableau de bord avec le code Vue.

Nous les apprendrons dans la section suivante, qui va être un peu longue et demander une certaine attention. Si vous avez besoin de caféine ou d'eau, ou si vous voulez vous décharger, c'est le moment !

3. Personnalisation de la structure de projet par défaut

Il est temps de bricoler les fondations que le projet d'échafaudage nous a données. Avant de commencer, assurez-vous que le serveur de développement fourni par webpack est en cours d'exécution. L'avantage d'exécuter ce serveur en continu est que toute modification que vous apportez au code source - une fois que vous l'enregistrez et actualisez la page Web - est immédiatement répercutée sur le navigateur.

Si vous souhaitez démarrer le serveur de développement, exécutez simplement la commande suivante depuis le terminal (en supposant que votre répertoire actuel est le répertoire du projet) :

 npm run dev

Dans les sections suivantes, nous allons modifier certains des fichiers existants et ajouter de nouveaux fichiers. Il sera suivi de brèves explications sur le contenu de ces fichiers, afin que vous ayez une idée de ce que ces changements sont censés faire.

Modifier les fichiers existants

index.html

Notre application est littéralement une application à une seule page, car il n'y a qu'une seule page Web qui s'affiche sur le navigateur. Nous en reparlerons plus tard, mais faisons d'abord notre premier changement — en modifiant le texte dans la <title> .

Avec cette petite révision, le fichier HTML ressemble à ceci :

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

Prenez un moment pour actualiser la page Web sur localhost:8080 et voyez le changement reflété dans la barre de titre de l'onglet du navigateur - il devrait indiquer "Vue Weather Dashboard". Cependant, c'était juste pour vous montrer le processus d'apporter des modifications et de vérifier si cela fonctionne. Nous avons plus de choses à faire !

Cette page HTML simple manque de beaucoup de choses que nous voulons dans notre projet, en particulier les suivantes :

  • Quelques méta-informations
  • Liens CDN vers Bootstrap (cadre CSS)
  • lien vers la feuille de style personnalisée (encore à ajouter dans le projet)
  • Pointeurs vers l'API Google Maps Geolocation à partir de la <script>

Après avoir ajouté ces éléments, le index.html final a le contenu suivant :

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

Enregistrez le fichier et actualisez la page Web. Vous avez peut-être remarqué une légère bosse pendant le chargement de la page - cela est principalement dû au fait que le style de la page est maintenant contrôlé par Bootstrap et que les éléments de style tels que les polices, l'espacement, etc. sont différents de la valeur par défaut que nous avions plus tôt (si vous n'êtes pas sûr, revenez à la valeur par défaut et voyez la différence).

Une capture d'écran lorsque vous actualisez la page Web avec localhost : 8080
( Grand aperçu )

Remarque : Une chose importante avant de continuer - l'URL de l'API Google Maps contient une clé qui est une propriété de FusionCharts. Pour l'instant, vous pouvez utiliser cette clé pour construire le projet, car nous ne voulons pas que vous vous enlisiez avec ce type de détails infimes (qui peuvent être des distractions lorsque vous êtes nouveau). Cependant, nous vous conseillons fortement de générer et d'utiliser votre propre clé API Google Maps une fois que vous avez fait des progrès et que vous vous sentez à l'aise pour prêter attention à ces petits détails.

package.json

Au moment d'écrire ces lignes, nous avons utilisé certaines versions des packages npm pour notre projet, et nous savons avec certitude que ces éléments fonctionnent ensemble. Cependant, au moment où vous exécutez le projet, il est fort possible que les dernières versions stables des packages que npm télécharge pour vous ne soient pas les mêmes que celles que nous avons utilisées, et cela pourrait casser le code (ou faire des choses qui vont au-delà notre contrôle). Ainsi, il est très important d'avoir exactement le même fichier package.json qui a été utilisé pour construire ce projet, afin que notre code/explications et les résultats que vous obtenez soient cohérents.

Le contenu du fichier package.json doit être :

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

Nous vous encourageons à parcourir le nouveau package.json et à déterminer quelles sont les fonctions des différents objets dans le json. Vous préférerez peut-être remplacer la valeur de la clé « author » par votre nom. De plus, les packages mentionnés dans les dépendances se révéleront au bon moment dans le code. Pour l'instant, il suffit de savoir que :

  • Les packages liés à babel permettent de gérer correctement le code de style ES6 par le navigateur ;
  • axios traite les requêtes HTTP basées sur Promise ;
  • moment et moment-timezone sont pour la manipulation de date/heure ;
  • fusioncharts et vue-fusioncharts sont responsables du rendu des graphiques :
  • vue , pour des raisons évidentes.

webpack.config.js

Comme pour package.json , nous vous suggérons de conserver un fichier webpack.config.js cohérent avec celui que nous avons utilisé pour construire le projet. Cependant, avant d'apporter des modifications, nous vous recommandons de comparer soigneusement le code par défaut dans le webpack.config.js et le code que nous avons fourni ci-dessous. Vous remarquerez quelques différences - recherchez-les sur Google et ayez une idée de base de ce qu'elles signifient. Étant donné que l'explication approfondie des configurations de webpack n'entre pas dans le cadre de cet article, vous êtes seul à cet égard.

Le fichier webpack.config.js personnalisé est le suivant :

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

Avec les modifications apportées au projet webpack.config.js , il est impératif d'arrêter le serveur de développement en cours d'exécution ( Ctrl + C ) et de le redémarrer avec la commande suivante exécutée à partir du répertoire du projet après avoir installé tous les packages mentionnés dans le package.json fichier package.json :

 npm install npm run dev

Avec cela, l'épreuve de peaufiner les configurations et de s'assurer que les bons packages sont en place se termine. Cependant, cela marque aussi le parcours de modification et d'écriture de code, qui est un peu long mais aussi très enrichissant !

src/main.js

Ce fichier est la clé de l'orchestration de haut niveau du projet — c'est ici que nous définissons :

  • Quelles sont les dépendances de niveau supérieur (où obtenir les packages npm les plus importants nécessaires) ;
  • Comment résoudre les dépendances, ainsi que des instructions à Vue sur l'utilisation des plugins/wrappers, le cas échéant ;
  • Une instance de Vue qui gère le composant le plus haut du projet : src/App.vue (le fichier nodal .vue ).

Conformément à nos objectifs pour le fichier src/main.js , le code doit être :

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

Il s'agit de l'un des fichiers les plus importants de l'ensemble du projet et représente le composant le plus élevé de la hiérarchie - l'ensemble de l'application elle-même, dans son ensemble. Pour notre projet, ce composant fera tout le gros du travail, que nous explorerons plus tard. Pour l'instant, nous voulons nous débarrasser du passe-partout par défaut et mettre quelque chose de notre côté.

Si vous êtes nouveau dans la manière d'organiser le code de Vue, il serait préférable d'avoir une idée de la structure générale dans les fichiers .vue . Les fichiers .vue comprennent trois sections :

  • Modèle
    C'est ici que le modèle HTML de la page est défini. Outre le HTML statique, cette section contient également la façon dont Vue incorpore du contenu dynamique, en utilisant les doubles accolades {{ }} .
  • Scénario
    JavaScript régit cette section et est responsable de la génération de contenu dynamique qui va et se trouve dans le modèle HTML aux endroits appropriés. Cette section est principalement un objet qui est exporté et se compose de :
    • Données
      Il s'agit d'une fonction elle-même, et généralement elle renvoie des données souhaitées encapsulées dans une belle structure de données.
    • Méthodes
      Un objet qui consiste en une ou plusieurs fonctions/méthodes, dont chacune manipule généralement les données d'une manière ou d'une autre, et contrôle également le contenu dynamique du modèle HTML.
    • Calculé
      Tout comme l'objet de méthode discuté ci-dessus avec une distinction importante - alors que toutes les fonctions de l'objet de méthode sont exécutées chaque fois que l'une d'entre elles est appelée, les fonctions de l'objet calculé se comportent beaucoup plus raisonnablement et s'exécutent si et seulement si elles ont été appelé.
  • Style
    Cette section concerne le style CSS qui s'applique au code HTML de la page (écrit dans le modèle) - mettez le bon vieux CSS ici pour rendre vos pages magnifiques !

En gardant à l'esprit le paradigme ci-dessus, personnalisons au minimum le code dans 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>

N'oubliez pas que l'extrait de code ci-dessus sert simplement à tester App.vue fonctionne avec notre propre code. Il subira ensuite de nombreuses modifications, mais enregistrez d'abord le fichier et actualisez la page sur le navigateur.

Une capture d'écran du navigateur avec le message "Le code de ce composant est dans App.vue"
( Grand aperçu )

À ce stade, c'est probablement une bonne idée d'obtenir de l'aide pour l'outillage. Découvrez les outils de développement Vue pour Chrome, et si vous n'avez pas beaucoup de problèmes à utiliser Google Chrome comme navigateur par défaut pour le développement, installez l'outil et jouez un peu avec. Il sera extrêmement utile pour le développement et le débogage ultérieurs, lorsque les choses deviendront plus compliquées.

Répertoires et fichiers supplémentaires

La prochaine étape serait d'ajouter des fichiers supplémentaires, afin que la structure de notre projet devienne complète. Nous ajouterions les répertoires et fichiers suivants :

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

Remarque : Enregistrez les fichiers .svg hyperliens dans votre projet.

Créez les répertoires et les fichiers mentionnés ci-dessus. La structure finale du projet devrait ressembler (n'oubliez pas de supprimer les dossiers et fichiers de la structure par défaut qui sont désormais inutiles) :

 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

Il peut y avoir d'autres fichiers, comme .babelrc , .gitignore , .editorconfig , etc. dans le dossier racine du projet. Vous pouvez les ignorer en toute sécurité pour le moment.

Dans la section suivante, nous allons ajouter un minimum de contenu aux fichiers nouvellement ajoutés et tester s'ils fonctionnent correctement.

src/css/style.css

Bien qu'il ne soit pas d'une grande utilité dans l'immédiat, copiez le code suivant dans le fichier :

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

Dans ce répertoire, téléchargez et enregistrez les fichiers .svg mentionnés ci-dessous :

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

src/components/Content.vue

C'est ce que nous appelons un "composant stupide" (c'est-à-dire un espace réservé) qui est là uniquement pour maintenir la hiérarchie et qui transmet essentiellement des données à ses composants enfants.

N'oubliez pas qu'il n'y a pas de barre technique pour écrire tout notre code dans le fichier App.vue , mais nous adoptons l'approche consistant à diviser le code en imbriquant les composants pour deux raisons :

  • Écrire du code propre, ce qui facilite la lisibilité et la maintenabilité ;
  • Pour reproduire la même structure que nous verrons à l'écran, c'est-à-dire la hiérarchie.

Avant d'imbriquer le composant défini dans Content.vue dans le composant racine App.vue , écrivons un code jouet (mais éducatif) pour 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>

Dans le code, observez attentivement et comprenez ce qui suit :

  • Dans la <script> (où nous écrivons évidemment du code JavaScript), nous définissons un objet qui est exporté (mis à disposition d'autres fichiers) par défaut. Cet objet contient une fonction data() , qui renvoie un objet tableau appelé childComponents , ses éléments étant les noms des fichiers de composants qui doivent être imbriqués davantage.
  • Dans la <template> (où nous écrivons un modèle HTML), la chose intéressante est le <ul> .
    • Dans la liste non ordonnée, chaque élément de la liste doit être le nom des composants enfants prévus, tels que définis dans l'objet tableau childComponents . De plus, la liste devrait s'étendre automatiquement jusqu'au dernier élément du tableau. On dirait que nous devrions écrire une boucle for , n'est-ce pas ? Pour ce faire, nous utilisons la directive v-for fournie par Vue.js. La directive v-for :
      • Agit comme un attribut de la <li> , parcourt le tableau, rend les noms des composants enfants où l'itérateur est mentionné entre les crochets {{ }} (où nous écrivons le texte pour les éléments de la liste).

Le code et l'explication ci-dessus constituent la base de votre compréhension ultérieure de la manière dont le script et le modèle sont interdépendants, et de la manière dont nous pouvons utiliser les directives fournies par Vue.js.

Nous avons beaucoup appris, mais même après tout cela, il nous reste une chose à apprendre sur la connexion transparente des composants dans la hiérarchie : transmettre les données du composant parent à ses enfants. Pour l'instant, nous devons apprendre à transmettre certaines données de src/App.vue à src/components/Content.vue , afin de pouvoir utiliser les mêmes techniques pour le reste du composant imbriqué dans ce projet.

Les données transmises des composants parents aux composants enfants peuvent sembler simples, mais le diable est dans les détails ! Comme expliqué brièvement ci-dessous, plusieurs étapes sont nécessaires pour que cela fonctionne :

  • Définir et les données
    Pour l'instant, nous voulons jouer avec des données statiques - un objet contenant des valeurs codées en dur sur différents aspects de la météo ira très bien ! Nous créons un objet appelé weather_data et le retournons à partir de la fonction data() de App.vue . L'objet weather_data est donné dans l'extrait ci-dessous :
 weather_data: { location: "California", temperature: { current: "35 C", }, highlights: { uvindex: "3", windstatus: { speed: "20 km/h", direction: "NE", }, visibility: "12 km", }, },
  • Transmission des données du parent
    Pour faire passer les données, nous avons besoin d'une destination où nous voulons envoyer les données ! Dans ce cas, la destination est le composant Content.vue , et la façon de l'implémenter est de :
    • Attribuez l'objet weather_data à un attribut personnalisé de la <Content>
    • Liez l'attribut aux données à l'aide de la directive v-bind : fournie par Vue.js, qui rend la valeur de l'attribut dynamique (réactive aux modifications apportées aux données d'origine).
       <Content v-bind:weather_data=“weather_data”></Content>

La définition et la transmission des données sont gérées du côté source de la poignée de main, qui dans notre cas est le fichier App.vue .

Le code du fichier App.vue , dans son état actuel, est donné ci-dessous :

 <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> 
Une capture d'écran du navigateur avec le message "Le code de ce composant est dans App.vue. Ces composants enfants de Content.vue sont : TempVarChart.vue, Highlights.vue »
( Grand aperçu )

Une fois les données définies et transmises depuis la source (composant parent), il incombe maintenant à l'enfant de recevoir les données et de les restituer de manière appropriée, comme expliqué dans les deux étapes suivantes.

  • Réception des données par l'enfant
    Le composant enfant, dans ce cas Content.vue , doit recevoir l'objet weather_data qui lui est envoyé par le composant parent App.vue . Vue.js fournit un mécanisme pour le faire — tout ce dont vous avez besoin est un objet tableau appelé props , défini dans l'objet par défaut exporté par Content.vue . Chaque élément des props du tableau est un nom des objets de données qu'il souhaite recevoir de son parent. Pour l'instant, le seul objet de données qu'il est censé recevoir est weather_data d'App.vue. Ainsi, le tableau d' props ressemble à :
 <template> // HTML template code here </template> <script> export default { props: ["weather_data"], data () { return { // data here } }, } </script> <style> // component specific CSS here </style>
  • Affichage des données dans la page
    Maintenant que nous nous sommes assurés de recevoir les données, la dernière tâche que nous devons accomplir est de rendre les données. Pour cet exemple, nous allons directement vider les données reçues sur la page Web, juste pour illustrer la technique. Cependant, dans les applications réelles (comme celle que nous sommes sur le point de créer), les données subissent normalement de nombreux traitements, et seules les parties pertinentes sont affichées de manière adaptée à l'objectif. Par exemple, dans ce projet, nous allons éventuellement obtenir des données brutes de l'API météo, les nettoyer et les formater, alimenter les données dans les structures de données nécessaires aux graphiques, puis les visualiser. Quoi qu'il en soit, pour afficher le vidage des données brutes, nous utiliserons simplement les crochets {{ }} que Vue comprend, comme indiqué dans l'extrait ci-dessous :
 <template> <div> // other template code here {{ weather_data }} </div> </template>

Il est maintenant temps d'assimiler tous les morceaux. Le code de Content.vue — dans son état actuel — est donné ci-dessous :

 <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> 
Une capture d'écran du navigateur avec le résultat du code fourni
( Grand aperçu )

Après avoir apporté les modifications décrites ci-dessus, actualisez la page Web sur le navigateur et voyez à quoi elle ressemble. Prenez un moment pour apprécier la complexité que Vue gère - si vous modifiez l'objet weather_data dans App.vue , il est transmis silencieusement à Content.vue , et éventuellement au navigateur affichant la page Web ! Essayez en modifiant la valeur de l'emplacement de la clé.

Bien que nous ayons appris les accessoires et la liaison de données à l'aide de données statiques, nous utiliserons des données dynamiques collectées à l'aide d'API Web dans l'application et modifierons le code en conséquence .

Sommaire

Avant de passer au reste des fichiers .vue , résumons ce que nous avons appris en écrivant le code pour App.vue et components/Content.vue :

  • Le fichier App.vue est ce que nous appelons le composant racine - celui qui se trouve au sommet de la hiérarchie des composants. Le reste des fichiers .vue représente des composants qui sont son enfant direct, son petit-enfant, etc.
  • Le fichier Content.vue est un composant factice - sa responsabilité est de transmettre les données aux niveaux inférieurs et de maintenir la hiérarchie structurelle, afin que notre code reste cohérent avec la philosophie "*ce que nous voyons est ce que nous implémentons*".
  • La relation parent-enfant d'un composant ne se produit pas de nulle part - vous devez enregistrer un composant (soit globalement, soit localement, selon l'utilisation prévue du composant), puis l' imbriquer à l'aide de balises HTML personnalisées (dont l'orthographe est exacte identique à celui des noms sous lesquels les composants ont été enregistrés).
  • Une fois enregistrées et imbriquées, les données sont transmises des composants parents aux composants enfants, et le flux n'est jamais inversé (de mauvaises choses se produiront si l'architecture du projet autorise le retour). Le composant parent est la source relative des données et il transmet les données pertinentes à ses enfants à l'aide de la directive v-bind pour les attributs des éléments HTML personnalisés. L'enfant reçoit les données qui lui sont destinées à l'aide d'accessoires, puis décide lui-même quoi faire avec les données.

Pour le reste des composants, nous ne nous livrerons pas à des explications détaillées - nous écrirons simplement le code en fonction des enseignements tirés du résumé ci-dessus. Le code sera évident, et si vous ne comprenez pas la hiérarchie, reportez-vous au schéma ci-dessous :

Un schéma expliquant la hiérarchie du code
( Grand aperçu )

Le diagramme indique que TempVarChart.vue et Highlights.vue sont l'enfant direct de Content.vue . Ainsi, il peut être judicieux de préparer Content.vue pour envoyer des données à ces composants, ce que nous faisons en utilisant le code ci-dessous :

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

Une fois que vous aurez enregistré ce code, vous obtiendrez des erreurs - ne vous inquiétez pas, c'est normal. Il sera corrigé une fois que vous aurez le reste des fichiers de composants prêts. Si cela vous dérange de ne pas pouvoir voir la sortie, commentez les lignes contenant les balises d'élément personnalisées <temp-var-chart> et <today-highlights> .

Pour cette section, il s'agit du code final de Content.vue . Pour le reste de cette section, nous ferons référence à ce code , et non aux précédents que nous avons écrits pour l'apprentissage.

src/components/TempVarChart.vue

Avec son composant parent Content.vue transmettant les données, TempVarChart.vue doit être configuré pour recevoir et restituer les données, comme indiqué dans le code ci-dessous :

 <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

Ce composant recevra également des données d' App.vue - son composant parent. Après cela, il doit être lié à ses composants enfants et les données pertinentes doivent leur être transmises.

Voyons d'abord le code de réception des données du parent :

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

À ce stade, la page Web ressemble à l'image ci-dessous :

Résultat du code affiché dans le navigateur
( Grand aperçu )

Nous devons maintenant modifier le code de Highlights.vue pour enregistrer et imbriquer ses composants enfants, puis transmettre les données aux enfants. Le code pour cela est le suivant :

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

Une fois que vous avez enregistré le code et affiché la page Web, vous êtes censé voir des erreurs dans l'outil Developer Console fourni par le navigateur ; ils apparaissent car bien que Highlights.vue envoie des données, personne ne les reçoit. Nous n'avons pas encore écrit le code pour les enfants de Highlights.vue .

Observez que nous n'avons pas effectué une grande partie du traitement des données, c'est-à-dire que nous n'avons pas extrait les facteurs individuels des données météorologiques qui figurent dans la section Faits saillants du tableau de bord. Nous aurions pu le faire dans la fonction data() , mais nous avons préféré garder Highlights.vue un composant stupide qui transmet simplement l'intégralité du vidage de données qu'il reçoit à chacun des enfants, qui possèdent alors leurs propres extraits ce qui leur est nécessaire . Cependant, nous vous encourageons à essayer d'extraire des données dans Highlights.vue et à envoyer les données pertinentes à chaque composant enfant - c'est néanmoins un bon exercice pratique !

src/components/UVIndex.vue

Le code de ce composant reçoit le vidage des données des hautes lumières de Highlights.vue , extrait les données de l'indice UV et les affiche sur la page.

 <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

Le code de ce composant reçoit le vidage des données des faits saillants de Highlights.vue , extrait les données pour la visibilité et les restitue sur la page.

 <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

Le code de ce composant reçoit le vidage des données des points forts de Highlights.vue , extrait les données pour l'état du vent (vitesse et direction) et les affiche sur la page.

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

Après avoir ajouté le code pour tous les composants, jetez un œil à la page Web sur le navigateur.

Résultat du code affiché dans le navigateur
( Grand aperçu )

Pas pour se décourager, mais tout ce labeur consistait simplement à relier les composants dans la hiérarchie et à tester si le flux de données se produit entre eux ou non ! Dans la section suivante, nous allons jeter la plupart du code que nous avons écrit jusqu'à présent et en ajouter beaucoup plus concernant le projet lui-même. Cependant, nous retiendrons certainement la structure et l'emboîtement des composants ; les enseignements de cette section nous permettront de construire un tableau de bord décent avec Vue.js.

4. Acquisition et traitement des données

Vous souvenez-vous de l'objet weather_data dans App.vue ? Il contenait des données codées en dur que nous avons utilisées pour tester si tous les composants fonctionnent correctement, et aussi pour vous aider à apprendre certains aspects de base de l'application Vue sans vous perdre dans les détails des données du monde réel. Cependant, il est maintenant temps que nous nous débarrassions de notre coquille et que nous entrions dans le monde réel, où les données de l'API domineront la majeure partie de notre code.

Préparation des composants enfants pour recevoir et traiter des données réelles

Dans cette section, vous obtiendrez un vidage de code pour tous les composants sauf App.vue . Le code gérera la réception de données réelles d' App.vue (contrairement au code que nous avons écrit dans la section précédente pour recevoir et restituer des données factices).

Nous vous encourageons fortement à lire attentivement le code de chaque composant, afin de vous faire une idée des données que chacun de ces composants attend et utilisera éventuellement dans la visualisation.

Une partie du code et la structure globale seront similaires à ceux que vous avez vus dans la structure précédente - vous ne serez donc pas confronté à quelque chose de radicalement différent. Cependant, le diable est dans les détails ! Examinez donc attentivement le code et, lorsque vous les avez raisonnablement bien compris, copiez le code dans les fichiers de composants respectifs de votre projet.

Note : Tous les composants de cette section sont dans le répertoire src/components/ . Ainsi, à chaque fois, le chemin ne sera pas mentionné — seul le nom du fichier .vue sera mentionné pour identifier le composant.

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

Les modifications suivantes ont été apportées au code précédent :

  • Dans le <template> , le texte et les données dans {{ }} ont été supprimés, car nous ne faisons que recevoir des données et les transmettre aux enfants, sans rendu spécifique à ce composant.
  • Dans l' export default {} :
    • Les props ont été modifiés pour correspondre aux objets de données qui seront envoyés par le parent : App.vue . La raison de la modification des accessoires est que App.vue lui-même affichera certaines des données qu'il acquiert à partir de l'API météo et d'autres ressources en ligne, en fonction de la requête de recherche de l'utilisateur, et transmettra le reste des données. Dans le code factice que nous avons écrit précédemment, App.vue transmettait l'intégralité du vidage de données factice, sans aucune discrimination, et les accessoires de Content.vue étaient configurés en conséquence.
    • La fonction data() ne renvoie plus rien, car nous ne faisons aucune manipulation de données dans ce composant.

TempVarChart.vueTempVarChart.vue

Ce composant est censé recevoir des projections de température détaillées pour le reste de la journée en cours et éventuellement les afficher à l'aide de FusionCharts. Mais pour le moment, nous les afficherons uniquement sous forme de texte sur la page Web.

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

Faits saillants.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>

Les modifications apportées par rapport au code précédent sont :

  • Dans le <template> , le texte et les données dans {{ }} ont été supprimés, car il s'agit d'un composant stupide, tout comme Content.vue , dont le seul travail est de transmettre les données aux enfants tout en conservant la hiérarchie structurelle. N'oubliez pas que des composants stupides comme Highlights.vue et Content.vue existent pour maintenir la parité entre la structure visuelle du tableau de bord et le code que nous écrivons.

UVIndex.vue

Les modifications apportées au code précédent sont les suivantes :

  • Dans le <template> et le <style> , l' div id a été remplacé par uvIndex , ce qui est plus lisible.
  • Dans l' export default {} , la fonction data() renvoie désormais un objet chaîne uvIndex , dont la valeur est extraite de l'objet Highlights reçu par le composant à l'aide de props . Cet uvIndex est maintenant temporairement utilisé pour afficher la valeur sous forme de texte dans le <template> . Plus tard, nous connecterons cette valeur à la structure de données appropriée pour le rendu d'un graphique.

Visibilité.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>

La seule modification apportée à ce fichier (par rapport à son code précédent) est que la définition de l'objet de visibility renvoyé par la fonction data() contient désormais toString() à sa fin, puisque la valeur reçue du parent sera un flottant numéro de point, qui doit être converti en chaîne.

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>

Les modifications apportées au code précédent sont les suivantes :

  • Tout au long du fichier, windstatus a été renommé en windStatus , pour favoriser la lisibilité et également pour être synchronisé avec l'objet de surbrillance fourni par App.vue avec les données réelles.
  • Des changements de nom similaires ont été apportés à la vitesse et à la direction — les nouveaux sont windSpeed ​​et windDirection .
  • Un nouvel objet derivedWindDirection est entré en jeu (également fourni par App.vue dans le bundle Highlights).

Pour l'instant, les données reçues sont rendues sous forme de texte ; plus tard, il sera branché sur la structure de données nécessaire à la visualisation.

Tester avec des données factices

Le recours répété à des données factices peut être un peu frustrant pour vous, mais il y a de bonnes raisons derrière cela :

  • Nous avons apporté de nombreuses modifications au code de chaque composant, et c'est une bonne idée de tester si ces modifications cassent le code. En d'autres termes, nous devons vérifier si le flux de données est intact, maintenant que nous sommes sur le point de passer à des parties plus complexes du projet.
  • Les données réelles de l'API météo en ligne auront besoin de beaucoup de massage, et il peut être écrasant pour vous de jongler entre le code d'acquisition et de traitement des données et le code pour un flux de données fluide dans les composants. L'idée est de garder le quantum de complexité sous contrôle, afin que nous ayons une meilleure compréhension des erreurs auxquelles nous pourrions être confrontés.

Dans cette section, ce que nous faisons consiste essentiellement à coder en dur certaines données json dans App.vue , qui seront évidemment remplacées par des données en direct dans un avenir proche. Il existe de nombreuses similitudes entre la structure json factice et la structure json que nous utiliserons pour les données réelles. Ainsi, cela vous donne également une idée approximative de ce à quoi s'attendre des données réelles, une fois que nous les rencontrons.

Cependant, nous admettons que c'est loin d'être l'approche idéale que l'on pourrait adopter lors de la construction d'un tel projet à partir de zéro. Dans le monde réel, vous commencerez souvent par la source de données réelle, jouerez un peu avec elle pour comprendre ce qui peut et devrait être fait pour l'apprivoiser, puis réfléchissez à la structure de données json appropriée pour capturer les informations pertinentes. Nous vous avons intentionnellement protégé de tout ce sale boulot, car cela vous éloigne de l'objectif - apprendre à utiliser Vue.js et FusionCharts pour créer un tableau de bord.

Passons maintenant au nouveau code pour 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>

Les modifications apportées au code par rapport à sa version précédente sont les suivantes :

  • Le nom du composant enfant a été changé en contenu du tableau de bord et, en conséquence, l'élément HTML personnalisé dans le <template> a été révisé. Notez que nous avons maintenant deux attributs - highlights et tempVar - au lieu d'un seul attribut que nous utilisions précédemment avec l'élément personnalisé. En conséquence, les données associées à ces attributs ont également changé. Ce qui est intéressant ici, c'est que nous pouvons utiliser la directive v-bind: :, ou son raccourci : (comme nous l'avons fait ici), avec plusieurs attributs d'un élément HTML personnalisé !
  • La fonction data() renvoie maintenant l'objet filename (qui existait auparavant), ainsi que deux nouveaux objets (au lieu de l'ancien weather_data ) : tempVar et highlights . La structure du json est appropriée pour le code que nous avons écrit dans les composants enfants, afin qu'ils puissent extraire les données dont ils ont besoin des vidages. Les structures sont assez explicites et vous pouvez vous attendre à ce qu'elles soient assez similaires lorsque nous traitons des données en direct. Cependant, le changement significatif que vous rencontrerez est l'absence de codage en dur (évident, n'est-ce pas) - nous laisserons les valeurs vides comme état par défaut et écrirons du code pour les mettre à jour dynamiquement en fonction des valeurs que nous recevrons du API météo.

Vous avez écrit beaucoup de code dans cette section, sans voir la sortie réelle. Avant de continuer, jetez un coup d'œil au navigateur (redémarrez le serveur avec npm run dev , si nécessaire) et profitez de la gloire de votre réussite. La page Web que vous devriez voir à ce stade ressemble à l'image ci-dessous :

Résultat du code affiché dans le navigateur
( Grand aperçu )

Code pour l'acquisition et le traitement des données

Cette section va être la viande du projet, avec tout le code à écrire dans App.vue pour ce qui suit :

  • Saisie de l'emplacement par l'utilisateur — une zone de saisie et un bouton d'appel à l'action suffisent ;
  • Fonctions utilitaires pour diverses tâches ; ces fonctions seront appelées plus tard dans différentes parties du code du composant ;
  • Obtenir des données de géolocalisation détaillées de l'API Google Maps pour JavaScript ;
  • Obtenir des données météorologiques détaillées de l'API Dark Sky ;
  • Mise en forme et traitement des données de géolocalisation et météo, qui seront transmises aux composants enfants.

Les sous-sections qui suivent illustrent comment nous pouvons mettre en œuvre les tâches qui nous sont présentées dans les points ci-dessus. À quelques exceptions près, la plupart d'entre eux suivront la séquence.

Entrée de l'utilisateur

Il est bien évident que l'action démarre lorsque l'utilisateur fournit le nom du lieu pour lequel les données météo doivent être affichées. Pour que cela se produise, nous devons implémenter les éléments suivants :

  • Une zone de saisie pour entrer l'emplacement ;
  • Un bouton de soumission qui indique à notre application que l'utilisateur a entré l'emplacement et qu'il est temps de faire le reste. Nous allons également implémenter le comportement lorsque le traitement démarre en appuyant sur Enter .

Le code que nous montrons ci-dessous sera limité à la partie modèle HTML de App.vue . Nous mentionnerons juste le nom de la méthode associée aux événements de clic, et les définirons plus tard dans l'objet méthodes du <script> dans 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>

Placer l'extrait ci-dessus au bon endroit est trivial - nous vous le laissons. Cependant, les parties intéressantes de l'extrait sont :

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

Comme vous le savez dans les sections précédentes, @ est le raccourci de Vue pour la directive v-on :, qui est associée à un événement. La nouveauté est " organizeAllDetails " - ce n'est rien d'autre que la méthode qui se déclenchera une fois que les événements (en appuyant sur Entrée ou en cliquant sur le bouton) se produiront. Nous devons encore définir la méthode, et le puzzle sera complet à la fin de cette section.

Affichage des informations textuelles contrôlé par App.vue

Une fois que l'entrée de l'utilisateur déclenche l'action et que de nombreuses données sont acquises à partir des API, nous rencontrons la question inévitable : "Que faire de toutes ces données ?". Évidemment, un certain massage des données est nécessaire, mais cela ne répond pas entièrement à notre question ! Nous devons décider quelle est l'utilisation finale des données, ou plus directement, quelles sont les entités qui reçoivent différents morceaux des données acquises et traitées ?

Les composants enfants d' App.vue , en fonction de leur hiérarchie et de leur objectif, sont les prétendants de première ligne pour la majeure partie des données. Cependant, nous aurons également des données qui n'appartiennent à aucun de ces composants enfants, mais qui sont assez informatives et complètent le tableau de bord. Nous pouvons en faire bon usage si nous les affichons sous forme d'informations textuelles directement contrôlées par App.vue , tandis que le reste des données est transmis à l'enfant pour être finalement affiché sous forme de jolis graphiques.

Avec ce contexte à l'esprit, concentrons-nous sur le code pour définir le stade de l'utilisation des données textuelles. C'est un modèle HTML simple à ce stade, sur lequel les données finiront par venir s'asseoir.

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

Dans l'extrait ci-dessus, vous devez comprendre ce qui suit :

  • Les éléments à l'intérieur de {{ }} - c'est la façon dont Vue insère des données dynamiques dans le modèle HTML, avant qu'il ne s'affiche dans le navigateur. Vous les avez déjà rencontrés, et il n'y a rien de nouveau ou de surprenant. Gardez simplement à l'esprit que ces objets de données proviennent de la méthode data() dans l'objet export default() de App.vue . Ils ont des valeurs par défaut que nous définirons en fonction de nos besoins, puis nous écrirons certaines méthodes pour remplir les objets avec de vraies données d'API.

Ne vous inquiétez pas de ne pas voir les changements sur le navigateur — les données ne sont pas encore définies, et il est naturel que Vue ne rende pas des choses qu'il ne connaît pas. Cependant, une fois les données définies (et pour l'instant, vous pouvez même vérifier en codant en dur les données), les données textuelles seront contrôlées par App.vue .

La méthode data()

La méthode data() est une construction spéciale dans les fichiers .vue - elle contient et renvoie des objets de données qui sont si cruciaux pour l'application. Rappelez-vous la structure générique de la partie <script> dans n'importe quel fichier .vue — elle contient approximativement ce qui suit :

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

Jusqu'à présent, vous avez rencontré les noms de certains des objets de données, mais il y en a beaucoup plus. La plupart d'entre eux sont pertinents pour les composants enfants, dont chacun gère un aspect différent du vidage des informations météorologiques. Vous trouverez ci-dessous l'intégralité de la méthode data() dont nous aurons besoin pour ce projet - vous aurez une bonne idée des données que nous attendons des API et de la manière dont nous diffusons les données, en fonction de la nomenclature des objets.

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

Comme vous pouvez le voir, dans la plupart des cas, la valeur par défaut est vide, car cela suffira à ce stade. Des méthodes seront écrites pour manipuler les données et les remplir avec les valeurs appropriées, avant qu'elles ne soient rendues ou transmises aux composants enfants.

Méthodes dans App.vue

Pour les fichiers .vue , les méthodes sont généralement écrites sous forme de valeurs de clés imbriquées dans l'objet methods { } . Leur rôle principal est de manipuler les objets de données du composant. Nous écrirons les méthodes dans App.vue en gardant la même philosophie à l'esprit. Cependant, en fonction de leur objectif, nous pouvons classer les méthodes d' App.vue dans les catégories suivantes :

  • Méthodes utilitaires
  • Méthodes orientées action/événement
  • Méthodes d'acquisition de données
  • Méthodes de traitement des données
  • Méthodes de colle de haut niveau

Il est important que vous compreniez cela - nous vous présentons les méthodes sur un plateau car nous avons déjà compris comment fonctionnent les API, quelles données elles fournissent et comment nous devrions utiliser les données dans notre projet. Ce n'est pas que nous ayons sorti les méthodes de nulle part et écrit un code mystérieux pour traiter les données. Aux fins d'apprentissage, c'est un bon exercice pour lire et comprendre avec diligence le code des méthodes et des données. Cependant, face à un nouveau projet que vous devez construire à partir de zéro, vous devez faire tout le sale boulot vous-même, et cela signifie expérimenter beaucoup avec les API - leur accès programmatique et leur structure de données, avant de les coller de manière transparente avec les données structure que votre projet exige. Vous n'aurez aucune prise en main et il y aura des moments frustrants, mais tout cela fait partie de la maturation en tant que développeur.

Dans les sous-sections suivantes, nous expliquerons chacun des types de méthodes et montrerons également la mise en œuvre des méthodes appartenant à cette catégorie. Les noms de méthodes sont assez explicites quant à leur objectif, tout comme leur mise en œuvre, que nous pensons que vous trouverez assez facile à suivre. Cependant, avant cela, rappelez-vous le schéma général des méthodes d'écriture dans les fichiers .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>

Méthodes utilitaires

Les méthodes utilitaires, comme leur nom l'indique, sont des méthodes écrites principalement dans le but de modulariser le code répétitif utilisé pour les tâches marginales. Ils sont appelés par d'autres méthodes si nécessaire. Ci-dessous sont les méthodes utilitaires pour 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; },

Bien que nous ne l'ayons pas implémenté, vous pouvez retirer les méthodes utilitaires du fichier .vue et les placer dans un fichier JavaScript séparé. Tout ce que vous avez à faire est d'importer le fichier .js au début de la partie script dans le fichier .vue , et vous devriez être prêt à partir. Une telle approche fonctionne très bien et maintient le code propre, en particulier dans les grandes applications où vous pouvez utiliser de nombreuses méthodes mieux regroupées en fonction de leur objectif. Vous pouvez appliquer cette approche à tous les groupes de méthodes répertoriés dans cet article et voir l'effet lui-même. Cependant, nous vous suggérons de faire cet exercice une fois que vous avez suivi le cours présenté ici, afin que vous ayez une vue d'ensemble de toutes les parties fonctionnant en parfaite synchronisation, et que vous disposiez également d'un logiciel fonctionnel auquel vous pouvez vous référer, une fois que quelque chose pauses pendant l'expérimentation.

Méthodes orientées action/événement

Ces méthodes sont généralement exécutées lorsque nous devons effectuer une action correspondant à un événement. Selon le cas, l'événement peut être déclenché à partir d'une interaction utilisateur ou par programmation. Dans le fichier App.vue , ces méthodes se trouvent sous les méthodes utilitaires.

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

Une chose intéressante dans certains des extraits de code ci-dessus est l'utilisation de $ref . En termes simples, c'est la façon dont Vue associe l'instruction de code qui la contient à la construction HTML qu'elle est censée affecter (pour plus d'informations, lisez le guide officiel). Par exemple, les méthodes makeInputEmpty() et detectEnterKeyPress() affectent la zone de saisie, car dans le HTML de la zone de saisie, nous avons mentionné la valeur de l'attribut ref comme input .

Méthodes d'acquisition de données

Nous utilisons les deux API suivantes dans notre projet :

  • API Google Maps Geocoder
    Cette API permet d'obtenir les coordonnées de l'emplacement recherché par l'utilisateur. Vous aurez besoin d'une clé API pour vous-même, que vous pouvez obtenir en suivant la documentation dans le lien donné. Pour l'instant, vous pouvez utiliser la clé API utilisée par FusionCharts, mais nous vous demandons de ne pas en abuser et d'obtenir votre propre clé. Nous nous référons à l'API JavaScript de l'index.html de ce projet, et nous utiliserons les constructeurs fournis par celle-ci pour notre code dans le fichier App.vue .
  • L'API météo Dark Sky
    Cette API permet d'obtenir les données météorologiques correspondant aux coordonnées. Cependant, nous ne l'utiliserons pas directement ; nous l'envelopperons dans une URL qui redirigera via l'un des serveurs de FusionCharts. La raison en est que si vous envoyez une requête GET à l'API à partir d'une application entièrement cliente comme la nôtre, cela entraîne l'erreur CORS frustrante (plus d'informations ici et ici).

Remarque importante : Depuis que nous avons utilisé les API Google Maps et Dark Sky, ces deux API ont leurs propres clés API que nous avons partagées avec vous dans cet article. Cela vous aidera à vous concentrer sur les développements côté client plutôt que sur le casse-tête de la mise en œuvre du backend. Cependant, nous vous recommandons de créer vos propres clés , car nos clés API seront assorties de limites et si ces limites dépassent, vous ne pourrez pas essayer l'application par vous-même.

Pour Google Maps, rendez-vous sur cet article pour obtenir votre clé API. Pour l'API Dark Sky, visitez https://darksky.net/dev pour créer votre clé API et les points de terminaison respectifs.

Avec le contexte à l'esprit, voyons la mise en œuvre des méthodes d'acquisition de données pour notre projet.

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

Méthodes de colle de haut niveau

Avec l'utilité, l'acquisition et les méthodes de traitement à l'écart, il nous reste maintenant la tâche d'orchestrer l'ensemble. Nous le faisons en créant des méthodes de collage de haut niveau, qui appellent essentiellement les méthodes écrites ci-dessus dans une séquence particulière, de sorte que toute l'opération soit exécutée de manière transparente.

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

monté

Vue fournit des hooks de cycle de vie d'instance - des propriétés qui sont essentiellement des méthodes et se déclenchent lorsque le cycle de vie de l'instance atteint ce stade. Par exemple, created, mount, beforeUpdate, etc., sont tous des hooks de cycle de vie très utiles qui permettent au programmeur de contrôler l'instance à un niveau beaucoup plus granulaire que cela n'aurait été possible autrement.

Dans le code d'un composant Vue, ces hooks de cycle de vie sont implémentés comme vous le feriez pour n'importe quel autre prop . Par exemple:

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

Armé de cette nouvelle compréhension, jetez un œil au code ci-dessous pour le prop mounted d' App.vue :

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

Code complet pour App.vue

Nous avons couvert beaucoup de terrain dans cette section, et les dernières sections vous ont donné des choses en morceaux. Cependant, il est important que vous disposiez du code complet et assemblé pour App.vue (sous réserve de modifications ultérieures dans les sections suivantes). Ici ça va:

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

Et enfin, après tant de patience et de travail acharné, vous pouvez voir le flux de données avec sa puissance brute ! Visitez l'application sur le navigateur, actualisez la page, recherchez un emplacement dans la zone de recherche de l'application et appuyez sur Entrée !

L'application telle qu'elle apparaît dans le navigateur
( Grand aperçu )

Maintenant que nous en avons fini avec tout le gros du travail, faites une pause. Les sections suivantes se concentrent sur l'utilisation des données pour créer des graphiques beaux et informatifs, puis donnent à notre application laide une séance de toilettage bien méritée à l'aide de CSS.

5. Visualisation des données avec FusionCharts

Considérations fondamentales pour les graphiques

Pour l'utilisateur final, l'essence d'un tableau de bord est essentiellement ceci : une collection d'informations filtrées et soigneusement organisées sur un sujet particulier, transmises par des instruments visuels/graphiques pour une ingestion rapide. Ils ne se soucient pas des subtilités de l'ingénierie de votre pipeline de données ou de l'esthétique de votre code - tout ce qu'ils veulent, c'est une vue de haut niveau en 3 secondes. Par conséquent, notre application grossière affichant des données textuelles ne signifie rien pour eux, et il est grand temps que nous mettions en place des mécanismes pour envelopper les données avec des graphiques.

Cependant, avant de nous plonger dans la mise en œuvre des graphiques, examinons quelques questions pertinentes et les réponses possibles de notre point de vue :

  • Quels types de graphiques sont appropriés pour le type de données que nous traitons ?
    Eh bien, la réponse a deux aspects - le contexte et le but. Par contexte, nous entendons le type de données, et elles s'inscrivent globalement dans le schéma de choses plus importantes, délimitées par la portée et l'audience du projet. Et par objectif, nous entendons essentiellement « sur quoi nous voulons mettre l'accent ? ». Par exemple, nous pouvons représenter la température d'aujourd'hui à différents moments de la journée en utilisant un graphique à colonnes (colonnes verticales de largeur égale, avec une hauteur proportionnelle à la valeur représentée par la colonne). Cependant, nous nous intéressons rarement aux valeurs individuelles, mais plutôt à la variation et à la tendance globales à travers les données. Pour répondre à l'objectif, il est dans notre intérêt d'utiliser un graphique en courbes, et nous le ferons sous peu.
  • Que faut-il garder à l'esprit avant de choisir une bibliothèque de graphiques ?
    Étant donné que nous réalisons un projet utilisant principalement des technologies basées sur JavaScript, il est évident que toute bibliothèque de graphiques que nous choisissons pour notre projet doit être native du monde JavaScript. Avec cette prémisse de base à l'esprit, nous devrions considérer ce qui suit avant de nous concentrer sur une bibliothèque particulière :
    • Prise en charge des frameworks de notre choix , qui dans ce cas, est Vue.js. Un projet peut être développé dans d'autres frameworks JavaScript populaires comme React ou Angular - vérifiez la prise en charge de la bibliothèque de graphiques pour votre framework préféré. En outre, la prise en charge d'autres langages de programmation populaires tels que Python, Java, C++, .Net (AS et VB), en particulier lorsque le projet implique des éléments importants de backend, doit être envisagée.
    • Disponibilité des types de graphiques et des fonctionnalités , car il est presque impossible de savoir à l'avance quelle sera la forme finale et le but des données dans le projet (surtout si les exigences sont réglementées par vos clients dans un cadre professionnel). Dans ce cas, vous devez élargir votre réseau et choisir une bibliothèque de cartes qui possède la plus grande collection de cartes. Plus important encore, pour différencier votre projet des autres, la bibliothèque doit avoir suffisamment de fonctionnalités sous la forme d'attributs de graphique configurables, afin que vous puissiez affiner et personnaliser la plupart des aspects des graphiques et le bon niveau de granularité. De plus, les configurations de graphique par défaut doivent être raisonnables et la documentation de la bibliothèque doit être de premier ordre, pour des raisons évidentes pour les développeurs professionnels.
    • La courbe d'apprentissage, la communauté de support et l'équilibre doivent également être pris en compte, en particulier lorsque vous débutez dans la visualisation de données. À une extrémité du spectre, vous avez des outils propriétaires tels que Tableau et Qlickview qui coûtent une bombe, ont une courbe d'apprentissage fluide, mais présentent également de nombreuses limitations en termes de personnalisation, d'intégration et de déploiement. À l'autre extrémité, il y a d3.js - vaste, gratuit (open source) et personnalisable jusqu'à la base, mais vous devez payer le prix d'une courbe d'apprentissage très abrupte pour pouvoir faire quoi que ce soit de productif avec la bibliothèque.

Ce dont vous avez besoin, c'est du sweet spot - le bon équilibre entre productivité, couverture, personnalisation, courbe d'apprentissage et, bien sûr, coût. Nous vous invitons à jeter un coup d'œil à FusionCharts - la bibliothèque de graphiques JavaScript la plus complète et la plus adaptée aux entreprises pour le Web et le mobile, que nous utiliserons dans ce projet pour créer des graphiques.

Introduction à FusionCharts

FusionCharts est utilisé dans le monde entier comme bibliothèque graphique JavaScript par des millions de développeurs répartis dans des centaines de pays à travers le monde. Techniquement, il est aussi chargé et configurable que possible, avec un support pour l'intégrer à presque toutes les piles technologiques populaires utilisées pour les projets Web. L'utilisation commerciale de FusionCharts nécessite une licence, et vous devez payer la licence en fonction de votre cas d'utilisation (veuillez contacter le service commercial si vous êtes curieux). Cependant, nous utilisons FusionCharts dans ces projets juste pour essayer quelques choses, et donc la version sans licence (vient avec un petit filigrane dans vos graphiques, et quelques autres restrictions). L'utilisation de la version sans licence convient parfaitement lorsque vous essayez les graphiques et que vous les utilisez dans vos projets non commerciaux ou personnels. Si vous envisagez de déployer l'application à des fins commerciales, veuillez vous assurer que vous disposez d'une licence de FusionCharts.

Puisqu'il s'agit d'un projet impliquant Vue.js, nous aurons besoin de deux modules qui doivent être installés, si ce n'est déjà fait :

  • Le module fusioncharts , car il contient tout ce dont vous aurez besoin pour créer les graphiques
  • Le module vue-fusioncharts , qui est essentiellement un wrapper pour fusioncharts, afin qu'il puisse être utilisé dans un projet Vue.js

Si vous ne les avez pas installés auparavant (comme indiqué dans la troisième section), installez-les en exécutant la commande suivante à partir du répertoire racine du projet :

 npm install fusioncharts vue-fusioncharts --save

Ensuite, assurez-vous que le fichier src/main.js du projet contient le code suivant (également mentionné dans la section 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) })

La ligne la plus critique dans l'extrait ci-dessus est peut-être la suivante :

 Vue.use(VueFusionCharts, FusionCharts)

Il demande à Vue d'utiliser le module vue-fusioncharts pour donner un sens à de nombreuses choses dans le projet qui ne sont apparemment pas explicitement définies par nous, mais qui sont définies dans le module lui-même. De plus, ce type d'instruction implique une déclaration globale , ce qui signifie que partout où Vue rencontre quelque chose d'étrange dans le code de notre projet (choses que nous n'avons pas explicitement définies concernant l'utilisation de FusionCharts), il regardera au moins une fois dans le vue-fusioncharts et les modules de nœuds fusioncharts pour leurs définitions, avant de générer des erreurs. Si nous avions utilisé FusionCharts dans une partie isolée de notre projet (ne l'utilisant pas dans presque tous les fichiers de composants), alors peut-être que la déclaration locale aurait eu plus de sens.

Avec cela, vous êtes prêt à utiliser FusionCharts dans le projet. Nous utiliserons une grande variété de cartes, le choix dépendant de l'aspect des données météorologiques que nous voulons visualiser. En outre, nous verrons l'interaction de la liaison de données, des composants personnalisés et des observateurs en action.

Schéma général d'utilisation de Fusioncharts dans les fichiers .vue

Dans cette section, nous expliquerons l'idée générale de l'utilisation de FusionCharts pour créer divers graphiques dans les fichiers .vue . Mais d'abord, voyons le pseudocode qui illustre schématiquement l'idée de base.

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

Comprenons différentes parties du pseudocode ci-dessus :

  • Dans le <template> , dans le niveau supérieur <div> (c'est à peu près obligatoire pour le code HTML du modèle de chaque composant), nous avons le composant personnalisé <fusioncharts> . Nous avons la définition du composant contenu dans le module Node vue-fusioncharts que nous avons installé pour ce projet. En interne, vue-fusioncharts s'appuie sur le module fusioncharts , qui a également été installé. Nous avons importé les modules nécessaires et résolu leurs dépendances, demandé à Vue d'utiliser le wrapper globalement (tout au long du projet) dans le fichier src/main.js , et donc il n'y a pas de manque de définition pour le composant personnalisé <fusioncharts> que nous avons utilisé ici. En outre, le composant personnalisé a des attributs personnalisés, et chacun des attributs personnalisés est lié à un objet de données (et à son tour, à leurs valeurs), par la directive v-bind , dont le raccourci est le symbole deux-points ( : ). Nous en apprendrons plus sur les attributs et leurs objets de données associés plus en détail, lorsque nous discuterons de certains des graphiques spécifiques utilisés dans ce projet.
  • Dans le <script> , vous déclarez d'abord les props que le composant est censé recevoir, puis continuez à définir les objets de données qui sont liés aux attributs de <fusioncharts> . Les valeurs attribuées aux objets de données sont les valeurs extraites par les attributs de <fusioncharts> , et les graphiques sont créés sur la base de ces valeurs extraites. En dehors de ceux-ci, la partie la plus intéressante du code est l'objet watch { } . Il s'agit d'un objet très spécial dans le schéma des choses de Vue - il demande essentiellement à Vue de surveiller tout changement apporté à certaines données, puis de prendre des mesures en fonction de la façon dont la fonction de handler de ces données a été définie. Par exemple, nous voulons que Vue surveille le prop reçu, c'est-à-dire data_prop_received_by_the_component dans le pseudocode. Le prop devient une clé dans l'objet watch { } et la valeur de la clé est un autre objet - une méthode de gestionnaire qui décrit ce qui doit être fait chaque fois que le prop change. Avec des mécanismes aussi élégants pour gérer les changements, l'application conserve sa réactivité. Le deep: true représente un indicateur booléen que vous pouvez associer à des observateurs, de sorte que l'objet surveillé est surveillé assez profondément, c'est-à-dire que même les modifications apportées aux niveaux imbriqués de l'objet sont suivies.
    ( Pour plus d'informations sur les watchers, consultez la documentation officielle ).

Maintenant que vous êtes équipé d'une compréhension du schéma général des choses, plongeons dans les implémentations spécifiques des graphiques dans les fichiers de composants .vue . Le code sera assez explicite et vous devriez essayer de comprendre comment les détails s'intègrent dans le schéma général des choses décrites ci-dessus.

Implémentation des graphiques dans les fichiers .vue

Bien que les spécificités de la mise en œuvre varient d'un graphique à l'autre, l'explication suivante s'applique à tous :

  • <template>
    Comme expliqué précédemment, le composant personnalisé <fusioncharts> a plusieurs attributs, chacun d'eux étant lié à l'objet de données correspondant défini dans la fonction data() en utilisant la directive v-bind :. Les noms d'attributs sont assez explicites pour ce qu'ils signifient, et déterminer les objets de données correspondants est également trivial.
  • <script>
    Dans la fonction data() , les objets de données et leurs valeurs sont ce qui fait fonctionner les graphiques, en raison de la liaison effectuée par les directives v-bind ( : ) utilisées sur les attributs de <fusioncharts> . Avant d'approfondir les objets de données individuels, il convient de mentionner certaines caractéristiques générales :
    • Les objets de données dont les valeurs sont 0 ou 1 sont de nature booléenne, où 0 représente quelque chose d'indisponible/désactivé, et 1 représente la disponibilité/l'état activé. Cependant, soyez prudent car les objets de données non booléens peuvent également avoir 0 ou 1 comme valeurs, en plus d'autres valeurs possibles - cela dépend du contexte. Par exemple, containerbackgroundopacity avec sa valeur par défaut à 0 est booléen, alors que lowerLimit avec sa valeur par défaut à 0 signifie simplement que le nombre zéro est sa valeur littérale.
    • Certains objets de données traitent des propriétés CSS comme la marge, le remplissage, la taille de la police, etc. - la valeur a une unité implicite de "px" ou pixel. De même, d'autres objets de données peuvent avoir des unités implicites associées à leurs valeurs. Pour des informations détaillées, veuillez vous référer à la page des attributs de graphique respectifs du FusionCharts Dev Center.
  • Dans la fonction data() , l'objet peut-être le plus intéressant et le plus non évident est le dataSource. Cet objet contient trois objets principaux imbriqués :
    • chart : cet objet encapsule de nombreux attributs de graphique liés à la configuration et à la cosmétique du graphique. C'est presque une construction obligatoire que vous trouverez dans tous les graphiques que vous créerez pour ce projet.
    • colorrange : cet objet est quelque peu spécifique au graphique considéré et est principalement présent dans les graphiques qui traitent de plusieurs couleurs/nuances pour délimiter différentes sous-gammes de l'échelle utilisée dans le graphique.
    • value : Cet objet, encore une fois, est présent dans les graphiques qui ont une valeur spécifique qui doit être mise en évidence dans la plage de l'échelle.
  • L'objet watch { } est peut-être la chose la plus cruciale qui donne vie à ce graphique et aux autres graphiques utilisés dans ce projet. La réactivité des graphes, c'est-à-dire que les graphes se mettent à jour en fonction des nouvelles valeurs résultant d'une nouvelle requête utilisateur est contrôlée par les watchers définis dans cet objet. Par exemple, nous avons défini un observateur pour les mises en highlights des accessoires reçues par le composant, puis défini une fonction de gestionnaire pour indiquer à Vue les actions nécessaires qu'il doit entreprendre, lorsque quelque chose change concernant l'objet surveillé dans l'ensemble du projet. Cela signifie que chaque fois que App.vue une nouvelle valeur pour l'un des objets dans les highlights , les informations se répercutent jusqu'à ce composant et la nouvelle valeur est mise à jour dans les objets de données de ce composant. Le graphique étant lié aux valeurs, est également mis à jour à la suite de ce mécanisme.

Les explications ci-dessus sont assez larges pour nous aider à développer une compréhension intuitive de la situation dans son ensemble. Une fois que vous comprenez intuitivement les concepts, vous pouvez toujours consulter la documentation de Vue.js et FusionCharts, lorsque quelque chose ne vous est pas clair à partir du code lui-même. Nous vous laissons l'exercice, et à partir de la prochaine sous-section, nous n'expliquerons pas les choses que nous avons couvertes dans cette sous-section.

src/components/TempVarChart.vue

Un diagramme montrant la température horaire
( Grand aperçu )
 <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

Ce composant contient un graphique extrêmement utile - la jauge angulaire.

L'indice UV
( Grand aperçu )

Le code du composant est donné ci-dessous. Pour des informations détaillées sur les attributs de graphique de la jauge angulaire, reportez-vous à la page FusionCharts Dev Center pour la jauge angulaire.

 <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

Dans ce composant, nous utilisons une jauge linéaire horizontale pour représenter la visibilité, comme illustré dans l'image ci-dessous :

Une capture d'écran de la jauge linéaire horizontale représentant la visibilité aérienne (16 km)
( Grand aperçu )

Le code du composant est donné ci-dessous. Pour une compréhension approfondie des différents attributs de ce type de graphique, veuillez vous référer à la page FusionCharts Dev Center pour la jauge linéaire horizontale.

 <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

Ce composant affiche la vitesse et la direction du vent (vitesse du vent, si vous êtes averti en physique), et il est très difficile de représenter un vecteur à l'aide d'un graphique. Pour de tels cas, nous suggérons de les représenter à l'aide de quelques belles images et valeurs textuelles. Puisque la représentation à laquelle nous avons pensé dépend entièrement du CSS, nous l'implémenterons dans la section suivante qui traite du CSS. Cependant, jetez un œil à ce que nous visons à créer :

État du vent : direction du vent (à gauche) et vitesse du vent (à droite)
( Grand aperçu )
 <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>

Conclusion avec Highlights.vue

Rappelez-vous que nous avons déjà implémenté du code avec CSS pour tous les composants — sauf Content.vue et Highlights.vue . Étant donné que Content.vue est un composant stupide qui ne fait que relayer les données, le style minimal dont il a besoin a déjà été couvert. De plus, nous avons déjà écrit le code approprié pour styliser la barre latérale et les cartes contenant les graphiques. Par conséquent, il ne nous reste plus qu'à ajouter quelques éléments stylistiques à Highlights.vue , ce qui implique principalement l'utilisation des classes 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>

Déploiement et code source

Avec les graphiques et le style en ordre, nous avons terminé ! Prenez un moment pour apprécier la beauté de votre création.

Le résultat
( Grand aperçu )

Il est maintenant temps pour vous de déployer votre application et de la partager avec vos pairs. Si vous n'avez pas beaucoup d'idées sur le déploiement et que vous vous attendez à ce que nous vous aidions, consultez ici nos idées de déploiement. L'article lié contient également des suggestions sur la façon de supprimer le filigrane FusionCharts en bas à gauche de chaque graphique.

Si vous vous trompez quelque part et que vous voulez un point de référence, le code source est disponible sur Github.