json-to-html-table-with-lit
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseJSON to HTML Table with Lit
使用Lit实现JSON转HTML表格
Prerequisites
前提条件
- Vscode
- Node >= 16
- Typescript
- Vscode
- Node >= 16
- Typescript
Getting Started
开始上手
We can start off by navigating in terminal to the location of the project and run the following:
npm init @vitejs/app --template lit-tsThen enter a project name and now open the project in vscode and install the dependencies:
lit-html-tablecd lit-html-table
npm i lit
npm i -D @types/node
code .Update the with the following:
vite.config.tsimport { defineConfig } from "vite";
import { resolve } from "path";
export default defineConfig({
base: "/lit-html-table/",
build: {
lib: {
entry: "src/lit-html-table.ts",
formats: ["es"],
},
rollupOptions: {
input: {
main: resolve(__dirname, "index.html"),
},
},
},
});我们可以先在终端中导航到项目所在位置,然后运行以下命令:
npm init @vitejs/app --template lit-ts然后输入项目名称,接着在Vscode中打开项目并安装依赖:
lit-html-tablecd lit-html-table
npm i lit
npm i -D @types/node
code .更新文件内容如下:
vite.config.tsimport { defineConfig } from "vite";
import { resolve } from "path";
export default defineConfig({
base: "/lit-html-table/",
build: {
lib: {
entry: "src/lit-html-table.ts",
formats: ["es"],
},
rollupOptions: {
input: {
main: resolve(__dirname, "index.html"),
},
},
},
});Template
模板文件
Open up the and update it with the following:
index.html<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/src/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>JSON to Lit HTML Table</title>
<script type="module" src="/src/lit-html-table.ts"></script>
</head>
<body>
<lit-html-table src="https://jsonplaceholder.typicode.com/posts">
<!-- <span slot="title" style="color: red;">Title</span> -->
<!-- <script type="application/json">
[
{
"id": "0",
"name": "First Item"
}
]
</script> -->
</lit-html-table>
</body>
</html>We are passing a src attribute to the web component for this example but we can also add a script tag with the type attribute set to with the contents containing the json.
application/jsonIf any table header cell needed to be replaced an element can be provided with the slot name set to the key in the json object.
打开并更新内容如下:
index.html<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/src/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>JSON to Lit HTML Table</title>
<script type="module" src="/src/lit-html-table.ts"></script>
</head>
<body>
<lit-html-table src="https://jsonplaceholder.typicode.com/posts">
<!-- <span slot="title" style="color: red;">Title</span> -->
<!-- <script type="application/json">
[
{
"id": "0",
"name": "First Item"
}
]
</script> -->
</lit-html-table>
</body>
</html>在这个示例中,我们给Web组件传递了一个src属性,但也可以添加一个type为的script标签,并在其中写入JSON内容。
application/json如果需要替换某个表头单元格,可以提供一个元素,并将其slot属性设置为JSON对象中的对应键名。
Web Component
Web组件
Before we update our component we need to rename to
my-element.tslit-html-table.tsOpen up and update it with the following:
lit-html-table.tsimport { html, css, LitElement } from "lit";
import { customElement, property } from "lit/decorators.js";
type ObjectData = { [key: string]: any };
@customElement("lit-html-table")
export class LitHtmlTable extends LitElement {
@property() src = "";
data?: ObjectData[];
static styles = css`
tr {
text-align: var(--table-tr-text-align, left);
vertical-align: var(--table-tr-vertical-align, top);
padding: var(--table-tr-padding, 10px);
}
`;
render() {
// Check if data is loaded
if (!this.values) {
return html`<slot name="loading">Loading...</slot>`;
}
// Check if items are not empty
if (this.values.length === 0) {
return html`<slot name="empty">No Items Found!</slot>`;
}
// Convert JSON to HTML Table
return html`
<table>
<thead>
<tr>
${Object.keys(this.values[0]).map((key) => {
const name = key.replace(/\b([a-z])/g, (_, val) =>
val.toUpperCase()
);
return html`<th>
<slot name="${key}">${name}</slot>
</th>`;
})}
</tr>
</thead>
<tbody>
${this.values.map((item) => {
return html`
<tr>
${Object.values(item).map((row) => {
return html`<td>${row}</td>`;
})}
</tr>
`;
})}
</tbody>
</table>
`;
}
async firstUpdated() {
await this.fetchData();
}
// Download the latest json and update it locally
async fetchData() {
let _data: any;
if (this.src.length > 0) {
// If a src attribute is set prefer it over any slots
_data = await fetch(this.src).then((res) => res.json());
} else {
// If no src attribute is set then grab the inline json in the slot
const elem = this.parentElement?.querySelector(
'script[type="application/json"]'
) as HTMLScriptElement;
if (elem) _data = JSON.parse(elem.innerHTML);
}
this.values = this.transform(_data ?? []);
this.requestUpdate();
}
transform(data: any) {
return data;
}
}We have defined a few CSS Custom Properties to style the table cell but many more can be added here.
If everything goes well run the command and the follow should appear:
npm run dev在更新组件之前,我们需要将重命名为
my-element.tslit-html-table.ts打开并更新内容如下:
lit-html-table.tsimport { html, css, LitElement } from "lit";
import { customElement, property } from "lit/decorators.js";
type ObjectData = { [key: string]: any };
@customElement("lit-html-table")
export class LitHtmlTable extends LitElement {
@property() src = "";
data?: ObjectData[];
static styles = css`
tr {
text-align: var(--table-tr-text-align, left);
vertical-align: var(--table-tr-vertical-align, top);
padding: var(--table-tr-padding, 10px);
}
`;
render() {
// Check if data is loaded
if (!this.values) {
return html`<slot name="loading">Loading...</slot>`;
}
// Check if items are not empty
if (this.values.length === 0) {
return html`<slot name="empty">No Items Found!</slot>`;
}
// Convert JSON to HTML Table
return html`
<table>
<thead>
<tr>
${Object.keys(this.values[0]).map((key) => {
const name = key.replace(/\b([a-z])/g, (_, val) =>
val.toUpperCase()
);
return html`<th>
<slot name="${key}">${name}</slot>
</th>`;
})}
</tr>
</thead>
<tbody>
${this.values.map((item) => {
return html`
<tr>
${Object.values(item).map((row) => {
return html`<td>${row}</td>`;
})}
</tr>
`;
})}
</tbody>
</table>
`;
}
async firstUpdated() {
await this.fetchData();
}
// Download the latest json and update it locally
async fetchData() {
let _data: any;
if (this.src.length > 0) {
// If a src attribute is set prefer it over any slots
_data = await fetch(this.src).then((res) => res.json());
} else {
// If no src attribute is set then grab the inline json in the slot
const elem = this.parentElement?.querySelector(
'script[type="application/json"]'
) as HTMLScriptElement;
if (elem) _data = JSON.parse(elem.innerHTML);
}
this.values = this.transform(_data ?? []);
this.requestUpdate();
}
transform(data: any) {
return data;
}
}我们定义了一些CSS自定义属性来设置表格单元格样式,还可以在此基础上添加更多样式。
如果一切顺利,运行命令后,页面将显示如下内容:
npm run devEditing
编辑功能
What if we wanted to support editing of any cell? With Lit and Web Components we can progressively enhance the experience without changing the html.
At the top of the class add the following boolean property:
@property({ type: Boolean }) editable = false;Now update the tag in the render method:
tbody<tbody>
${this.values.map((item, index) => {
return html`
<tr>
${Object.entries(item).map((row) => {
return html`<td>
${this.editable
? html`<input
value="${row[1]}"
type="text"
@input=${(e: any) => {
const value = e.target.value;
const key = row[0];
const current = this.values![index];
current[key] = value;
this.values![index] = current;
this.requestUpdate();
this.dispatchEvent(
new CustomEvent("input-cell", {
detail: {
index: index,
data: current,
},
})
);
}}
/>`
: html`${row[1]}`}
</td>`;
})}
</tr>
`;
})}
</tbody>By checking to see if the and if return an input with an event listener to update the data and dispatch an event.
editabletrueinputAdd the attribute to the :
editableindex.html<lit-html-table editable> ... </lit-html-table>After a reload the table should look like this and any cell can be edited.
An event listener can be added just before the closing tag in to grab the latest values or cell information:
bodyindex.html<script>
const elem = document.querySelector("lit-html-table");
elem.addEventListener(
"input-cell",
(e) => {
// Index and data for the individual cell
const { index, data } = e.detail;
// New array of json items
const values = elem.values;
},
false
);
</script>This can be taken farther by checking for the type of the value and returning a color, number or checkbox input.
如果我们想支持单元格编辑怎么办?借助Lit和Web组件,我们可以在不修改HTML结构的前提下逐步增强交互体验。
在类的顶部添加以下布尔类型属性:
@property({ type: Boolean }) editable = false;然后更新render方法中的标签:
tbody<tbody>
${this.values.map((item, index) => {
return html`
<tr>
${Object.entries(item).map((row) => {
return html`<td>
${this.editable
? html`<input
value="${row[1]}"
type="text"
@input=${(e: any) => {
const value = e.target.value;
const key = row[0];
const current = this.values![index];
current[key] = value;
this.values![index] = current;
this.requestUpdate();
this.dispatchEvent(
new CustomEvent("input-cell", {
detail: {
index: index,
data: current,
},
})
);
}}
/>`
: html`${row[1]}`}
</td>`;
})}
</tr>
`;
})}
</tbody>通过检查属性,如果为则返回一个输入框,并添加事件监听器来更新数据并派发事件。
editabletrueinput在中给组件添加属性:
index.htmleditable<lit-html-table editable> ... </lit-html-table>重新加载页面后,表格将显示为可编辑状态,任意单元格都能修改。
可以在的闭合标签前添加事件监听器,以获取最新的值或单元格信息:
index.htmlbody<script>
const elem = document.querySelector("lit-html-table");
elem.addEventListener(
"input-cell",
(e) => {
// Index and data for the individual cell
const { index, data } = e.detail;
// New array of json items
const values = elem.values;
},
false
);
</script>还可以进一步优化,根据值的类型返回颜色选择器、数字输入框或复选框。