6.2 KiB
sidebar_position | title |
---|---|
20 | Content and Site Generation |
With the advent of server-side frameworks and content management systems, it is possible to build sites whose source of truth is a spreadsheet! This demo explores a number of approaches.
GatsbyJS
gatsby-transformer-excel
generates nodes for each data row of each worksheet. The official documentation
includes examples and more detailed usage instructions.
:::note
gatsby-transformer-excel
is maintained by the Gatsby core team and all bugs
should be directed to the main Gatsby project. If it is determined to be a bug
in the parsing logic, issues should then be raised with the SheetJS project.
:::
NuxtJS
@nuxt/content
is a file-based CMS for Nuxt, enabling static-site generation
and on-demand server rendering powered by spreadsheets.
nuxt.config.js configuration
Through an override in nuxt.config.js
, Nuxt Content will use custom parsers.
Differences from a stock create-nuxt-app
config are shown below:
// highlight-start
import { readFile, utils } from 'xlsx';
// This will be called when the files change
const parseSheet = (file, { path }) => {
// `path` is a path that can be read with `XLSX.readFile`
const wb = readFile(path);
const o = wb.SheetNames.map(name => ({ name, data: utils.sheet_to_json(wb.Sheets[name])}));
return { data: o };
}
// highlight-end
export default {
// ...
// highlight-start
// content.extendParser allows us to hook into the parsing step
content: {
extendParser: {
// the keys are the extensions that will be matched. The "." is required
".numbers": parseSheet,
".xlsx": parseSheet,
".xls": parseSheet,
// can add other extensions like ".fods" as desired
}
},
// highlight-end
// ...
}
Template Use
When a spreadsheet is placed in the content
folder, Nuxt will find it. The
data can be referenced in a view with asyncData
. The name should not include
the extension, so "sheetjs.numbers"
would be referenced as "sheetjs"
:
async asyncData ({$content}) {
return {
// $content('sheetjs') will match files with extensions in nuxt.config.js
data: await $content('sheetjs').fetch()
};
}
In the template, data.data
is an array of objects. Each object has a name
property for the worksheet name and a data
array of row objects. This maps
neatly with nested v-for
:
<!-- loop over the worksheets -->
<div v-for="item in data.data" v-bind:key="item.name">
<table>
<!-- loop over the rows of each worksheet -->
<tr v-for="row in item.data" v-bind:key="row.Index">
<!-- here `row` is a row object generated from sheet_to_json -->
<td>{{ row.Name }}</td>
<td>{{ row.Index }}</td>
</tr>
</table>
</div>
Nuxt Content Demo
Complete Example (click to show)
:::note
This was tested against create-nuxt-app v4.0.0
on 2022 August 13.
:::
- Create a stock app:
npx create-nuxt-app SheetJSNuxt
When prompted, enter the following options:
Project name
: hit Enter (use default SheetJSNuxt)Programming language
: hit Down Arrow (TypeScript
selected) and hit EnterPackage manager
: selectNpm
and hit EnterUI framework
: selectNone
and hit EnterNuxt.js modules
: scroll toContent
, select with Space, then hit EnterLinting tools
: hit Enter (do not select any Linting tools)Testing framework
: selectNone
and hit EnterRendering mode
: selectUniversal (SSR / SSG)
and hit EnterDeployment target
: selectStatic (Static/Jamstack hosting)
and hit EnterDevelopment tools
: hit Enter (do not select any Development tools)What is your GitHub username?
: hit EnterVersion control system
: selectNone
The project will be configured and modules will be installed.
- Install the SheetJS library and start the dev server:
cd SheetJSNuxt
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
npm run dev
When the build finishes, the terminal will display a URL like:
ℹ Listening on: http://localhost:64688/ 05:41:11
No issues found. 05:41:11
The dev server is listening on that URL. Open the link in a web browser.
-
Download https://sheetjs.com/pres.xlsx and move to the
content
folder. -
Modify
nuxt.config.js
as described earlier -
Replace
pages/index.vue
with the following:
<!-- sheetjs (C) 2013-present SheetJS -- http://sheetjs.com -->
<template><div>
<div v-for="item in data.data" v-bind:key="item.name">
<h2>{{ item.name }}</h2>
<table><thead><tr><th>Name</th><th>Index</th></tr></thead><tbody>
<tr v-for="row in item.data" v-bind:key="row.Index">
<td>{{ row.Name }}</td>
<td>{{ row.Index }}</td>
</tr>
</tbody></table>
</div>
</div></template>
<script>
export default {
async asyncData ({$content}) {
return {
data: await $content('pres').fetch()
};
}
}
</script>
The browser should refresh to show the contents of the spreadsheet. If it does not, hit Refresh manually or open a new browser window.
- To verify that hot loading works, open
pres.xlsx
from thecontent
folder in Excel. Add a new row to the bottom and save the file:
The dev server terminal should show a line like:
ℹ Updated ./content/pres.xlsx @nuxt/content 05:43:37
The page should automatically refresh with the new content:
- Stop the dev server (hit
CTRL+C
in the terminal window) and run
npm run generate
This will create a static site in the dist
folder, which can be served with:
npx http-server dist
Accessing the page http://localhost:8080 will show the page contents. Verifying the static nature is trivial: make another change in Excel and save. The page will not change.