Components: [Table] Responsive stacking support

Created on 17 Nov 2017  路  29Comments  路  Source: angular/components

Bug, feature request, or proposal:

feature request

What is the expected behavior?

I would like to be able to convert a CDK table into a stacked list. I want to do this through CSS media queries (i.e. when window size is narrow), and I also want to be able to do it through JavaScript (e.g., when the user clicks a button). The header cell contents should be displayed on each line instead of just at the top. E.g.,

     Header 1     Header 2     Header 3     Header 4
       1.1          1.2          1.3          1.4
       2.1          2.2          2.3          2.4

becomes

     Header 1        1.1
     Header 2        1.2
     Header 3        1.3
     Header 4        1.4

     Header 1        2.1
     Header 2        2.2
     Header 3        2.3
     Header 4        2.4

and even

     Header 1
        1.1
     Header 2
        1.2
     Header 3
        1.3
     Header 4
        1.4

     Header 1
        2.1
     Header 2
        2.2
     Header 3
        2.3
     Header 4
        2.4

If this is already facilitated by the CDK, I would like to see an example of this feature in the docs.

What is the current behavior?

The two docs pages I am looking at (https://material.angular.io/guide/cdk-table and the link below) do not mention 'responsive' or 'stack', and there are no demonstrations of tables that stack.

From https://material.angular.io/components/table/overview
image

What are the steps to reproduce?

Go to https://material.angular.io/components/table/overview on a small mobile browser or, with a desktop browser, resize the window to be narrow (e.g., 760px wide).

What is the use-case or motivation for changing an existing behavior?

1) There are groups where I work, and no doubt at many other organizations, that do not want to use a table library that does not offer this feature.
2) The need for this feature is evidenced by its existence in other solutions, such as PrimeNG datatable https://www.primefaces.org/primeng/#/datatable/responsive and https://css-tricks.com/responsive-data-tables/

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

N/A

Is there anything else we should know?

This is a restatement of https://github.com/angular/material2/issues/6608, trying to follow the advice of @jelbourn.

P4 materiatable feature needs discussion

Most helpful comment

Since the foundation of the table allows for any arbitrary content to be in the header/row cells, it'll be hard to make a universal approach. For example, if your header has a filter/menu/icon/etc then you'll likely not want those things to show up in the responsive version.

I'll incorporate this into my design for some kind of mat-simple-table which understands more deeply about what data is being shown and some rules on what is possible (e.g. header cells are only text).

For now with the current tables, it's just too open for any templates to fully prescribe something.

That said, if you want to make your tables responsive. Here's an example of how you can do this:

Wide-screen:
sdgmds2pcop

Narrow-screen:
nuamqoyocjs

CSS:

// By default, the mobile label shouldn't show
.mobile-label {
  display: none;
}

@media(max-width: 600px) {
  // Custom header label that shows in each column, with a set width and bold font
  .mobile-label {
    width: 80px;
    display: inline-block;
    font-weight: bold;
  }

  // Remove the header row altogether
  .mat-header-row {
    display: none;
  }

  // The row cells should display as a column, justified left, with a bit of extra padding
  .mat-row { 
    flex-direction: column;
    align-items: start;
    padding: 8px 24px;
  }
}

Here's a working example: https://stackblitz.com/edit/angular-mohmt5?file=app%2Ftable-basic-example.ts

All 29 comments

I was able to implement a workaround using samples provided here: https://css-tricks.com/responsive-data-tables/

It would certainly be nice to be able to have this behaviour as part of the component (and toggle it on/off).

Agree with @marcusreese, this is a must-have feature in a first-mobile & across devices today's world. Maybe some workaround with FlexLayout as an example?

Since the foundation of the table allows for any arbitrary content to be in the header/row cells, it'll be hard to make a universal approach. For example, if your header has a filter/menu/icon/etc then you'll likely not want those things to show up in the responsive version.

I'll incorporate this into my design for some kind of mat-simple-table which understands more deeply about what data is being shown and some rules on what is possible (e.g. header cells are only text).

For now with the current tables, it's just too open for any templates to fully prescribe something.

That said, if you want to make your tables responsive. Here's an example of how you can do this:

