mcpField
J
json-to-html-table-with-lit

json-to-html-table-with-lit

github.com/rodydavis开发6047 次安装2 周前更新
代码仓库

关于 json-to-html-table-with-lit

面向 Lit Web 开发者的教程,介绍将 JSON URL 或内嵌 JSON 渲染为 HTML 表格,并添加单元格编辑、事件通知及自定义表头。

主要功能

  1. 01从 URL 或内嵌 JSON 生成表格
  2. 02编辑表格单元格
  3. 03通过事件获取单元格数据
  4. 04按 JSON 键替换表头单元格
  5. 05使用 CSS 自定义属性设置样式

适用场景

  1. 01在网页中展示 JSON 数据
  2. 02为 JSON 表格添加单元格编辑功能

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/rodydavis/skills --skill json-to-html-table-with-lit

安装后在 Claude Code 中输入/json-to-html-table-with-lit确认已加载。

详细介绍

使用 Lit 将 JSON 转换为 HTML 表格

本文将介绍如何设置 Lit Web 组件,并使用它根据 JSON URL 或内嵌 JSON 创建 HTML 表格。

简而言之 最终源码见此处,在线演示。

前提条件

  • Vscode
  • Node >= 16
  • Typescript

入门

首先,在终端中导航到项目所在位置,然后运行以下命令:

npm init @vitejs/app --template lit-ts

接着输入项目名称 lit-html-table,然后在 vscode 中打开项目并安装依赖项:

cd lit-html-table
npm i lit
npm i -D @types/node
code .

按以下内容更新 vite.config.ts:

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

模板

打开 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">
      
      
    </lit-html-table>
  </body>
</html>

在本示例中,我们向 Web 组件传递了一个 src 属性,但也可以添加一个 script 标签,将 type 属性设为 application/json,并在其中填写 JSON 内容。

如果需要替换表格标题单元格,可以提供一个元素,并将其 slot 名称设为 JSON 对象中的键。

Web 组件

更新组件之前,我们需要将 my-element.ts 重命名为 lit-html-table.ts。

打开 lit-html-table.ts 并按以下内容更新:

import { 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 dev,应该会看到以下内容:

README 配图

编辑

如果我们想支持编辑任意单元格,该怎么做?借助 Lit 和 Web Components,我们可以逐步增强体验,而无需更改 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>

检查 editable 是否为 true,如果是,则返回一个带有事件监听器的输入框,以更新数据并派发 input 事件。

将 editable 属性添加到 index.html:

<lit-html-table editable> ... </lit-html-table>

重新加载后,表格应如下所示,并且可以编辑任意单元格。

README 配图

可以在 index.html 的闭合 body 标签之前添加事件监听器,以获取最新值或单元格信息:

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

还可以进一步扩展此功能:检查值的类型,并返回颜色、数字或复选框输入框。

结论

如果你想进一步了解如何使用 Lit 构建,可以阅读这里的文档。Lit playground 上也有一个示例这里。 此示例的源代码可在此处找到。

评论

支持 Markdown