Components: [Table] Columns get misaligned in dense tables

Created on 26 Jul 2017  路  14Comments  路  Source: angular/components

Bug, feature request, or proposal:

Bug. We are currently using md-table in an application that displays complex tables with a lot of columns containing financial data. The tables oftentimes become rather dense and there is not a lot of room left for each column. If a cell's content is too long and cannot be wrapped, the cell grows in size shifting the row's remaining cells to the right. In our case the problem gets even worse because we need to indent the content of the first cell (using padding-left) in order to visualize hierarchical information.

What is the expected behavior?

md-table should behave like an HTML table and ensure all cells in the same column have the same width.

What are the steps to reproduce?

I have created a plunker that shows the problem:
https://plnkr.co/edit/kJF4NNCrFLRu4g4pFwRc?p=preview

Which versions of Angular, Material, OS, TypeScript, browsers are affected?

[email protected], angular 4.3.1

Sorry if I should have overlooked any issue already describing this problem and thanks for all your work on this great library! :)

P4

Most helpful comment

_tl;dr: just add overflow: hidden; word-wrap: break-word to your cells_

The issue here stems from the cell's content stretching the width. Since each cell has different content, they become misaligned and the table looks wonky.

image

To resolve this, tell each cell that it cannot overflow and that it should wrap its content rather than stretch the container.

Here's some CSS that fixes this case:

.mat-cell, .mat-header-cell {
  overflow: hidden;
  word-wrap: break-word;
}

For a plunker with this applied, see https://plnkr.co/edit/3ctqAiPsFe60Mnc3T0Eh?p=preview

image

The reason this is not included as part of the table is because the mat-table knows that cells can have a wide array of content and is not trying to be too prescriptive about how to overflow and wrap its contents. Though, an argument could be made that this should always be applied to the cells.

Note that in this particular example, I'd go one step further and say that progress and color probably can be more restricted in their width and let the name column stretch as much as it wants. This can be done by applying this CSS:

.mat-column-progress {
  max-width: 60px;
}

.mat-column-color {
  max-width: 100px;
}

Plunker of this applied: https://plnkr.co/edit/anDyDjxmECOWHlTrIwzD?p=preview

image

All 14 comments

I think I found a workaround for this problem. By changing the css to use display: table instead of flexbox, the browser takes care of all layout problems quite nicely. Not sure what the rationale behind using flexbox instead of table was, however. Maybe I have overlooked something?

.mat-table {
  display: table;

  > .mat-header-row, > .mat-row {
    display: table-row;

    > .mat-header-cell, > .mat-cell {
      display: table-cell;
      height: 48px;
      vertical-align: middle;
    }
  }
}

Thanks, @jpett! I modified your styles a bit (restored border, fixed width and paddings):

.mat-table {
    display: table;
    width: 100%;

    > .mat-header-row, > .mat-row {
        display: table-row;
        padding: 0;
        border: none;

        > .mat-header-cell, > .mat-cell {
            display: table-cell;
            height: 48px;
            vertical-align: middle;

            border-bottom: 1px solid rgba(0, 0, 0, 0.12);
        }
    }
}

These really saved me from recalculating all the things in js... This was going to be a nightmare. Now my table looks perfect, and extends it's parent md-card. Saved my day :)

Thanks guys, this is wonderful.
I've had the same problem due to long strings in some columns. Slim columns also take up way too much space by default. I wouldn't know how to solve this using flex-layout.

Any plnkr links guys?

Here you go: https://plnkr.co/edit/tzb09J?p=preview (just converted the scss code to css for the example)

I ran into problems problems when I tried to combine this with expandable table rows. The expanded part is now only one column wide. Guess that happens when you combine two hacks.

Does someone know how to solve this issue using flexbox instead of native tables?

there is a problem with the proposed solution (display: table): the max-height attribute doesn't work anymore. So table scrolling is gone.

_tl;dr: just add overflow: hidden; word-wrap: break-word to your cells_

The issue here stems from the cell's content stretching the width. Since each cell has different content, they become misaligned and the table looks wonky.

image

To resolve this, tell each cell that it cannot overflow and that it should wrap its content rather than stretch the container.

Here's some CSS that fixes this case:

.mat-cell, .mat-header-cell {
  overflow: hidden;
  word-wrap: break-word;
}

For a plunker with this applied, see https://plnkr.co/edit/3ctqAiPsFe60Mnc3T0Eh?p=preview

image

The reason this is not included as part of the table is because the mat-table knows that cells can have a wide array of content and is not trying to be too prescriptive about how to overflow and wrap its contents. Though, an argument could be made that this should always be applied to the cells.

