如何将一个静态的HTML表格转换为动态的JavaScript数据网格

841 阅读12分钟

HTML表格在渲染少量数据时使用起来很简单。但是,当它们显示大量数据时,用户就很难操作了。

像排序、过滤和分页这样的功能使得处理许多行的数据更加容易。我们可以通过从HTML表迁移到JavaScript数据网格组件来轻松实现这些功能。

在这篇文章中,我们将使用AG Grid JavaScript Data Grid的免费社区版,从一个长的静态HTML表转换为一个易于使用的交互式数据网格。我们需要的JavaScript数量是最少的,而且非常简单。

我们将分三步建立示例代码:

  • 在一个HTML表格中渲染一个静态的Todo项目数据列表。
  • 从REST API加载一个Todo项目列表并在表中呈现。
  • 将HTML表转换为数据网格,以允许排序、过滤和分页。

如何用HTML表渲染数据

我们应用程序的第一个版本将允许我们创建基本的页面结构,并确保我们为用户呈现正确的数据。

我创建了一个简单的index.html 文件,如下所示:

<!DOCTYPE html>
<html>

<head>
    <title>Table Example</title>
</head>

<body>

    <style>
        table {
            border-collapse: collapse;
            width: 100%;
        }

        td,
        th {
            border: 1px solid #000000;
            text-align: left;
            padding: 8px;
        }
    </style>

    <h1>TODO List</h1>

    <div id="data-table">
        <table id="html-data-table">
            <tr>
                <th>userId</th>
                <th>id</th>
                <th>title</th>
                <th>completed</th>
            </tr>
            <tr>
                <td>1</td>
                <td>1</td>
                <td>My todo 1</td>
                <td>false</td>
            </tr>
        </table>    
    </div>

</body>

</html>

这将在一个表格中呈现一个单一的Todo项目。

Single Todo Item Shown in an HTML Table

在HTML表格中显示的单个Todo项目

这里是静态HTML表格的例子:

table ,使用width:100% ,使其宽度为页面的100%,并使用border-collapse: collapse ,使表格中单元格之间的边框线显示为一条线。

如果没有border-collapse ,这个表格看起来就像下面的图片。

Table Styled without border-collapse

没有边框折叠的表格样式

简短HTML表格的好处

HTML表格是在页面上以表格形式呈现少量数据的一种非常快速的方式。

表格需要造型,因为table ,不同浏览器的默认造型各不相同,而且通常是在没有边框的情况下显示,使得数据难以阅读。

目前,我们的Todo项目列表被静态地编码到页面中。下一步,我们将使用JavaScript从REST API中fetch 列表。

如何从API中读取JSON并在HTML表格中呈现?

由于我们将从API加载数据,我不会在表中硬编码任何数据。为了支持动态加载,我简单地从table ,因为我将使用JavaScript创建数据行:

    <div id="data-table">
        <table id="html-data-table">
            <tr>
                <th>userId</th>
                <th>id</th>
                <th>title</th>
                <th>completed</th>
            </tr>
        </table>    
    </div>

我将在index.html 页面中,在终止的body 标签之前立即添加JavaScript。

    <script type="text/javascript" charset="utf-8">
    </script>
</body>

首先,我将编写读取数据的代码:

我将使用"{JSON}Placeholder "的REST API应用程序进行演示。GET 通过对网址jsonplaceholder.typicode.com/todos,我们将收到一个JSON响应,这是一个Todo项目的列表。

你可以通过点击上面的链接,在不使用JavaScript的情况下自己尝试一下。

对API提出GET 请求的最简单方法是使用JavaScript中的fetch 函数:

    <script type="text/javascript" charset="utf-8">

        fetch('https://jsonplaceholder.typicode.com/todos')
            .then(function (response) {
                return response.json();
            }).then(function (apiJsonData) {
                console.log(apiJsonData);
            })

    </script>
</body>

为了解释上述代码,我将在下面分节描述:

  • 发出一个GET请求到https://jsonplaceholder.typicode.com/todos
fetch('https://jsonplaceholder.typicode.com/todos')
  • 然后当请求完成后,将响应转换为一个JavaScript对象--在我们的例子中,这将是一个包含所有Todo项目的数组:
.then(function (response) {
	return response.json();
})
  • 然后把这个JavaScript对象写到控制台
.then(function (apiJsonData) {
	console.log(apiJsonData);
})

在我们的应用程序中使用这段代码,我们不会在表中看到任何东西,但我们会在浏览器开发工具控制台中看到数组的渲染,我们可以在那里查看数据:

