10

I'm trying to build a LWC that renders a complex table-based UI. It needs to have distinct rows and columns with dynamically-determined colspans - a real table. It's for assigning lodging, and each row will represent a room and each column for which the room can be assigned. It will involve drag/drop etc.

I was hoping to make each row and each cell a component, so that data and logic could be encapsulated in each of those. However, that doesn't seem to work because LWC includes a tag w/ the component name in the rendered HTML.

Here's some test code.

Table component markup:

<template>
<p>
    This is our very nice room table!
</p>
<table style="border:1px black solid">
    <!--header row goes here -->
    <template for:each={rooms} for:item="room">
        <c-ncilodgeroomtablerow key={room}>
        </c-ncilodgeroomtablerow>
    </template>
</table>
</template>

The var rooms is just set to [1,2,3] in the js controller so that it will iterate 3 times.

Here's the row component markup, just hard-coded w/ static content for now:

<template>
<tr>
    <td>
        cell one
    </td>
    <td>
        cell two
    </td>
</tr>
</template>

This does not render as desired. It ends up with everything in one cell, because it puts c- tags around the tr tags, and I think the tr's need to be immediate children of the table. Here's the rendered markup:

<table style="border: 1px solid black;">
<c-ncilodgeroomtablerow>
    <tr>
        <td>cell one</td>
        <td>cell two</td>
    </tr>
</c-ncilodgeroomtablerow>
<c-ncilodgeroomtablerow>
    <tr>
        <td>cell one</td>
        <td>cell two</td>
    </tr>
</c-ncilodgeroomtablerow>
<c-ncilodgeroomtablerow>
    <tr>
        <td>cell one</td>
        <td>cell two</td>
    </tr>
</c-ncilodgeroomtablerow>
</table>

I also tried putting the trs in the table component markup, inside the iterator, and then changing the row component to just include the td tags and not the tr's. That didn't work either - again because of the c- tags inside the tr's.

Is there any way to make something like this work w/ LWCs? Or do I need to look into making a css-based table layout? Open to general advice.

12

Building on the answer provided already by @sfdcfox, this following seems to be working for me and allows me to componentize the entire row (much easier to emit events this way).

parent.html

<table class="slds-table slds-table_bordered slds-border_left slds-border_right">
  <thead>
    <tr class="slds-line-height_reset">
      <th class="" scope="col">
        <div class="slds-truncate" title="Name">Name</div>
      </th>
      <th class="" scope="col">
        <div class="slds-truncate" title="Some Custom Column">Some Custom Column</div>
      </th>
    </tr>
  </thead>
  <tbody>
    <template for:each={iterableDataSet} for:item="row">
      <c-child-row row={row}>
      </c-child-row>
    </template>
  </tbody>
</table>

childRow.html

<template>
  <!-- taken straight from SLDS -->
  <th data-label="Name" scope="row">
    <div class="slds-truncate" title={row.Name}>{row.Name}</div>
  </th>
  <td data-label="Some Custom Column">
    <div class="slds-truncate">
      <lightning-input
        data-id={row.Id}
        type="number"
        variant="label-hidden"
        name="inputForDataField"
        onchange={handleOnChange}
        value={row.Data_Field__c}>
      </lightning-input>
    </div>
  </td>
  <!-- more tds .... -->
</template>

And the critical piece on childRow.css

:host {
  display: table-row;
}
| improve this answer | |
  • Finally got a chance to try this, and worked a treat! Thanks @tsalb – mscholtz Mar 5 '19 at 1:42
  • Wouldn't adding display: contents to the row work too? It would skip over the parent element and therefore give the table proper HTML table structure? – Gus Melendez Feb 4 at 1:28
4

Ideally, you shouldn't use table; lightning-layout and lightning-layout-item work as tables for all practical purposes. If that doesn't work for you, then you'd want to simply apply the appropriate CSS to your components.

Row Display

:host {
  display: table-row;
}

Cell Display

:host {
  display: table-cell;
}

I seem to be having a problem writing a working version of this, but it should work once everything is nested. Do not include tr in your row component, nor td in your cell component. Your component becomes the row/cell, so you don't need to nest them.

| improve this answer | |
  • Thanks @sfdcfox, we thought of that but I don't believe that the CSS version of the table allows you to do arbitrary colspans. i.e. you can make a row into a single cell that spans the whole row, but you can't make 1 cell in the middle of a row span two columns. AFAIK. And that's something we need. – mscholtz Feb 21 '19 at 5:42
  • 1
    Hmm, can't you just use a slds-grid? <div class="slds-grid slds-gutters"> <div class="slds-col slds-size_2-of-12">... etc – Caspar Harmer Feb 21 '19 at 7:10
  • @Casper, oh, I had forgotten about that. Good suggestion. In this case, though, we actually want things like flush gridlines etc. i.e. we really want it to look like a traditional table. And we will need to support more than 12 columns, which slds grid does not. – mscholtz Feb 21 '19 at 7:50
  • Our tentative direction is to not make the rows and cells components. Instead the table component is rendering the trs and tds in iterators and then putting components inside the cells. This works, but means that we will have to do rerendering at the whole tabel level instead of further down the DOM. We are seeing some other minor artifacts of having those <c-...> tags in the markup, including some CSS oddness. Anyone know why those need to be in there? – mscholtz Feb 21 '19 at 7:53
  • @mscholtz You'll want to read about the shadow DOM. Each LWC becomes a shadow root, isolating CSS and internal DOM structure into a custom HTML element. This means that you can only style the outer part of the component, as if it were a real DOM element (because it is) instead of as an entire DOM tree. The <c-...> elements are there because that's how shadow DOM works. – sfdcfox Feb 21 '19 at 11:04
2

I agree with most statements from the comments where there are better choices than a table but another option I found is to use the following CSS in your "row" component:

:host{
    display: contents;
}

This causes an element's children to appear as if they were direct children of the element's parent, ignoring the element itself.

Do note that browser support still has some issues.

https://caniuse.com/#search=display%3A%20contents

Specifically in the accessibility area (people using assistive technology). You can read about that here:

https://hiddedevries.nl/en/blog/2018-04-21-more-accessible-markup-with-display-contents

So if accessibility would be a criteria for your components, keep this in mind.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.