AngularJS: Fixed-header scrollable table
For the last couple projects I’ve worked on, there has been a need for a Bootstrap-based scrollable table with a fixed header. A pretty common requirement, I’d say. Common enough to inspire quite a few solutions in my current favorite client-side framework, AngularJS.
At the time, there weren’t many options, so I wrote my own directive with a few requirements in mind:
- Static table height. This is to allow room for more content on the page regardless of how many rows the table has.
- Pretty column headers. Often, there isn’t enough screen real estate to display the full text for each column header, so when one is too long, truncate it and add ellipsis. None of the existing Angular implementations handle this well, and it turned out to not be an easy feature to implement.
- Sortable headers. Specify an attribute to sort on and an optional comparator function.
- Scroll-to-row. Specify an identifier for each row, and the directive will respond to “rowSelected” events, scrolling to display the row.
Everything else was left alone, so that you still have full control over the (transcluded) HTML and can style it or add event handlers wherever you please.
The CSS was inspired by: http://salzerdesign.com/blog/?p=191
Here is the directive in action, complete with sortable headers:
As with any software, some tradeoffs were made to meet my requirements. Here are some of them:
- Header height is fixed. Overly-long headers are truncated. I typically add a title=”” attribute so users can still see the full text.
- Headers are no longer included in the calculation of column width. The best workaround here is to add min-widths to table cells.
- Dynamically adding row content may shift columns so that the header is no longer accurate. There is a watch attribute required by the directive that must be updated in order for the column headers to re-render.
Edit Jan 2, 2014: The code is now on GitHub, for your forking pleasure (ahem).
Comments are closed.
This is awesome work. I thought I had this problem solved, but this shows me that I was just scratching the surface. I’m also learning how to write better directives from reading your code.
Thanks! It has evolved dramatically over the months. The biggest improvement (IMO) was adding inter-directive communication between the main table and headers. Also, I’d be keen to find a better method for discovering when the table has rendered.
Very nice plugin, unfortunately it didn’t work for me. Have to spend some more time figuring out what is going on. I think the “watch” issue had some issues.
Hi Robert, if you put your code into a jsfiddle or plunkr I could take a look.
Hi. Thanks for this blog. Excelent!
Now, I have some issues. First. What if I don´t want a header (), and none “headerSpacer”?
Is there some kind of key word to disable it?
Thanks again.
Well, if you don’t want a header, I suggest another plugin! Or just use a plain ‘ol <table>. Alternatively, the template is just HTML, so you can remove the <div class=”headerSpacer”> if you wish.
Great work!
Unfortunately your directive fails if the header contains more than 1 row, see http://jsfiddle.net/alalonde/BrTzg/
I will have a look at the code now to see if I can fix the problem myself, but your help is of course appreciated 🙂
Hi Peter,
Yes, I personally never needed more than one row, but I don’t think it would be too difficult to modify the CSS/JS to support more than one. Let me know if you figure out a good solution.
Unfortunately not so easy as it might seem. Some static stuff in the CSS will have to become dynamic. I couldn’t get it working 🙁
I am trying to use your module. Do you have a usage document on what I need to do to install and use your module? I am new to angular btw.
Hi Michael, I added usage instructions to the GitHub page.
Hi,
Is there also a version for Bootstrap 3.x?
because i think it does not work correct there.
br alex
Yes, the latest version is 3.x compatible. See the GitHub project page.
Thanks for this table – it seems to work really well.
Is there an easy way to set the initial sort column and direction as the records appear unsorted until you click on a direction arrow in the column header to sort by that column.
Hi Evahn, I don’t believe the directive, when loaded, indicates an initially sorted column. You could of course provide sorted data to the ‘rows’ attribute. Please file an issue on Github if you would like to see this functionality added.
Hi Alec,
Good Job!!
If the table head is fixed its working excellent!! but if i have two static column and 5 dynamic column which i am looping for the header title then its not coming perfect as i am expecting.
Could you please help me for the same?
Thanks
Hi Sanjay,
If you put your code online with Plunkr or JSFiddle I could take a look more easily. Or, if you are sure it is a bug, please create an issue on Github. Thanks
Hi Alec, nice job..thanks!!
We like the resizing of the colums and truncate feature of the header, but can it also be disabled? We need the header ‘names’ to be fully visible all the time, but scrollable of course 🙂
Any help would be appreciated, thanks!
Hi Willem,
The display of the headers is just simple CSS, so just adjust or override as necessary. See
.scrollArea table .th-inner
.hi Alec ,
I want to fixed the header and first column,is this possible?
Fixed header: yes; first column: no, that’s out of scope for us.
Hello Alec,
Thanks for your work !
Just to precise that it correctly merges with ng-table (I had a doubt with watch=”$data” ^^) except regarding the pagination which is included in the scrollable container.
Hi Alec,
Great work!
I was unable to use ‘resizing’ directive.
How can use the resizing of the colums?
Best regards,
Sergey
Hi Sergey, simply add the ‘resizable’ attribute to the
scrollable-table
element to enable resizing.Awesome tutorial!
How can I add a fixed footer into it?
Hi Alec,
Thanks for the great job,
I used your module and il works perfectly with Chrome and IE but nothing is displayed in Firefox.
Do you have tested it on different browsers?
Thnaks.
Hi Alec,
Great work! It helped me a lot.
How do I wrap the text in headers? I could not find a way to wrap the text for header, all I can get to work is ellipsis. Is there a way that I can wrap the text into a separate line for header?
I am not able increase the height of the scrollable table. It s always taking around 310 px height. I want the grid should expand the containers height and show the scroll after that.Can you tell me if it can be achieved through any workaround ?
Hi Sundar,
The height and overflow:scroll are set in the CSS.
Hey Alee,
I have 2 issues.
a. I have a dropdown on table header. On click the dropdown shows up however it hides behind table body. Tried with overflows and positions, no use though
b. I need to override title attribute on table header.
Any suggestion will be helpful
Hi,
How to control the direction right to left (rtl) for header, it is applied for body but header not.
Thanking You,
Dear,
the direction working fine when it is not resizable option, and the problem only when resizable
Thanking you,
Hi Alec,
Great work,
I used your module and it works perfectly with Chrome and IE but not working perfectly in Firefox.
Do you have any solution ?
Thanks.
This should be fixed in the latest version. See the GitHub repo.
I love the module, but I’m having some problems getting it to work, even though I seem to have followed the code pretty precisely.
https://s3.amazonaws.com/uploads.hipchat.com/45588/778139/Lobwe0lCyGUHpZn/upload.png
As you can see in that image, the header row columns are showing in the data area, and not in their (visual) header row – but the sort arrow IS in that row, which is kind of funny.
Aloha Alec,
I am using your module. I am running into a situation, where u might give me some quick inputs.
Situation here is I need server side sorting on my table. When I click on the table header, it should make service request to get sorted data.
The moment I use the column resizer to resize column width, it fires the “th”s data-ng-click method.
{{column.ColumnLabel}}
vm.tableHeaderClick(column) method is defined in my controller. This gets called as and when I do a column resize using column resize handler.
How can I stop this event propagation from column resizing to this click event ?
Thanks
Srkanth
Hi Alec,
Great work. I’m using your table in my current project and I’m very satisfied.
Thank you!
Do you have any suggestions what would be the best way to make height of the table become dynamic ?
Best wishes from Bosnia…
This solution works fine for me
$(‘.scrollableContainer’).height(h);
Regards,
Fehim.
Scrollable Tables doesn’t work with modals.
How can I fix this?
Please create a jsfiddle displaying the issue.