Data shown when console.log used

使用console.log时显示的数据

API调用返回200个项目,每个项目是一个Todo对象:

  {
    "userId": 1,
    "id": 1,
    "title": "delectus aut autem",
    "completed": false
  }

我们的下一步是渲染表格中的数据:

    <script type="text/javascript" charset="utf-8">

        fetch('https://jsonplaceholder.typicode.com/todos')
            .then(function (response) {
                return response.json();
            }).then(function (apiJsonData) {
                console.log(apiJsonData);
                renderDataInTheTable(apiJsonData);
            })

        function renderDataInTheTable(todos) {
            const mytable = document.getElementById("html-data-table");
            todos.forEach(todo => {
                let newRow = document.createElement("tr");
                Object.values(todo).forEach((value) => {
                    let cell = document.createElement("td");
                    cell.innerText = value;
                    newRow.appendChild(cell);
                })
                mytable.appendChild(newRow);
            });
        }
    </script>
</body>

renderDataInTheTable 函数在DOM中找到表格,这样我们就可以向它追加新的行,然后在所有从API调用返回的Todo项目上循环。

对于每个Todo项目,代码会创建一个新的tr 元素,然后将Todo项目中的每个值作为一个td 元素添加到表中:

let newRow = document.createElement("tr");
Object.values(todo).forEach((value) => {
    let cell = document.createElement("td");
    cell.innerText = value;
    newRow.appendChild(cell);
})

fetchrenderDataInTheTable 代码被添加到我们的应用程序中,并加载页面时,我们将看到HTML表格中现在有所有200个Todo项目被呈现在表格中。

Dynamically loaded todo list

动态加载的todo列表

下面是动态HTML表页面的例子。

长HTML表的优点和缺点

HTML表是在页面上呈现数据的一种简单方法,但对于长的数据列表来说,却不太适用。

尽管用户可以使用浏览器内置的 "在页面中查找 "功能来搜索数据,但数据项可能很难找到。

通过在HTML表格中的渲染,我们的用户没有办法对数据进行排序,也没有办法对其进行过滤,只显示已完成的Todo项目。我们必须向我们的应用程序添加额外的代码来实现排序和过滤功能。

当更多的行被添加到表中时,HTML表会自动增长。这可能会使它们在添加了大量数据的应用程序中更难使用。

当我们添加大量数据时,我们可能希望有分页功能,以限制数据表只显示一定数量的行,并允许用户点击进入下一页以查看更多项目。这也是我们必须编写额外代码来处理的功能。

当我们的应用程序到了需要更多用户互动的时候,我们应该考虑使用数据网格组件。

我们可以用它来添加额外的功能,比如:

  • 排序
  • 过滤
  • 调整列的大小
  • 分页

数据网格组件和库

有许多免费的数据网格组件可用,但它们中的大多数都是特定的框架,因此它们需要使用React、Angular或Vue进行编码。

我在这个例子中使用AG网格,因为免费版本可以使用JavaScript、TypeScript、React、Angular或Vue。AG "代表不可知论,意味着可以与任何框架一起使用。

当你学会在一个框架中使用AG Grid时,同样的API也可用于其他框架,使你的知识可以转移到其他项目中。

AG Grid的免费版本可以用于商业应用,所以如果你设法将这里的演示应用扩展为商业的Todo List管理应用,你仍然可以免费使用AG Grid。许多商业应用都是使用AG Grid的免费版本建立的。

此外,AG Grid经常被作为工作申请中的一项技能,所以值得尝试。

AG Grid的商业版本有额外的功能,如Excel导出和创建图表,但我们在这个演示中不需要任何这些功能。

使用数据网格意味着我们配置数据网格,给它渲染数据,而网格则处理所有其他功能,如排序、过滤和分页。

我们可以将现有的代码转换为使用AG网格,只需做一些修改。

如何添加AG网格的JavaScript和CSS

AG Grid是一个库,所以我们将包括所需的JavaScript。

如果你使用的是构建工具,比如npm ,那么各种npm install 命令都列在AG Grid入门文档中。

我们使用的是普通的JavaScript,所以我们可以在我们的head 部分包括script

<head>
    <title>Data Grid Example</title>
    <script src="https://unpkg.com/ag-grid-community/dist/ag-grid-community.min.noStyle.js"></script>
    <link rel="stylesheet" href="https://unpkg.com/ag-grid-community/dist/styles/ag-grid.css">
    <link rel="stylesheet" href="https://unpkg.com/ag-grid-community/dist/styles/ag-theme-balham.css">
