„Es sieht toll aus, aber könnten Sie einfach einen Excel-Import hinzufügen?“
Wenn Sie schon lange Software entwickeln, haben Sie diese Frage wahrscheinlich schon mehr als einmal von einem Manager gehört. Für einen Laien klingt die Frage nach Excel-Import/Export nicht nach einer großen Sache. Wie schwer kann das schon sein, oder?
Doch allzu oft löst diese Frage bei Entwicklern Angst aus. Auf den meisten Plattformen ist die Arbeit mit Excel-Dateien sehr aufwändig. Historisch gesehen gilt dies im Web umso mehr. Die Arbeit mit Excel-Daten in einer Web-App hat sich ein bisschen wie dieser xkcd-Comic angefühlt: „Es kann schwierig sein, den Unterschied zwischen dem Einfachen und dem praktisch Unmöglichen zu erklären.“ Das Erstellen einer eigenen Tabellenkalkulation im Browser mit Excel-Import und Excel-Export scheint ein Problem zu sein, dessen Lösung fünf Jahre und ein Forschungsteam dauern wird.
Das ändert sich gerade. Wir haben jetzt schlüsselfertige Bibliotheken, mit denen Sie eine voll funktionsfähige Tabellenkalkulation in Ihre Web-App einfügen können. SpreadJS ist eine davon. Wir werden uns ansehen, wie man eine vorhandene Vue-App – eine echte App, die einen Vuex-Store verwendet – mit SpreadJS erweitert.
Der Rest des Artikels setzt voraus, dass Sie bereits HTML, CSS und JavaScript verstehen. Außerdem wird vorausgesetzt, dass Sie über Kenntnisse des progressiven JavaScript-Frameworks Vue.js zum Erstellen von Web-UIs verfügen. Es ist hilfreich, wenn Sie Vuex für die Statusverwaltung verwendet haben, aber keine Sorge, wenn nicht. Es ist intuitiv und leicht zu verstehen, und wenn Sie Vue verwendet haben, können Sie einfach durch Lesen des Codes herausfinden, was passiert.
In diesem Blog beschreiben wir, wie Sie mit den folgenden Schritten Excel-Import und -Export zu Ihrer Vue-App hinzufügen:
Die Vue-App, an der wir arbeiten werden, ist ein einfaches Verkaufs-Dashboard mit einigen Übersichtsfeldern und einer Datentabelle. Es ist die Art von App, die in die Kategorie „nicht schlecht“ fällt:
Obwohl es sich nur um eine Demo handelt, handelt es sich genau um die Art von App, die Unternehmens-Webentwickler erstellen müssen. Es ist auch genau die Art von App, zu der wir normalerweise Excel-Funktionen hinzufügen sollen, sodass sie ein perfektes Beispiel für den Rest dieses Artikels ist.
Den Code für diese Anwendung finden Sie hier .
Wenn Sie sehen möchten, wie Sie eine Vue-Anwendung von Grund auf erstellen, sehen Sie sich diese Kurzanleitung an.
Um die Bühne zu bereiten, ist das Dashboard eine mit Vue erstellte Einzelseitenanwendung. Es verwendet die neuesten und besten Vue-Best Practices : Einzeldateikomponenten und einen Vuex-Datenspeicher. Es verwendet auch Bootstrap für seine CSS-Komponenten und sein Rastersystem.
Bootstrap ist nicht mehr so beliebt wie früher, aber in Wirklichkeit ist Bootstrap immer noch allgegenwärtig – insbesondere in Enterprise-Web-Apps, bei denen normalerweise Excel-Unterstützung benötigt wird. Wir würden wetten, dass viele neue Enterprise-Web-Apps im Jahr 2030 immer noch Bootstrap verwenden werden.
Wenn Sie in Ihren Tabellenkalkulations-Apps lieber Bulma oder Tachyons verwenden möchten, dann nur zu! SpreadJS funktioniert mit beiden problemlos.
Schauen wir uns an, wie der Code strukturiert ist. Unser Vuex-Store und unsere Vue-Anwendung sind beide in main.js definiert. Wir haben mehrere Vue-Komponenten in Einzeldateien, die sich alle im Ordner „components“ befinden.
Wenn Sie sich unseren Vuex-Store ansehen, werden Sie Folgendes sehen:
const store = new Vuex.Store({ state: { recentSales } mutations: { UPDATE_RECENT_SALES(state) { state.recentSales.push([]); state.recentSales.pop(); } } });
Der Anfangszustand unseres Shops ist auf den Wert von recentSales eingestellt, einem Satz von Dummy-Daten, die wir importiert haben. Wir haben auch eine Funktion, die die Aktualisierung der letzten Verkäufe übernimmt, wenn sie geändert werden.
Moment mal. Wenn wir nur einen Datensatz haben, wie generieren wir dann drei Diagramme und eine Tabelle? Um zu sehen, was passiert, öffnen Sie die Komponente Dashboard.vue . Darin sehen Sie, dass mehrere berechnete Eigenschaften basierend auf den Daten im Vuex-Speicher generiert werden:
<template> <div style="background-color: #ddd"> <NavBar title="Awesome Dashboard"/> <div class="container"> <div class="row"> <TotalSales :total="totalSales"/> <SalesByCountry :salesData="countrySales"/> <SalesByPerson :salesData="personSales"/> <SalesTable :tableData="salesTableData"/> </div> </div> </div> </template> <script> import NavBar from "./NavBar"; import TotalSales from "./TotalSales"; import SalesByCountry from "./SalesByCountry"; import SalesByPerson from "./SalesByPerson"; import SalesTable from "./SalesTable"; import { groupBySum } from "../util/util"; export default { components: { NavBar, SalesByCountry, SalesByPerson, SalesTable, TotalSales }, computed: { totalSales() { const items = this.$store.state.recentSales; const total = items.reduce((acc, sale) => (acc += sale.value), 0); return parseInt(total); }, chartData() { const items = this.$store.state.recentSales; const groups = groupBySum(items, "country", "value"); return groups; }, personSales() { const items = this.$store.state.recentSales; const groups = groupBySum(items, "soldBy", "value"); return groups; }, salesTableData() { return this.$store.state.recentSales; }, } }; </script>
Jetzt macht es mehr Sinn! Der einzelne Datensatz enthält alles, was wir brauchen, um alle Zahlen und Tabellen für unser Dashboard zu generieren. Da sich die Daten in einem reaktiven Vuex-Speicher befinden, werden alle Dashboard-Panels automatisch aktualisiert, wenn die Daten aktualisiert werden.
Diese Reaktionsfähigkeit wird im nächsten Abschnitt nützlich sein, wenn wir unsere langweilige alte statische Tabelle durch eine bearbeitbare Kalkulationstabelle ersetzen.
Und jetzt geht der Spaß los! Wir haben unser Dashboard, aber wir wollen die klobige alte HTML-Tabelle loswerden. Also müssen wir ein paar Dinge ein wenig ändern. Wir haben einen tollen Ausgangspunkt, aber wir müssen unsere App lokal ausführen, um SpreadJS im Entwicklungsmodus ohne Lizenz verwenden zu können.
Sie können den fertigen Code herunterladen, um das Endergebnis zu sehen.
Öffnen Sie zunächst das Originalprojekt ohne SpreadJS. Öffnen Sie ein Terminal, navigieren Sie zu dem Verzeichnis, in dem Sie das Repository geklont haben, und führen Sie „npm install“ aus. Dadurch werden die Abhängigkeiten installiert, die zum Ausführen der Anwendung erforderlich sind. Wenn die Installation der Abhängigkeiten abgeschlossen ist, führen Sie „npm serve“ aus, um die aktualisierte App in Aktion zu sehen. Wenn Sie die verwendeten Bibliotheken gezielt importieren möchten, können Sie diesen Befehl verwenden:
npm install @mescius/spread-sheets @mescius/spread-sheets-vue @mescius/spread-excelio file-saver bootstrap
Lassen Sie uns die Änderungen durchgehen, die wir vornehmen müssen, um unsere alte App auf die neue und verbesserte Version zu aktualisieren. Da wir unsere Verkaufstabelle durch eine Kalkulationstabelle ersetzen werden, werden wir die Tabelle in unsere vorhandene SalesTable.vue- Komponente einfügen, aber zuerst müssen wir unsere alte Tabelle entfernen. Sobald sie weg ist, sieht unsere SalesTable-Vorlage folgendermaßen aus:
<template> <TablePanel title="Recent Sales"> </TablePanel> </template>
Nachdem wir die Tabelle entfernt haben, ist unser Tabellenfeld fertig und wartet auf eine Kalkulationstabelle. Fügen wir also eine hinzu! Nachdem wir ein SpreadJS-Blatt hinzugefügt haben, sieht unsere Vorlage folgendermaßen aus:
<template> <TablePanel title="Recent Sales"> <gc-spread-sheets :hostClass='hostClass' @workbookInitialized='workbookInit'> <gc-worksheet :dataSource='tableData' :autoGenerateColumns='autoGenerateColumns'> <gc-column :width='50' :dataField="'id'" :headerText="'ID'" :visible = 'visible' :resizable = 'resizable' > </gc-column> <gc-column :width='300' :dataField="'client'" :headerText="'Client'" :visible = 'visible' :resizable = 'resizable' > </gc-column> <gc-column :width="350" :headerText="'Description'" :dataField="'description'" :visible = 'visible' :resizable = 'resizable' > </gc-column> <gc-column :width="100" :dataField="'value'" :headerText="'Value'" :visible = 'visible' :formatter = 'priceFormatter' :resizable = 'resizable' > </gc-column> <gc-column :width="100" :dataField="'itemCount'" :headerText="'Quantity'" :visible = 'visible' :resizable = 'resizable' > </gc-column> <gc-column :width="100" :dataField="'soldBy'" :headerText="'Sold By'" :visible = 'visible' :resizable = 'resizable' ></gc-column> <gc-column :width="100" :dataField="'country'" :headerText="'Country'" :visible = 'visible' :resizable = 'resizable' ></gc-column> </gc-worksheet> </gc-spread-sheets> </TablePanel> </template>
Das ist eine Menge, die man verarbeiten muss. Gehen wir es also durch, um zu verstehen, was passiert.
Zuerst erstellen wir eine Tabelle, indem wir das Element „gc-spread-sheets“ verwenden und es an zwei Eigenschaften unserer Komponente binden: „hostClass“ und „workbookInit“.
Innerhalb der Tabelle erstellen wir ein neues Arbeitsblatt mit dem Element gc-worksheet und binden es an die Eigenschaften tableData und autoGenerateColumns unserer Komponente. Beachten Sie, dass tableData genau dasselbe tableData ist, das wir zum Generieren unserer einfachen HTML-Tabelle verwendet haben. Wir können unsere Daten so wie sie sind in SpreadJS einfügen, ohne dass Änderungen erforderlich sind!
Schließlich definieren wir im Arbeitsblatt Spalten, die SpreadJS mitteilen, wie unsere Daten angezeigt werden sollen. Die Eigenschaft dataField gibt an, welche Eigenschaft des zugrunde liegenden Datensatzes diese Spalte anzeigen soll, und headerText gibt SpreadJS einen gut formatierten Spaltennamen. Die restlichen Bindungen für jede Spalte sind unkompliziert. Die SpreadJS-Dokumentation enthält eine vollständige Liste aller Elemente, die Sie an eine gc-Spalte übergeben können.
Wie viel Code wird also benötigt, damit das Ganze funktioniert, wenn unsere Vorlage vorhanden ist? Glücklicherweise gar nicht viel! Hier ist der neue Skriptcode unserer SalesTable.vue -Komponente:
import "@mescius/spread-sheets/styles/gc.spread.sheets.excel2016colorful.css"; // SpreadJS imports import GC from "@mescius/spread-sheets"; import "@mescius/spread-sheets-vue"; import Excel from "@mescius/spread-excelio"; import TablePanel from "./TablePanel"; export default { components: { TablePanel }, props: ["tableData"], data(){ return { sheetName: 'Sales Data', hostClass:'spreadsheet', autoGenerateColumns:true, width:200, visible:true, resizable:true, priceFormatter:"$ #.00" } }, methods: { workbookInit: function(_spread_) { this._spread = spread; var self = this; spread.bind(GC.Spread.Sheets.Events.ValueChanged, function () { const store = self.$store; var sheet = self._spread.getSheetFromName("Sales Data"); var newSalesData = sheet.getDataSource(); store.commit('UPDATE_RECENT_SALES', newSalesData); }); } } };
Aufgrund der Einfachheit von Vue ist nur sehr wenig Code erforderlich, damit dies funktioniert. Wenn Sie hier mit etwas nicht vertraut sind, werden die Vue-Komponenten im Abschnitt „Detaillierte Komponenten“ der Vue-Dokumentation ausführlich erläutert. Die einzigen Dinge, die sich geändert haben, sind einige Importe, einige Dateneigenschaften und ein paar Methoden. Die Dateneigenschaften sollten Ihnen bekannt vorkommen; wir haben sie gerade in der Vorlage gesehen. Es sind Konfigurationsoptionen, die wir an die Komponenten in unserer SpreadJS-Tabelle binden.
Die Methode workbookInit ist ein Rückruf, den SpreadJS aufruft, wenn das Blatt initialisiert wird. In dieser Methode speichern wir unser SheetJS-Tabellenobjekt als Instanzvariable auf unserer Komponente, damit wir bei Bedarf direkt damit interagieren können. Wir haben auch eine Bindungsfunktion für das ValueChanged-Ereignis hinzugefügt, um Daten automatisch zu aktualisieren, wenn Werte in der SpreadJS-Instanz geändert werden.
Eine letzte Änderung: Wir geben unserer Komponente einen bereichsbezogenen Stil, um den Tabellenkalkulationsstil selbst zu unterstützen. Wir haben dies bereits gesehen, als wir die HostClass an das Element gc-spread-sheets übergeben haben. Da die HostClass auf „spreadsheet“ eingestellt ist, erstellen wir eine CSS-Klasse mit dem Namen „spreadsheet“:
<style scoped> .spreadsheet { width: 100%; height: 400px; border: 1px solid lightgray; } </style>
Wenn wir an dieser Stelle keine weiteren Änderungen vornehmen und unser Dashboard laden, sieht es folgendermaßen aus:
Aber warten Sie, es gibt noch mehr!
Erinnern Sie sich, wie wir unsere Tabellendaten an die Kalkulationstabelle übergeben haben, ohne Änderungen am Datensatz vorzunehmen? Jetzt, da unsere Daten in einer Kalkulationstabelle vorliegen, können wir sie bearbeiten.
Was passiert, wenn wir den Wert von Verkauf Nr. 6 von 35.000 $ auf 3.500 $ ändern? Wenn wir in das Blatt gehen und den Wert bearbeiten, erhalten wir ein Dashboard, das folgendermaßen aussieht:
Wow! Was ist passiert?
Wir haben das SpreadJS-Blatt aktualisiert und unser Vuex-Store wurde automatisch aktualisiert.
Außerdem sieht es so aus, als ob Angelas Umsatz von einem spektakulären auf einen mittelmäßigen Monat abgerutscht ist. Das tut mir leid, Angela!
Wir haben jetzt ein verbessertes Dashboard, mit dem sich Manager freuen würden. Sie können die Daten ändern und zusehen, wie sich das Dashboard vor ihren Augen aktualisiert. Wir können es jedoch noch besser machen, indem wir die Möglichkeit zum Importieren und Exportieren von Excel-Dateien hinzufügen. Als Nächstes erfahren Sie, wie das geht.
Das Hinzufügen eines Excel-Exports zu unserem Blatt ist einfach. Fügen wir zunächst unserem Dashboard eine Exportschaltfläche hinzu. Wir platzieren sie unten in unserem Tabellenbereich in der Datei SalesTable.vue , direkt nach dem schließenden Tag gc-spread-sheets:
… </gc-spread-sheets> <div class="dashboardRow"> <button class="btn btn-primary dashboardButton" @click="exportSheet"> Export to Excel </button> </div> </TablePanel> </template>
Wie Sie sehen, erwartet unsere Schaltfläche einen Klick-Handler namens exportSheet. Wir werden ihn gleich hinzufügen, aber zuerst importieren wir eine Funktion aus einem NPM-Paket namens file-saver:
import { saveAs } from 'file-saver';
Als nächstes fügen wir exportSheet zum Methodenobjekt unserer Komponente hinzu:
exportSheet: function() { const spread = this._spread; const fileName = "SalesData.xlsx"; //const sheet = spread.getSheet(0); const excelIO = new IO(); const json = JSON.stringify(spread.toJSON({ includeBindingSource: true, columnHeadersAsFrozenRows: true, })); excelIO.save(json, (blob) => { saveAs(blob, fileName); }, function (e) { console.log(e) }); }
Der Code macht Folgendes: Zuerst erhalten wir einen Verweis auf unser Verkaufsdatenblatt. Da es das einzige Blatt in unserer Tabelle ist, befindet es sich am Index 0 und wir greifen darauf zu, indem wir getSheet aufrufen. Dies kann an anderer Stelle in der Funktion verwendet werden, falls wir direkt mit dem Blatt interagieren müssen.
Anschließend instanziieren wir die ExcelIO-Bibliothek von SpreadJS, konvertieren unser Tabellenblatt in JSON und bitten SpreadJS, es zu speichern. Voilà! Wir haben eine Excel-Datei aus unserer Vue-App mit Tabellenkalkulationsfunktion exportiert!
Beachten Sie, dass wir dem toJSON-Aufruf des Blatts zwei Serialisierungsoptionen übergeben: includeBindingSource und columnHeadersAsFrozenRows. Zusammen stellen diese Optionen sicher, dass die an das Blatt gebundenen Daten korrekt exportiert werden und dass das Blatt unsere Spaltenüberschriften enthält. Wenn Sie sich also die exportierte Excel-Datei ansehen, werden Sie jede Spalte verstehen.
Als Nächstes ist es an der Zeit, die Möglichkeit zum Importieren von Excel-Dateien hinzuzufügen.
Direkt unter unserer Exportschaltfläche fügen wir den folgenden Markup-Eintrag hinzu:
<div> <b>Import Excel File:</b> <div> <input type="file" class="fileSelect" @change='fileChange($event)' /> </div> </div>
Wie Sie sehen, verwenden wir einen Standard-HTML-Dateiwähler und lösen eine Komponentenmethode namens „fileChange“ aus, wenn eine Datei ausgewählt wird.
Nachdem wir nun die Vorlage hinzugefügt haben, fügen wir den Änderungshandler zum Methodenobjekt unserer Komponente hinzu:
fileChange: function (_e_) { if (this._spread) { const fileDom = e.target || e.srcElement; const excelIO = new Excel.IO(); const spread = this._spread; const store = this.$store; /*const deserializationOptions = { includeBindingSource: true, frozenRowsAsColumnHeaders: true };*/ excelIO.open(fileDom.files[0], (_data_) => { // Used for simply loading the JSON from a file //spread.fromJSON(data, deserializationOptions); var newSalesData = extractSheetData(data); store.commit('IMPORT_RECENT_SALES', newSalesData) }); } }
Das Importieren einer Excel-Datei ist fast dasselbe wie das Exportieren, nur umgekehrt. Nachdem wir eine Datei ausgewählt haben, bitten wir ExcelIO, sie zu importieren. Wenn dies erledigt ist, übergibt es die Blattinformationen als JavaScript-Objekt an eine Rückruffunktion. Als Nächstes leiten wir die importierten Daten durch eine benutzerdefinierte Funktion, um die benötigten Daten daraus zu extrahieren und sie dann wieder in den Vuex-Speicher zu übertragen.
Normalerweise ist das Importieren einer Datei so einfach wie das Aufrufen der ExcelIO-Öffnen-Methode, aber unter Verwendung der Arbeitsmappenmethode „fromJSON“. In diesem Fall möchten wir nur die Daten aus der importierten Datei analysieren und den Store aktualisieren, der dann SpreadJS aktualisiert.
In unserer Funktion „extractSheetData“, die Sie in der Datei src/util.util.js finden, sehen Sie, dass wir Daten aus dem von ExcelIO zurückgegebenen JavaScript-Objekt extrahieren und sie so umstrukturieren, dass sie der Form der Daten in unserem Vuex-Speicher entsprechen.
Unsere Importfunktion geht davon aus, dass die Daten im importierten Blatt dieselben Spalten wie unser ursprünglicher Datensatz haben. Wenn jemand eine Tabelle hochlädt, die diese Anforderung nicht erfüllt, kann unsere App sie nicht verarbeiten. Dies ist eine akzeptable Einschränkung in den meisten Branchen-Apps. Da unser Dashboard für die Anzeige eines bestimmten Datentyps konzipiert ist, ist es sinnvoll, Benutzer aufzufordern, Daten in dem von der App erwarteten Format bereitzustellen.
Wenn die Datenextraktion abgeschlossen ist, rufen wir im Vuex-Store ein Commit auf und senden die aktualisierten Verkaufstransaktionsdaten. Das SpreadJS-Blatt und die Dashboard-Panels aktualisieren sich dann selbst, um die neuen Daten widerzuspiegeln. Wir können tatsächlich eine andere Mutationsfunktion zum Importieren als zum Ändern eines Werts verwenden, sodass wir dies der Datei main.js als „IMPORT_RECENT_SALES“ hinzufügen können:
const store = new Vuex.Store({ state: { recentSales }, mutations: { UPDATE_RECENT_SALES(state) { state.recentSales.push([]); state.recentSales.pop(); }, IMPORT_RECENT_SALES(state, sales) { state.recentSales = sales; } } });
Nachdem Sie den Code gesehen haben, testen wir den Excel-Import und -Export in unserer Vue-App.
Klicken Sie zunächst auf die Schaltfläche „Nach Excel exportieren“. Ihr Webbrowser lädt dann eine Excel-Tabelle herunter, die alle Daten enthält, die wir in der Tabelle unseres Dashboards gesehen haben.
Öffnen Sie das Blatt in Excel und fügen Sie ein paar Datenzeilen hinzu. Es ist in Ordnung, wenn Sie neue Länder oder neue Verkäufer verwenden; alle unsere Dashboard-Komponenten können damit umgehen. Achten Sie nur darauf, die Spaltenreihenfolge oder -namen nicht zu ändern. Wenn Sie fertig sind, klicken Sie unten im Bereich „Letzte Verkäufe“ auf die Schaltfläche „Datei auswählen“. Wählen Sie die Excel-Datei aus, die Sie gerade bearbeitet haben.
Wenn Sie die Datei auswählen, werden die aktualisierten Dashboard-Komponenten angezeigt.
Wir sind fertig! Wir haben eine normale Vue-Dashboard-App genommen und ihr eine Live-Tabelle hinzugefügt. Wir können jetzt die Daten in der Tabelle bearbeiten und zusehen, wie sich unser gesamtes Dashboard selbst aktualisiert. Unser erweitertes Dashboard kann auch Excel-Dateien importieren und exportieren.
Vue, Vuex und SpreadJS ergänzen sich gut. Mit Vues einfacher Vorlagenerstellung und Datenbindung, Vuexs reaktivem Datenspeicher und SpreadJS‘ interaktiven Tabellenkalkulationen können komplexe JavaScript-Unternehmensanwendungen in wenigen Stunden erstellt werden.
So toll das auch klingen mag, wir haben gerade erst an der Oberfläche dessen gekratzt, was SpreadJS alles kann. Um besser zu verstehen, was SpreadJS für Sie tun kann, sehen Sie sich die SpreadJS-Demos an, die vollständige Demos der verschiedenen Funktionen von SpreadJS, Erklärungen und Live-Code enthalten, der diese Funktionen vorführt. Wenn Sie tiefer in die Verwendung von SpreadJS in Ihren eigenen Apps eintauchen möchten, finden Sie in der SpreadJS-Dokumentation die Informationen, die Sie benötigen.
Erfahren Sie mehr über diese JavaScript-Tabellenkalkulationskomponente: