Daten mit Serverless und Vue erkunden: Daten filtern und verwenden

Avatar of Sarah Drasner
Sarah Drasner am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

In diesem zweiten Artikel dieses Tutorials werden wir die Daten, die wir von unserer Serverless-Funktion erhalten haben, verwenden, um sie mit Vue und Vuex zu verteilen, unsere Tabelle zu aktualisieren und die Daten für unseren WebGL-Globus zu modifizieren. Dieser Artikel setzt Grundkenntnisse in Vue voraus. Das mit Abstand Coolste/Nützlichste, was wir in diesem Artikel behandeln werden, ist die Verwendung von Computed Properties in Vue.js zur performanten Filterung der Tabelle. Lesen Sie weiter!

Artikelserie

  1. GitHub-Dateien automatisch mit Serverless-Funktionen aktualisieren
  2. Daten filtern und verwenden (Sie sind hier!)
showing how the demo works

Sie können sich die Live-Demo hier ansehen oder den Code auf GitHub erkunden.

Zuerst werden wir eine vollständige Vue-App mit serverseitigem Rendering, Routing und Code-Splitting mit einem Tool namens Nuxt aufsetzen. (Dies ist ähnlich zu Zeit's Next.js für React). Wenn Sie das Vue CLI-Tool noch nicht installiert haben, führen Sie Folgendes aus:

npm install -g vue-cli
# or
yarn global add vue-cli

Dies installiert das Vue CLI global, sodass wir es jederzeit verwenden können. Dann führen wir Folgendes aus:

vue init nuxt/starter my-project
cd my-project
yarn

Das erstellt diese spezielle Anwendung. Jetzt können wir unseren lokalen Entwicklungs-Server mit Folgendem starten:

npm run dev

Wenn Sie mit Vuex noch nicht vertraut sind, ist es ähnlich wie bei React's Redux. Es gibt detailliertere Informationen darüber, was es ist und was es tut, in diesem Artikel hier.

import Vuex from 'vuex';
import speakerData from './../assets/cda-data.json';

const createStore = () => {
 return new Vuex.Store({
   state: {
     speakingColumns: ['Name', 'Conference', 'From', 'To', 'Location'],
     speakerData
   }
 });
};

export default createStore;

Hier ziehen wir die Sprecherdaten aus unserer `cda.json`-Datei, die nun mit Längen- und Breitengraden von unserer Serverless-Funktion aktualisiert wurde. Beim Importieren speichern wir sie in unserem State, damit wir sie anwendungsweit zugänglich haben. Möglicherweise stellen Sie auch fest, dass die Spalten nicht mehr dem entsprechen, was wir für unsere Tabelle verwenden möchten, da wir die JSON-Datei mit unserer Serverless-Funktion aktualisiert haben. Das ist in Ordnung! Wir speichern auch nur die Spalten, die wir benötigen, um die Tabelle zu erstellen.

Nun haben wir im Verzeichnis `pages` unserer App eine Datei `Index.vue`. Wenn wir weitere Seiten hätten, müssten wir sie lediglich zu diesem Verzeichnis hinzufügen. Vorerst verwenden wir diese Indexseite und einige Komponenten in unserer Vorlage.

<template>
 <section>
   <h1>Cloud Developer Advocate Speaking</h1>
   <h3>Microsoft Azure</h3>
   <div class="tablecontain">
      ...
      <speaking-table></speaking-table>
    </div>
    <more-info></more-info>
    <speaking-globe></speaking-globe>
 </section>
</template>

Wir werden alle unsere Daten aus dem Vuex-Store abrufen und dafür eine Computed Property verwenden. Wir werden auch eine Möglichkeit schaffen, diese Daten in einer Computed Property zu filtern. Schließlich werden wir diese gefilterte Eigenschaft sowohl an die Sprechertabelle als auch an den Sprecherglobus übergeben.

  computed: {
    speakerData() {
      return this.$store.state.speakerData;
    },
    columns() {
      return this.$store.state.speakingColumns;
    },
    filteredData() {
      const x = this.selectedFilter,
        filter = new RegExp(this.filteredText, 'i')
      return this.speakerData.filter(el => {
        if (el[x] !== undefined) { return el[x].match(filter) }
        else return true;
      })
    }
  }
}</script>