</head>

这包括AG Grid的社区版和正确呈现网格所需的CSS。

我们的data-table div 不再需要有任何table 元素:

    <div id="data-table" class="ag-theme-balham">
    </div>

AG Grid将在我们设置数据网格时为其创建HTML。我们添加class ,以使用AG Grid主题。在这个例子中,我们使用的主题是ag-theme-balham

AG Grid需要为div ,设置宽度和高度。我选择在代码中添加这个作为style 部分:

    <style>
        #data-table {
            height: 500px;
            width: 100%;
        }
    </style>

网格将显示为500像素高,并填满屏幕的宽度100% 。这复制了我们在HTML表格中的基本样式。但它也显示了使用数据网格的一个好处。呈现的表格的大小可以很容易地控制,并且滚动条将在必要时由网格本身自动添加。

如何配置AG网格和渲染数据

script 部分的代码发生了变化,因为我们需要:

  • 配置数据网格。
  • 使用配置创建一个新的数据网格。
  • 获取数据并将其添加到网格中。

我将在下面展示初步修正的script 部分,然后在下面的段落中解释:

    <script type="text/javascript" charset="utf-8">

        const columnDefs = [
            { field: 'userId' },
            { field: 'id' },
            { field: 'title' },
            { field: 'completed' },
        ];

        const gridOptions = {
            columnDefs: columnDefs,
            onGridReady: (event) =>{renderDataInTheTable(event.api)}
        };

        const eGridDiv = document.getElementById('data-table');
        new agGrid.Grid(eGridDiv, gridOptions);

        function renderDataInTheTable(api) {
            fetch('https://jsonplaceholder.typicode.com/todos')
                .then(function (response) {
                    return response.json();
                }).then(function (data) {
                    api.setRowData(data);
                    api.sizeColumnsToFit();
                })
        }
    </script>

一个数据网格是由数据和配置驱动的--我们不需要写太多的代码就可以创建一个功能性的数据网格。

首先,我们创建一个列对象的数组,定义数据网格中的列。这些列映射到数据上。

我们从API调用中收到的数据有四个属性。"userId"、"id"、"title "和 "completed":

  {
    "userId": 1,
    "id": 1,
    "title": "delectus aut autem",
    "completed": false
  }

为了在数据网格中呈现这些列,我们创建了一个带有field 属性的对象,其值是数据对象中的属性名称:

        const columnDefs = [
            { field: 'userId' },
            { field: 'id' },
            { field: 'title' },
            { field: 'completed' },
        ];

接下来我们创建gridOptions 对象。这就配置了数据网格:

        const gridOptions = {
            columnDefs: columnDefs,
            onGridReady: (event) =>{renderDataInTheTable(event.api)}
        };

columnDefs 属性被分配给我们先前定义的列对象阵列。

onGridReady 属性被分配了一个函数,当网格被创建并在DOM中呈现时(也就是说,当网格准备好时),它将调用renderDataInTheTable 函数。

为了将网格添加到页面中,我们找到将包含网格的div 元素,然后为该元素实例化一个新的AG网格对象,并使用我们配置的选项:

        const eGridDiv = document.getElementById('data-table');
        new agGrid.Grid(eGridDiv, gridOptions);

获取数据并在网格中呈现数据的函数与我们用于动态HTML表格的fetch 代码基本相同。不同的是,renderDataInTheTable 函数接收一个 AG Grid Api 对象作为参数,允许我们调用 AG Grid 功能来设置行数据和列的大小以适应网格:

        function renderDataInTheTable(api) {
            fetch('https://jsonplaceholder.typicode.com/todos')
                .then(function (response) {
                    return response.json();
                }).then(function (data) {
                    api.setRowData(data);
                    api.sizeColumnsToFit();
                })
        }

当这段代码运行时,我们将基本上复制了动态HTML表的相同功能,但现在所有的数据都显示在一个带有滚动条的数据网格中。

为了获得使用数据网格的好处,并允许用户对数据进行排序、过滤和导航,我们只需修改配置。

如何实现排序、过滤和分页

下面是我们到目前为止在数据网格中配置的内容。

  • 要显示数据中的哪些字段
  • 使用什么数据

