12

I have a simple table with numerous columns, and I would like the columns to shrink automatically when sizing the browser window. The table-responsive works with Bootstrap, but I am looking for something similar for Angular Material. I am trying to avoid having 2 table definitions, and do not really want to do this myself in CSS. Is there a standard way in Angular Material for this?

<table class="mdl-data-table mdl-js-data-table mdl-shadow--2dp" flex>
    <thead>
        <th class="mdl-data-table__cell--non-numeric">Date</th>
        <th class="mdl-data-table__cell--non-numeric">Time</th>
        <th class="mdl-data-table__cell--non-numeric">Park</th>
        <th class="mdl-data-table__cell--non-numeric">Home Team</th>
        <th class="mdl-data-table__cell--non-numeric">Away Team</th>
        <th class="mdl-data-table__cell--non-numeric">Win/Loss</th>
    </thead>
    <tbody>
        <tr ng-repeat="game in games">
            <td class="mdl-data-table__cell--non-numeric">{{game.GameDate | SLS_Date}}</td>
            <td class="mdl-data-table__cell--non-numeric">{{game.GameTime | SLS_Time:'HH:mm' }}</td>
            <td class="mdl-data-table__cell--non-numeric">{{game.VenueName}}</td>
            <td class="mdl-data-table__cell--non-numeric">{{game.HomeTeamName}}</td>
            <td class="mdl-data-table__cell--non-numeric">{{game.AwayTeamName}}</td>
            <td class="mdl-data-table__cell--non-numeric">{{game.WinLoss}}</td>
        </tr>
    </tbody>
</table>

Note, I have used the Material Design Lite table here, as Angular Material does not have a table today. Same problem, including the use of flex and grids.

I guess the new Material world does not like tables, so best option is to change the layout to not use tables.

Steven Scott
  • 10,234
  • 9
  • 69
  • 117
  • 1
    Those who are interested in a solution for Angular 2+ can add a thumbs up to https://github.com/angular/material2/issues/8494 because when more people show interest, the team will have more reason to prioritize this feature. – Marcus Dec 01 '17 at 17:45

3 Answers3

49

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 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 a data attribute so it can be accessed via the CSS content property (inspired by this article). 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 mobile view
  • reacts on language changes and updates the column names for mobile view
  • reacts also on row changes/updates (e.g. add a row)
  • in mobile view it only shows the columns which were made sortable via the matSort directive

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 i just wanted to share it, so that it maybe could serve and help as a starting point for others.

Angular 12+ (TS strict mode)

Because of the request in the comments from the user klaydze, here is an alternative version that supports the requirement to use strict: true in tsconfig.json for Angular 12+.

Stackblitz using TS strict mode

Angular < 12

Stackblitz

knoefel
  • 5,991
  • 3
  • 29
  • 43
  • 3
    Awesome job! Thanks – H. Herzl Mar 31 '20 at 13:07
  • 3
    wow. Just awesome. Thanks for the wonderful solution. You just save the world. I really mean it. – Santosh Jan 18 '21 at 09:23
  • 2
    Yes, he really saved. Thanks very much, @knoefel Maybe deploy the directive as an angular package so that anyone can use it without creating the directive in their code – Obum Jan 25 '21 at 14:09
  • 1
    I'm having problem consuming this directive in Angular 12. It complain related to this `Type 'HTMLCollection' must have a '[Symbol.iterator]()' method that returns an iterator.`. – klaydze Jul 07 '21 at 14:09
  • 1
    @klaydze I updated the stackblitz demo to Angular 12 and it's still working fine. I think your `tsconfig.json` is missing the `DOM.Iterable` lib. I added it also in the stackblitz demo, so you can check there how to use it. – knoefel Jul 07 '21 at 15:30
  • @knoefel thank you updating. since I have `strict: true` in `tsconfig`, it also complaining `Argument of type 'OperatorFunction<[boolean, boolean], (HTMLTableRowElement | HTMLCollectionOf | null)[]>' is not assignable to parameter of type 'OperatorFunction<[boolean, boolean], [HTMLTableRowElement, HTMLCollectionOf]>'. Type '(HTMLTableRowElement | HTMLCollectionOf | null)[]' is not assignable to type '[HTMLTableRowElement, HTMLCollectionOf]'. Target requires 2 element(s) but source may have fewer.ts(2345)` – klaydze Jul 08 '21 at 05:58
  • @klaydze I made an alternative version using the TS config setting `"strict": true` -> [Stackblitz](https://stackblitz.com/edit/material-table-responsive-strict-mode?file=src/app/mat-table-responsive/mat-table-responsive.directive.ts) I had to add `"strictPropertyInitialization": false` in `tsconfig.json` so please check that file too. – knoefel Jul 08 '21 at 10:25
  • @knoefel error in `mapTo`. TS2345: Argument of type 'OperatorFunction<[boolean, boolean], (HTMLTableRowElement | HTMLCollectionOf | null)[]>' is not assignable to parameter of type 'OperatorFunction<[boolean, boolean], [HTMLTableRowElement, HTMLCollectionOf]>'. Type '(HTMLTableRowElement | HTMLCollectionOf | null)[]' is not assignable to type '[HTMLTableRowElement, HTMLCollectionOf]'. Target requires 2 element(s) but source may have fewer. 54 mapTo([this.thead.rows.item(1), this.tbody.rows]), – klaydze Jul 08 '21 at 10:55
  • Did you use the updated code in `mat-table-responsive.directive.ts` from the new stackblitz demo i linked in my previous comment? Because your error shows that you use the version without strict mode: check the last line in your last comment `54 mapTo([this.thead.rows.item(1), this.tbody.rows])`. – knoefel Jul 08 '21 at 11:02
  • 1
    @knoefel wow! this fixed everything! thank you it's a very nice directive! – klaydze Jul 09 '21 at 01:39
  • 1
    Changed my acceptance to this more popular answer, as it does provide the basics that I need. The old answer was that it was not supported by MDL with a GitHub issue pending. This coded answer goes well beyond what I expected as result, and the work alone deserves the credit, plus it does work for my use case. – Steven Scott Dec 13 '21 at 20:54
  • line: `columnNames: [...headRow.children].map(` error: `Type 'HTMLCollection' must have a '[Symbol.iterator]()' method that returns an iterator.` **:'(** Line: `rows: [...bodyRows].map(row => [...row.children])` Error: `Type 'HTMLCollectionOf | undefined' must have a '[Symbol.iterator]()' method that returns an iterator.`. I'm using angular 13 – Manuel Villarroel May 06 '22 at 02:24
  • 1
    @ManuelVillarroel you need to add the `"DOM.Iterable"` lib in `tsconfig.json`. Check the stackblitz demos, there you can find how to add it. [Here](https://stackoverflow.com/questions/57621104/type-htmlcollectionofhtmlcanvaselement-must-have-a-symbol-iterator-met) is also a stack overflow question regarding this subject. – knoefel May 06 '22 at 06:47
  • This is an extremely elegant solution, thank you for this! – Aaron Lavers Jul 25 '22 at 06:31
  • Just noticed that the header `` is staying in the old state, it doesn't transforming on mobiles. – borkafight Aug 10 '22 at 15:47
  • Ok, I got the problem: Eslint requires to add the "app" prefix to the directive `selector: '[matTableResponsive]'` and I did it. Once I removed this, it's working as a charm now! – borkafight Aug 10 '22 at 16:11
4

There is currently no way you can achieve this in MDL, Check out this issue on github.

I am having the same problem with my tables, but I guess I will have to make them responsive my self. There are a lot of tutorials out there & you should do a bit of research which table design would suit your responsive website the best.

Also, I think its worth checking out materializecss's responsive tables

realappie
  • 4,656
  • 2
  • 29
  • 38
  • Unfortunately, I have come to the same conclusion and moved to the Angular Grid instead. I also did some redesign to not need the tables the same way. The Materialize CSS is a nice thought to get somethings to work. – Steven Scott Oct 05 '15 at 15:38
  • 1
    I love Materialize as the defacto css framework for Material specification, but unfortunately, they never changed their grid and layout to use flexbox instead of floats. Not to mention that the project is dead; no commits in years, I believe – OzzyTheGiant Jan 26 '21 at 02:09
1

An easy solution is to wrap your table with a <div> with following styles:

width: 100%;
overflow-x: auto;
kozenka
  • 589
  • 4
  • 6
  • 1
    This isn't really a profound solution, but in regards to the complexity of other solutions, this is an elegant and viable solution for me. Thanks. – fonzane Nov 20 '22 at 06:46