Sie werden feststellen, dass wir die Namen der Computed Properties, selbst in anderen Computed Properties, auf die gleiche Weise verwenden wie Daten - d.h. `speakerData()` wird in dem Filter zu `this.speakerData`. Es wäre auch verfügbar als `{{ speakerData }}` in unserer Vorlage und so weiter. So werden sie verwendet. Das schnelle Sortieren und Filtern einer großen Datenmenge in einer Tabelle basierend auf Benutzereingaben ist definitiv eine Aufgabe für Computed Properties. In diesem Filter prüfen wir auch, ob wir Dinge wegen Groß-/Kleinschreibung ausschließen oder versuchen, eine Zeile abzugleichen, die undefiniert ist, da unsere Daten manchmal Lücken aufweisen.

Dies ist ein wichtiger Teil zum Verständnis, da Computed Properties in Vue unglaublich nützlich sind. Es handelt sich um Berechnungen, die basierend auf ihren Abhängigkeiten gecacht werden und nur dann aktualisiert werden, wenn sie benötigt werden. Das bedeutet, dass sie bei guter Nutzung extrem performant sind. Computed Properties werden nicht wie Methoden verwendet, obwohl sie anfangs ähnlich aussehen mögen. Wir können sie auf die gleiche Weise registrieren, typischerweise mit begleitender Logik, aber sie werden eigentlich mehr wie Daten verwendet. Sie können sie als eine weitere Ansicht Ihrer Daten betrachten.

Berechnete Werte sind sehr wertvoll für die Manipulation bereits vorhandener Daten. Wann immer Sie etwas erstellen, bei dem Sie eine große Datenmenge durchsuchen müssen und diese Berechnungen nicht bei jedem Tastendruck wiederholen möchten, sollten Sie über die Verwendung eines berechneten Wertes nachdenken. Ein weiterer guter Kandidat wäre, wenn Sie Informationen aus Ihrem Vuex-Store abrufen. Sie könnten diese Daten abrufen und cachen.

Eingaben erstellen

Nun möchten wir dem Benutzer ermöglichen, auszuwählen, welche Art von Daten er filtern möchte. Um diese Computed Property zur Filterung basierend auf Benutzereingaben zu verwenden, können wir einen Wert als leeren String in unseren Daten erstellen und `v-model` verwenden, um eine Beziehung zwischen dem, was in diesem Suchfeld eingegeben wird, und den Daten herzustellen, die wir in der früheren `filteredData`-Funktion filtern möchten. Wir möchten auch, dass sie eine Kategorie auswählen können, um ihre Suche einzugrenzen. In unserem Fall haben wir bereits Zugriff auf diese Kategorien, sie sind die gleichen wie die Spalten, die wir für die Tabelle verwendet haben. Daher können wir eine Auswahl mit einem entsprechenden Label erstellen:

<label for="filterLabel">Filter By</label>
 <select id="filterLabel" name="select" v-model="selectedFilter">
 <option v-for="column in columns" key="column" :value="column">
   {{ column }}
 </option>
</select>

Wir werden auch das zusätzliche Filterfeld in eine `v-if`-Direktive einschließen, da es für den Benutzer nur verfügbar sein sollte, wenn er bereits eine Spalte ausgewählt hat.

<span v-if="selectedFilter">
  <label for="filterText" class="hidden">{{ selectedFilter }}</label>
  <input id="filteredText" type="text" name="textfield" v-model="filteredText"></input>
</span>

Tabelle erstellen

Nun übergeben wir die gefilterten Daten an die Sprechertabelle und den Sprecherglobus.

<speaking-globe :filteredData="filteredData"></speaking-globe>

Was es uns ermöglicht, unsere Tabelle sehr schnell zu aktualisieren. Wir können auch Direktiven gut nutzen, um unsere Tabelle klein, deklarativ und lesbar zu halten.

<table class="scroll">
 <thead>
   <tr>
     <th v-for="key in columns">
       {{ key }}
     </th>
   </tr>
 </thead>
 <tbody>
   <tr v-for="(post, i) in filteredData">
     <td v-for="entry in columns">
       <a :href="post.Link" target="_blank">
         {{ post[entry] }}
       </a>
     </td>
   </tr>
 </tbody>