Note that in this particular example, I'd go one step further and say that progress and color probably can be more restricted in their width and let the name column stretch as much as it wants. This can be done by applying this CSS:

.mat-column-progress {
  max-width: 60px;
}

.mat-column-color {
  max-width: 100px;
}

Plunker of this applied: https://plnkr.co/edit/anDyDjxmECOWHlTrIwzD?p=preview

image

Thanks for your elaborated response @andrewseguin! The solution you proposed still behaves a little different from classic html tables:

  • By default a classic html table won't break any words but grow in width in order to fit it's content. Especially in responsive designs on limited screen sizes this is in my opinion a more productive solution, since you can wrap the table in an overflow: auto container (as it is e.g. common in Bootstrap, using the class . table-responsive)
  • In your solution there is no way to let the table decide how to size the columns based on its content. Your example only allows for one column with dynamic width

I understand your argument that you don't want to be too prescriptive about how to overflow and wrap the table content. Still, I would recommend using default styles that work robustly and to clearly explain this matter in the documentation. In my opinion people will stumble upon this problem rather quickly if they don't only have a sample table with a limited amount of columns.

@jpett Thanks for your feedback on the issue. I'm now leaning towards adding a PR that includes the overflow styles I included in my previous comment, since I'm not sure there's a good case for not wanting those styles. Hopefully this should solve most problems that people will run into, at least with regards to columns where you know what their widths should be set to.

I agree that we have a feature gap with regards to resizing based on content. It's possible to use min and max widths to adjust the table, which works well for columns that are filled with static content or fixed width buttons. But this doesn't help for dynamic content or text that shrinks/grows when internationalized (looking at you, German).

While display: table works with our current feature-set and HTML rendering, I believe that we're going to see that it doesn't play nice with features such as sticky columns, which will likely break up the templating in a significant way. I love the simplicity in using a set of styles that auto-sizes for us, but I don't think it'll work for us with our long-term plans for the table.

I'm currently trying to design/prototype what it would look like to have some inputs or directives that can be applied to a column definition that can help provide some kind of auto-sizing. E.g.

<ng-container cdkColumnDef="userName" width="shrink-to-fit">
  ...
</ng-container>

With this, a strategy can be applied each time the table renders that can resize the column based on the rendered output. If we incorporate some kind of smart-width feature, it should be easy to adapt it to also allow for simple inputs that automatically restrict the width to min/max bounds, and even provide some hooks so that you can easily resize the columns based using drag-support.

I'll soon be writing up a new set of tables for the md-table (once MdTableDataSource gets in). When I do, I'll set aside a section that talks a bit about using width styles. I agree it's non-trivial to properly set up a good responsive md-table right now, and I'm hopeful that we can design some clean solutions that helps users out.

@andrewseguin Thanks again for your extensive reply! In my opinion the word-wrap: break-word style is not a good solution for real applications. While having a dummy string like "AAAAAA" break in between is not a big deal, breaking any real data like dates, numbers, names or similar is a rather big one. I think I could not offer such a solution to our customers. Therefore I see having a break-word style just as a tiny improvement.

In the end it might just be a matter of priorities. I personally would prefer the mat-table to work more like a normal HTML table, providing autosize for columns by default without me having to set any width properties or styles. Of course, fixed headers and/or columns are also an important feature, but I think these could also be implemented without major problems in a display: table solution. In my experience it might even be a lot easier than implementing a smart-width feature, which could be kind of tricky ;)

@andrewseguin is there another fix for this? Unfortunately, as I stated before, I cannot use word-wrap: break-word, since this is splitting up vital information. The solution provided earlier by @jpett also doesn't work, because it breaks the view of the tables making them left-aligned and not fully sized.

While display: table works with our current feature-set and HTML rendering, I believe that we're going to see that it doesn't play nice with features such as sticky columns

@andrewseguin If I may, this Vue.js-based UI library achieves sticky-column (and other features) while still taking advantage of the display: table. I must admit that there is a slight animation delay (that might be due to the underlying animation implem). I might not be aware of some limitations but it sounds like a decent implementation.

I'll probably port their library because I don't think flexbox is the solution to fix the longstanding issues with tables.

On a side-note (since Google is an insider), is there any plans at the W3C level to improve the table element (especially around sticky stuff)?

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

_This action has been performed automatically by a bot._

Was this page helpful?
0 / 5 - 0 ratings

Related issues

crutchcorn picture crutchcorn  路  3Comments

LoganDupont picture LoganDupont  路  3Comments

savaryt picture savaryt  路  3Comments

vitaly-t picture vitaly-t  路  3Comments

xtianus79 picture xtianus79  路  3Comments