如何在动态 javascript table 中显示行数计数?

How do I display the row number count in a dynamic javascript table?

我在前端使用 ejs 遍历一个数组并在 bootstrap 5 table 中显示数据。 table 是动态的,因此会随着时间的推移添加和删除行。

所有数据都没有错误地通过并且 table 正在填充,但是,我想让第一列显示每一行的“计数”。例如,“1、2、3、4、5 等”。

我尝试使用 indexOf 但没有成功,因为代码已经存在于一个循环中,创建另一个循环需要我切换我的 ejs 语法,我失去了计算数组长度的能力.

下面是我的客户端代码,它在每一行的整个 # 列中产生值 -1

 <div class="col-12 d-flex flex-row-reverse">
  <a href="" class="a1" onclick="exportTableToCSV(null, 'text.csv')">Export to csv</a>
 </div>
 <div class="table-responsive">
<table class="table table-hover">
  <thead>
    <tr>
      <th scope="col">#</th>
      <th scope="col">Email</th>
      <th scope="col">Gender</th>
      <th scope="col">Age</th>
      <th scope="col">Clicked IG</th>
      <th scope="col">Date Added</th>
      <th scope="col">Remove</th>
    </tr>
  </thead>
  <tbody>
    <% for (let fan of artist.fans){%>
    <tr>
      <th scope="row"><%= artist.fans.indexOf(fan.id) %></th>
      <td><%= fan.email %></td>
      <td><%= fan.gender %></td>
      <td><%= fan.age %></td>
      <td><%= fan.subscribed %></td>
      <td><%= fan.dateAdded %></td>
      <td style="padding-left: 2rem;">
        <button onclick="removeRow()" ></button>
         </td>
      <td style="padding-left: 2rem;"><i class="bi bi-trash"></i></td>
      <% } %> 
    </tr>
  </tbody>
</table>
</div>

如果我将 artist.fans.indexOf(fan.id) 切换为 只是 fan.id 我会为每个粉丝电子邮件对象获得相应的 objectId。

如果我将 artist.fans.indexOf(fan.id) 切换为 artist.fans.length,我会在每行的 # 列中得到 7

这是我的数据库模型:

const artistSchema = new Schema({
image: [ImageSchema], 
genre: [ String ], 
fans: [{ 
    email: String, 
    subscribed: String, 
    gender: String, 
    age: Number 
        dateAdded: {
            type: Date,
            default: Date.now
                }
      }], 
 });

如何让每一行都被编号?

问题是您在 artist.fans.indexOf(fan.id) 中使用了 fan.id 作为搜索参数,它不能直接访问抛出 artist.fans 因此您需要使用另一种方法来接受 comparing function 所以你可以比较他们的 id

尝试使用

<th scope="row"><%= artist.fans.findIndex(f=> f.id == fan.id) %></th>

TL;DR:

只需这样做:

<% for( const fan of artist.fans.map( ( e, index ) => ({ index, ...e }) ) ) { %>
<tr>
    <th scope="row"><%= fan.index %></th>
    <td><%= fan.email %></td>
    <!-- etc -->
</tr>

<% }%>

解释:

I'd like to have the first column show the "count" for each row. Eg., "1, 2, 3, 4, 5, etc".

  • 这不是“计数”。我将其称为“row-number”(如果基于 1)或“row-index”(如果基于 0)。

  • 如果您使用 SQL 获取数据,那么您只需在查询中使用 ROW_NUMBER() 并将其映射到一些新的 number 属性 在你的 Fan 类型中。

    • 请注意,如果没有 well-defined ORDER BY 标准,ROW_NUMBER() 值有点毫无意义。
  • Don't use indexOf in a loop: indexOf 的运行时复杂度为 O(n) 因此不建议在同一个数组的循环中使用该函数,因为它会给你O(n^2) 运行时,非常糟糕

    • 这也适用于其他 O(n) reduction 函数,例如 findIndexfindincludeslastIndexOf.
    • 此外,indexOf(和其他)仅在 Array.prototype 上定义,因此它不适用于其他类型的可迭代对象,例如 NodeList.
  • 在 ejs 中,you can use an overload of Array.prototype.map 为您提供每个 elementindex,并将 index 粘贴到每个 element 中对象,像这样:

const fansWithIndex = artist.fans
    .map( function( element, index ) {
        element.index = index;
        return element;
    } );

<% for( const fan of fansWithIndex ) { %>
<tr>
    <th scope="row"><%= fan.index %></th>
    <td><%= fan.email %></td>
    <!-- etc -->
</tr>

<% }%>

...虽然 FP 纯粹主义者(像我自己)会争辩说上面的例子是错误的代码,因为它改变了源数据,而应该使用 Object.assign。此外,long-form function 可以使用 arrow-function => 变得更简单,如下所示:

const fansWithIndex = artist.fans.map( ( e, idx ) => Object.assign( { index: idx }, e ) );

<% for( const fan of fansWithIndex ) { %>
<tr>
    <th scope="row"><%= fan.index %></th>
    <td><%= fan.email %></td>
    <!-- etc -->
</tr>

<% }%>

这可以进一步简化:

  • 可以使用 Object.assign the object spread operator ...obj 的替代方法:
    • { index: idx, ...e } 几乎 在语义上与 Object.assign( { index: idx }, e ) 相同。
      • 区别在于 Object.assign 将调用自定义 setter 函数,而 ... 语法不会。
    • 注意:当立即从 map 中返回 object-literal 时,您 ,因此 => ({ foo: bar }) 而不是 => { foo: bar }
  • index: idx 可以通过将 idx 参数重命名为 index 并只放置 { index, ...e }.
  • 来简化
  • 像这样:
const fansWithIndex = artist.fans.map( ( e, index ) => ({ index, ...e }) );

<% for( const fan of fansWithIndex ) { %>
<tr>
    <th scope="row"><%= fan.index %></th>
    <td><%= fan.email %></td>
    <!-- etc -->
</tr>

<% }%>
  • 因为 artist.fans.map(...) 部分是一个具有单个输出的表达式,您现在可以将其直接内联到您的 for(of) 语句中,如下所示:
<% for( const fan of artist.fans.map( ( e, index ) => ({ index, ...e }) ) ) { %>
<tr>
    <th scope="row"><%= fan.index %></th>
    <td><%= fan.email %></td>
    <!-- etc -->
</tr>

<% }%>