</table>

Da wir die übergebene Computed Property verwenden, die von der Eingabe aktualisiert wird, wird diese andere Ansicht der Daten verwendet und nur dann aktualisiert, wenn die Daten irgendwie geändert werden, was ziemlich selten vorkommt.

Und nun haben wir eine performante Möglichkeit, viele Daten in einer Tabelle mit Vue zu durchsuchen. Die Direktiven und Computed Properties sind hier die Helden und machen es sehr einfach, dies deklarativ zu schreiben.

filtering the data in the table

Ich liebe es, wie schnell die Informationen mit wenig Aufwand von unserer Seite gefiltert werden. Computed Properties nutzen die Caching-Fähigkeit von Vue hervorragend aus.

Globe-Visualisierung erstellen

Wie bereits erwähnt, verwende ich eine Bibliothek von Google dataarts für den Globus, die in diesem Repo zu finden ist.

Der Globus ist out-of-the-box wunderschön, aber wir brauchen zwei Dinge, um damit arbeiten zu können: Wir müssen unsere Daten modifizieren, um das von der Globe erwartete JSON zu erstellen, und wir müssen genug über three.js wissen, um sein Aussehen zu aktualisieren und es in Vue zum Laufen zu bringen.

Es ist ein älteres Repo, daher ist es nicht als npm-Modul installierbar, was in unserem Fall eigentlich in Ordnung ist, da wir die Art und Weise, wie es aussieht, ein wenig manipulieren werden, weilich eine Kontrollfreak bin hüstel Ich meine, wir möchten damit spielen, um ihn zu unserem eigenen zu machen.

Das Auslagern des gesamten Inhalts dieses Repos in eine Methode ist jedoch nicht sehr sauber, daher werde ich einen Mixin verwenden. Der Mixin ermöglicht uns zwei Dinge: Er hält unseren Code modular, sodass wir nicht durch eine riesige Datei scrollen müssen, und er ermöglicht uns, diesen Globus wiederzuverwenden, falls wir ihn jemals auf einer anderen Seite unserer App platzieren möchten.

Ich registriere den Globus so:

import * as THREE from 'three';
import { createGlobe } from './../mixins/createGlobe';

export default {
 mixins: [createGlobe],
  …
}

und erstelle eine separate Datei in einem Verzeichnis namens `mixins` (falls ich weitere Mixins erstellen möchte) mit dem Namen `createGlobe.js`. Für weitere Informationen über Mixins, ihre Funktionsweise und was sie tun, lesen Sie diesen anderen Artikel, den ich über die Arbeit mit ihnen geschrieben habe.

Daten modifizieren

Wenn Sie sich an den ersten Artikel erinnern, benötigen wir, um den Globus zu erstellen, Werte, die so aussehen:

var data = [
    [
    'seriesA', [ latitude, longitude, magnitude, latitude, longitude, magnitude, ... ]
    ],
    [
    'seriesB', [ latitude, longitude, magnitude, latitude, longitude, magnitude, ... ]
    ]
];

Bisher liefert uns der `filteredData`-Computed-Wert, den wir aus unserem Store zurückgeben, für jeden Eintrag unseren Längen- und Breitengrad, da wir diese Informationen aus unserer Computed Property erhalten haben. Vorerst möchten wir nur eine Ansicht dieses Datensatzes, nur die Daten unseres Teams, aber in Zukunft möchten wir vielleicht auch Informationen von anderen Teams sammeln, also sollten wir es so aufbauen, dass neue Werte relativ einfach hinzugefügt werden können.

Erstellen wir eine weitere Computed Property, die die Daten so zurückgibt, wie wir sie benötigen. Wir erstellen sie zunächst als Objekt, da dies beim Erstellen effizienter ist, und dann erstellen wir ein Array.

teamArr() {
  //create it as an object first because that's more efficient than an array
  var endUnit = {};
  //our logic to build the data will go here

  //we'll turn it into an array here
  let x = Object.entries(endUnit);
  let area = [],
    places,
    all;

  for (let i = 0; i < x.length; i++) {
    [all, places] = x[i];
    area.push([all, [].concat(...Object.values(places))]);
  }
  return area;
}