Wide-screen:
sdgmds2pcop

Narrow-screen:
nuamqoyocjs

CSS:

// By default, the mobile label shouldn't show
.mobile-label {
  display: none;
}

@media(max-width: 600px) {
  // Custom header label that shows in each column, with a set width and bold font
  .mobile-label {
    width: 80px;
    display: inline-block;
    font-weight: bold;
  }

  // Remove the header row altogether
  .mat-header-row {
    display: none;
  }

  // The row cells should display as a column, justified left, with a bit of extra padding
  .mat-row { 
    flex-direction: column;
    align-items: start;
    padding: 8px 24px;
  }
}

Here's a working example: https://stackblitz.com/edit/angular-mohmt5?file=app%2Ftable-basic-example.ts

Some sort of responsive implementation would be nice, even at the very least allow the table to overflow along the x-axis and perhaps add a fixed column. For example, there is a Vue.js component library called Element that has a lot of nice options for table. Check out an example at Element's website.

@dhniels I think that is an awesome idea. that table's functionality looks awesome. I hope that material team will provide these kinds of options.

1+ for responsive table

I think a scroll at the x-axis will be a great approach and of course, it will be a lot better if an implementation of a fixed column like the Element's webpage is added.

Another simple workaround to make the table responsive:

Create a new div:

<div class="responsive_table">
</div>

Inside that div create your mat-table.
Now go to your css and put something like this:

@media(max-width: 800px) {
    .responsive_table {
        overflow-x: auto !important;
    }

    .mat-table {
        min-width:800px;
    }
}

This is not an "elegant" solution but works.

Another +1 for built-in responsive Angular Material Tables

1+ for that!

Has angular added support for this yet?

Is there any way to add the mobile-label programmatically?

I use it like this, don't remember anymore what is my code and what I copied.
https://stackblitz.com/edit/rman-2952

One more, +1 for this feature.