为了添加排序、过滤、可调整大小的列和分页,我们修改了gridOptions 的配置:

        const gridOptions = {

            defaultColDef: {
                sortable: true,
                filter: 'agTextColumnFilter',
                resizable: true
            },

            pagination: true,

            columnDefs: columnDefs,
            onGridReady: (event) =>{renderDataInTheTable(event.api)}
        };

我们可以通过向columnDefs 对象添加额外的属性来单独配置AG网格中的列。或者如果所有的列都默认需要相同的功能,我们可以配置defaultColDef

这里我们将其配置为可排序、可过滤和可调整大小:

            defaultColDef: {
                sortable: true,
                filter: 'agTextColumnFilter',
                resizable: true
            },

我们为所有列定义的默认过滤器是文本过滤器。

为了给网格添加自动分页功能,我们添加了pagination: true 属性,AG网格将自动为我们分页数据。

Data Grid with Sorting, Filtering and Pagination

带有排序、过滤和分页的数据网格

用户友好的数据网格

通过上述代码,我们已经创建了一个用户友好的数据网格,它可以动态地获取数据并将其添加到支持排序、过滤和分页的数据网格中。

下面是数据网格HTML页面的例子:

<!DOCTYPE html>
<html>

<head>
    <title>Data Grid Example</title>
    <script src="https://unpkg.com/ag-grid-community/dist/ag-grid-community.min.noStyle.js"></script>
    <link rel="stylesheet" href="https://unpkg.com/ag-grid-community/dist/styles/ag-grid.css">
    <link rel="stylesheet" href="https://unpkg.com/ag-grid-community/dist/styles/ag-theme-balham.css">
</head>

<body>
    <style>
        #data-table {
            height: 500px;
            width: 100%;
        }
    </style>

    <h1>TODO List</h1>

    <div id="data-table" class="ag-theme-balham">
    </div>

    <script type="text/javascript" charset="utf-8">

        const columnDefs = [
            { field: 'userId' },
            { field: 'id' },
            { field: 'title' },
            { field: 'completed' },
        ];

        const gridOptions = {

            defaultColDef: {
                sortable: true,
                filter: 'agTextColumnFilter',
                resizable: true
            },

            pagination: true,
            
            columnDefs: columnDefs,
            onGridReady: (event) =>{renderDataInTheTable(event.api)}
        };

        const eGridDiv = document.getElementById('data-table');

        new agGrid.Grid(eGridDiv, gridOptions);

        function renderDataInTheTable(api) {
            fetch('https://jsonplaceholder.typicode.com/todos')
                .then(function (response) {
                    return response.json();
                }).then(function (data) {
                    api.setRowData(data);
                    api.sizeColumnsToFit();
                })
        }
    </script>
</body>
</html>

数字过滤器

由于userIdid 列是数字,我们可以通过修改columnDefs ,使之使用数字过滤器:

        const columnDefs = [
            { field: 'userId', filter: 'agNumberColumnFilter'},
            { field: 'id', filter: 'agNumberColumnFilter'},
            { field: 'title' },
            { field: 'completed' },
        ];

这里是数据网格数字过滤器HTML页面的例子。

在AG Grid文档中列出了很多列的配置选项,例如配置宽度、样式,以及使单元格可编辑。

数据网格的好处

对于许多网站来说,一个简单的HTML表格将是呈现表格数据的一种完全合理的方式。它既快速又容易理解,而且用一点CSS就可以让你的用户觉得这个表很好看。

当你的页面变得更加复杂,渲染更多的数据,或者需要用户有更多的互动性时,那么使用数据网格组件或库就开始变得更有意义了。

数据网格提供了许多你的用户需要的功能,而不需要编写大量的代码。在这篇文章中的例子中,我们从一个从API中读取数据的动态表格,转移到一个从API中读取数据的数据网格,并带有排序、过滤、分页和列大小调整功能。

这是很多额外的功能,但我们的HTML代码长度是一样的,我们添加的JavaScript也不那么复杂,因为数据网格做了所有渲染数据的工作。

数据网格可以处理成百上千行的数据,而且更新速度很快,所以它们经常被用于实时金融交易系统,单元格中的价格每几毫秒就会更新一次。

如果你使用React,那么除了AG网格之外,你还可以看看Material UIReact Table。React Table是一个 "表格",而不是一个数据网格,所以它在最初需要更多的代码来渲染这个表格。

Material UI和React Table都只适用于React。AG Grid与框架无关,可以与JavaScript、TypeScript、React、Angular和Vue一起工作。

这篇文章的源代码可以在这个Github repodocs/html-table-to-ata-grid文件夹中找到