In dem gerade erstellten Objekt prüfen wir, ob unsere Werte bereits existieren, und wenn nicht, erstellen wir einen neuen. Wir müssen auch einen Schlüssel aus dem zusammengefügten Längen- und Breitengrad erstellen, damit wir doppelte Instanzen überprüfen können. Das ist besonders hilfreich, weil ich nicht weiß, ob meine Teamkollegen den Ort nur als Stadt oder als Stadt und Bundesland eingeben. Die Google Maps API ist in dieser Hinsicht ziemlich nachsichtig - sie können einen konsistenten Ort für beide Zeichenketten finden.

Wir werden auch den kleinsten und inkrementellen Wert für die Vergrößerung festlegen. Unsere Entscheidung für die Vergrößerung wird hauptsächlich aus Versuch und Irrtum entstehen, indem wir diesen Wert anpassen und sehen, was sinnvoll ist. Mein erster Versuch hier waren lange, wackelige Stäbe und es sah aus wie ein glatzköpfiger, kaputter Stachelschwein, es dauerte eine Minute, bis ich einen Wert fand, der funktionierte.

this.speakerData.forEach(function(index) {
   let lat = index.Latitude,
      long = index.Longitude,
      key = lat + ", " + long,
      magBase = 0.1,
      val = 'Microsoft CDAs';

   //if we either the latitude or longitude are missing, skip it
   if (lat === undefined || long === undefined) return;

   //because the pins are grouped together by magnitude, as we build out the data, we need to check if one exists or increment the value
   if (val in endUnit) {

     //if we already have this location (stored together as key) let's increment it
     if (key in endUnit[val]) {
       //we'll increase the maginifation here
     }
   } else {
     //we'll create the new values here
   }

 })

Nun prüfen wir, ob der Ort bereits existiert, und wenn ja, inkrementieren wir ihn. Wenn nicht, erstellen wir neue Werte für ihn.

this.speakerData.forEach(function(index) {
...

  if (val in endUnit) {
    //if we already have this location (stored together as key) let's increment it
    if (key in endUnit[val]) {
      endUnit[val][key][2] += magBase;
    } else {
      endUnit[val][key] = [lat, long, magBase];
    }
  } else {
    let y = {};
    y[key] = [lat, long, magBase];
    endUnit[val] = y;
  }

})

Interessant aussehen lassen

Ich habe bereits erwähnt, dass ein Grund, warum wir die grundlegenden dataarts JavaScript-Dateien in einem Mixin speichern möchten, darin besteht, dass wir einige Modifikationen an seinem Aussehen vornehmen möchten. Lassen Sie uns auch darüber sprechen, denn das ist ein Aspekt jeder interessanten Datenvisualisierung.

Wenn Sie nicht viel über die Arbeit mit three.js wissen, ist es eine Bibliothek, die ziemlich gut dokumentiert ist und viele Beispiele zum Ausprobieren bietet. Der eigentliche Durchbruch in meinem Verständnis dafür, was es ist und wie man damit arbeitet, kam jedoch nicht wirklich aus diesen Quellen. Ich habe viel aus Rachel Smith's Serie auf Codepen und Chris Gammon's (nicht zu verwechseln mit Chris Gannon) exzellenter YouTube-Serie gewonnen. Wenn Sie nicht viel über three.js wissen und es für 3D-Datenvisualisierung verwenden möchten, ist mein Vorschlag, dort zu beginnen.

Das erste, was wir tun werden, ist, die Farben der Pins auf dem Globus anzupassen. Die Standardfarben sind schön, aber sie passen nicht zum Stil unserer Seite oder zur Vergrößerung, die wir für diese Daten benötigen. Der zu aktualisierende Code befindet sich in Zeile 11 unseres Mixins.

const colorFn = opts.colorFn || function(x) {
  let c = new THREE.Color();
  c.setHSL(0.1 - x * 0.19, 1.0, 0.6);
  return c;
};