(I'm going through the highest voted issues today an commenting on their status)

We currently don't have any active plans to work on this; the cdk-table/mat-table adheres pretty strictly to the single-responsibility principle, where that responsibility is "render rows of cells". You can see this in the way that sorting and pagination are enhancements added on _top of_ the table without being built into it. Switching to a different rendering structure based on screen-size seems just outside of that responsibility.

That said, we _do_ want to provide some guidance (and maybe some components) at some point to help make this interaction easier, even if it's not built directly into the table. This definitely won't happen in 2019, though.

I use it like this, don't remember anymore what is my code and what I copied.
https://stackblitz.com/edit/rman-2952

Thanks, thats the first and only way - which I found on internet - how to make table (not mat-table) responsive.
But it is visible how hard it is - label has to be hardcoded two times, and that way which doesn't allow align data-label left and data right. Or is it possible?


There are more examples for making mat-table responsive. But material table is now declared by table element, not mat-table element. So these examples do not work for actual material table. I will appreciate if actual material table responsivity has working example in official material documentation.

What is the reason for the existence of both mat-table and cdk-table? It brings ambiguities and complications. But it seems that the material schematic for a table wants to support cdk-table using a DataSource that does not have a filter like MatTableDataSource. DataSource brings nothing useful, just complications and complexity where it should not be. I suggest that Material has one component for the table and that it has completed properly - and this component should by supported by material table schematic generated component.
I spend my days solving these ambiguities.

+1 for responsive mat table

@angular any update?

We at last solve this by reduction of visible columns according to display sizes and displaying rest columns in grid detail. Change material table row into column is not very good solution.

I created a little directive which can be applied to a mat-table table to make it responsive. This way it is easy to only add responsive behavior to only some tables in the application (which was a requirement in my case). So basically the directive duplicates the content of each header cell (column name) as an data attribute so it can be accessed via the CSS content property (inspired by the link from the first comment in this thread). Unfortunately CSS styles can't be applied to a directive, so the styles need to be imported globally.

Features:

  • no extra hard coded column names for the mobile view
  • reacts on language changes and updates the column names for the mobile view
  • reacts also on row changes/updates (e.g. add a row)
  • in the mobile view it only shows the columns which were made sortable via the matSort directive

Here is a little stackblitz demo:

https://stackblitz.com/edit/material-table-responsive

This should not be seen as a general solution, as it does of course not cover all use cases. Also the styling of the table mobile view is bound to my specific requirements and probably needs to be adjusted. But maybe it helps/serves as a starting point for others.

knoefel, nice work. Another way is hide some columns according to display width.

I have created a responsive solution for the matTable that I believe is currently the best that I know of.

馃殌 馃殌 馃殌 (make sure you use dev-tools "mobile/tablet"-view mode + "responsive" to minimize the window width)

馃殌 馃殌 馃殌

please note:

  • ExpressionChangedAfterItHasBeenCheckedError is due to this line : <p> Table width {{ t1._elementRef.nativeElement.offsetWidth }} px </p> that shows you the current width of the table. I think its nice to have this on display.
  • The transition from NORMAL to STACKED (and vise versa) might seem "slow/ clunky". Its due to fromEvent(window, "resize") .pipe(debounceTime(400)) this can be changed ...

How to start

1 add the the directive to mat-table
<table mat-table [responsiveTableWithTpl]="respPattern" responsiveTableAtPx="360" ...

  • respPattern is the "template" for how the stacked version should look

  • responsiveTableAtPx defines the cutoff in pixels when the table should transform.

  1. add styles to global. Took inspiration here: >> (StackB)

thats it.

Implementation details

By looking at the source of cdk-table (and I tell you its a very complex ...) I figuered that _renderRow() is a very essential function to render the table. So the idea is to "monkeyPatch/ duckPunch" this function to make use of my "respPattern" ng-template

@jelbourn @andrewseguin
If the material-team would provide some api to add a customRenderRow() this issue could be closed.

  • for stacked we could call matTable.customRenderRow(myFun1) and for native render matTable.customRenderRow(null)
  • Also I need to call private function this.matTable._forceRenderDataRows(); when I transform the table since the "data-model" did not change ...

Things to do

  • better api ?
  • better css to make it look like here? StackB
  • we are observing window:resize could we observe the width change of nativeElement-MatTable?
  • If the table width changes that is not due to window-resize we have to currently manually call <responsiveTableWithTpl>.onResize().

I created little sample for material table responsive.

datatable-responsive

Stackblitz Example

sathishvisar - very nice - your solution hides columns by the width of the display, and allows you to show the hidden columns at the click of a button.

I have one suggestion - now, the table hide the last column by the size of the display. It would be better to allow for each column from which display width to display, e.g. showIfWidthAbove: "xs" for an important column that always appears, showIfWidthAbove: "md" if the display width is 960px or more. This is because it is usually necessary to have the columns in the table in a different order than the hiding order. This would really be a solution for a real application.

I created little sample for material table responsive.

datatable-responsive

Stackblitz Example

Thank you so much, this is really helpful

eddiesri - yes - this is really responsive table!

Every row could have its actions - edit / delete / run function - how to do this?

To get @andrewseguin 's fantastic solution to work on Angular Material 8, I had to unset a couple of css properties on .mat-row and mat-cell. Here's the new code:

// By default, the mobile label shouldn't show
.mobile-label {
  display: none;
}

@media(max-width: 600px) {
  // Custom header label that shows in each column, with a set width and bold font
  .mobile-label {
    width: 80px;
    display: inline-block;
    font-weight: bold;
  }

  // Remove the header row altogether
  .mat-header-row {
    display: none;
  }

  // The row cells should display as a column, justified left, with a bit of extra padding
  .mat-row {
    flex-direction: column;
    align-items: start;
    padding: 8px 24px;
    min-height: unset; // Tweak for Angular Material 8
  }

  // Tweaks for Angular Material 8
  .mat-cell:first-of-type, .mat-footer-cell:first-of-type, .mat-header-cell:first-of-type {
    padding-left: unset;
  }

  .mat-cell {
    align-items: unset;
    min-height: unset;
  }
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

MurhafSousli picture MurhafSousli  路  3Comments

theunreal picture theunreal  路  3Comments

shlomiassaf picture shlomiassaf  路  3Comments

constantinlucian picture constantinlucian  路  3Comments

michaelb-01 picture michaelb-01  路  3Comments