Wenn Sie damit nicht vertraut sind, ist HSL ein wunderbar menschenlesbares Farbformat, das es einfach macht, die Farben unserer Pins in einem Bereich zu aktualisieren.

  • H steht für Farbton, der uns als Kreis gegeben wird. Das ist großartig für generative Projekte wie dieses, denn im Gegensatz zu vielen anderen Farbformaten wird es niemals fehlschlagen. 20 Grad ergeben den gleichen Wert wie 380 Grad und so weiter. Das x, das wir hier übergeben, hat eine Beziehung zu unserer Vergrößerung, daher möchten wir herausfinden, wo dieser Bereich beginnt und um wie viel er sich erhöht.
  • Der zweite Wert ist die Sättigung, die wir hier auf Maximum setzen, damit sie hervorsticht - auf einer Skala von 0 bis 1 ist 1,0 der höchste Wert.
  • Der dritte Wert ist die Helligkeit. Wie bei der Sättigung erhalten wir einen Wert von 0 bis 1, und wir verwenden diesen in der Mitte bei 0,5.

Sie können sehen, wenn ich nur eine kleine Änderung an dieser einen Codezeile zu `c.setHSL(0.6 - x * 0.7, 1.0, 0.4);` vornehme, würde sich der Farbbereich dramatisch ändern.

two different color patterns for the pins on the globe

Wir werden auch einige andere Feinabstimmungen vornehmen: Der Globus wird ein Kreis sein, aber er wird ein Bild für die Textur verwenden. Wenn wir diese Form in einen Ikosaeder oder sogar einen Torusknoten ändern wollten, könnten wir das tun, wir müssten nur eine Zeile Code hier ändern.

//from
const geometry = new THREE.SphereGeometry(200, 40, 30);
//to 
const geometry = new THREE.IcosahedronGeometry(200, 0);

und wir würden so etwas erhalten, Sie können sehen, dass die Textur sich auch an diese neue Form anpasst.

showing how we can change the shape of the globe to an icosahedron

Seltsam und cool, und vielleicht in diesem Fall nicht nützlich, aber es ist wirklich schön, dass das Erstellen einer dreidimensionalen Form mit three.js so einfach ist. Benutzerdefinierte Formen werden jedoch etwas komplexer.

Wir laden diese Textur anders in Vue als die Bibliothek es tun würde - wir müssen sie erhalten, sobald die Komponente gemountet ist und sie laden, und sie als Parameter übergeben, wenn wir auch den Globus instanziieren. Sie werden bemerken, dass wir keinen relativen Pfad zum Assets-Ordner erstellen müssen, da Nuxt und Webpack dies im Hintergrund für uns erledigen. Wir können statische Bilddateien auf diese Weise leicht verwenden.

mounted() {
  let earthmap = THREE.ImageUtils.loadTexture('/world4.jpg');
  this.initGlobe(earthmap);
}

Dann wenden wir diese übergebene Textur hier an, wenn wir das Material erstellen.

uniforms = THREE.UniformsUtils.clone(shader.uniforms);
uniforms['texture'].value = imageLoad;

material = new THREE.ShaderMaterial({
  uniforms: uniforms,
  vertexShader: shader.vertexShader,
  fragmentShader: shader.fragmentShader
});

Es gibt so viele Möglichkeiten, wie wir mit diesen Daten arbeiten und die Ausgabe ändern könnten - wir könnten die weißen Bänder um den Globus anpassen, wir könnten die Form des Globus mit einer Zeile Code ändern, wir könnten ihn mit Partikeln umgeben. Der Fantasie sind keine Grenzen gesetzt!


Und da haben wir es! Wir verwenden eine Serverless-Funktion zur Interaktion mit der Google Maps API, wir verwenden Nuxt, um die Anwendung mit serverseitigem Rendering zu erstellen, wir verwenden berechnete Werte in Vue, um diese Tabelle schlank, deklarativ und performant zu machen. Die Arbeit mit all diesen Technologien kann zu wirklich unterhaltsamen, explorativen Wegen führen, Daten zu betrachten.

Artikelserie

  1. GitHub-Dateien automatisch mit Serverless-Funktionen aktualisieren
  2. Daten filtern und verwenden (Sie sind hier!)