swimlane / ngx-datatable

✨ A feature-rich yet lightweight data-table crafted for Angular
http://swimlane.github.io/ngx-datatable/
MIT License
4.63k stars 1.68k forks source link

Render on server e.g. integrate into angular universal? #173

Open crebuh opened 7 years ago

crebuh commented 7 years ago
[ x] feature request
[ x] support request => Please do not submit support request here

Is it possible to use this component with project like angular universal and render the tables already on the server. And on the client using the sorting and filtering stuff? I tried to integrate it into my universal project without success, because the component of course is accessing the window document object.

Any solutions or ideas on that or is it not supposed to do this?

amcdnl commented 7 years ago

I haven't had a need for universal support but happy to accept a PR for it. I'm sure its got some minor tweaks and it would work.

crebuh commented 7 years ago

of course I would make a PR, I'm just a bit new to the universal environment. Are there any sample implementations for similar scenarios or do you have any advice how to shim/inject specific functionality depending on the environment?

PatrickJS commented 7 years ago

I can help with some advice on this

amcdnl commented 7 years ago

@gdi2290 - Ears are wide open :)

Quixomatic commented 7 years ago

@amcdnl @gdi2290 any updates on this issue? since document is not defined on the server side it throws errors when using with angular-universal

amcdnl commented 7 years ago

@Quixomatic - where is the document reference breaking it?

Quixomatic commented 7 years ago

/node_modules/angular2-data-table/release/index.umd.js:285 var testStyle = document.createElement('div').style; ^

ReferenceError: document is not defined at /home/ubuntu/workspace/node_modules/angular2-data-table/release/index.umd.js:285:17

Quixomatic commented 7 years ago

@amcdnl any thoughts on this?

scotteby commented 7 years ago

I would love to use this data table, it looks fantastic, but I'm getting the same error. Is there anyway I can implement a workaround to get your data table working in my application?

Thanks.

Quixomatic commented 7 years ago

@scotteby I was able to get this working with angular-universal with a bit of a temporary hack:

In the release folder if you open index.js and replace the following lines:

line 3199: var testStyle = typeof document != 'undefined' ? document.createElement('div').style : [];

line 3203: var styles = typeof window != 'undefined' ? window.getComputedStyle(document.documentElement, '') : [];

line 3204: var pre = typeof window != 'undefined' ? (Array.prototype.slice.call(styles).join('').match(/-(moz|webkit|ms)-/))[1] : null;

line 3205: var dom = typeof window != 'undefined' ? ('WebKit|Moz|MS|O').match(new RegExp('(' + pre + ')', 'i'))[1] : null;

line 3210: js: typeof window != 'undefined' ? pre[0].toUpperCase() + pre.substr(1): ''

line 3581: var ua = typeof window != 'undefined' ? window.navigator.userAgent : null;

tldr; I added conditions that check to see if global objects like window/document are undefined, if so don't do the normal stuff, do the angular-universal safe stuff instead.

Quixomatic commented 7 years ago

I also added nested my component inside of a div with a *ngIf="isBrowser"

isBrowser being a boolean variable that you can import from angular-universal that will not render the datatable component until you're loading things on the client side.

amcdnl commented 7 years ago

@gdi2290 Is there better ways to get reference to the window using the renderer or something?

Quixomatic commented 7 years ago

@amcdnl Think they're working on implementing jsdom into the server side of things to make all of this easier.

scotteby commented 7 years ago

Thanks @Quixomatic , this works great in our app now. Really appreciate the quick turnaround on providing a fix for us.

By the way, I did have to set the isBrowser flag to true in the ngOnInit method inside my component to get the table to render.

import { isBrowser } from 'angular2-universal';

export class MyComponent {

 isBrowser;

 ngOnInit()
    {
        this.isBrowser = true;
    }
}
Quixomatic commented 7 years ago

@scotteby this function worked fine for me after importing isBrowser into a shared service, would work importing into a component and defining the function there as well.

    isBrowser () {
        return isBrowser
    }

Then my *ngIf="isBrowser()"

scotteby commented 7 years ago

Thanks for the suggestion, that does make it a little easier to implement.

andreschort commented 7 years ago

I'm trying to use the isBrowser workaround but I get the exception by just importing NgxDatatableModule into my app module. Any ideas on what am I missing?

csy9 commented 7 years ago

I am having some trouble resolving what I believe to be a related issue, while trying to update from angular2-data-table (1.7.0) to ngx-datatable (5.0.0). Still using universal. Changing index.js to check for undefined document/window as shown earlier has fixed the "document/window not defined" exceptions, but now I am stuck on a similar exception:

ReferenceError: MouseEvent is not defined at ...\release\index.js:1301:42

Any advice on how to finish patching index.js for the new version would be greatly appreciated!

Crewski commented 7 years ago

@andreschort I am getting the same issue. Simply importing NgxDatatableModule is causing the exception.

rezidual commented 7 years ago

@amcdnl are there any updates regarding support for Angular Universal? This is an amazing component and it would be great to be able to leverage the server-side rendering capabilities of Universal for improved performance.

amcdnl commented 7 years ago

I believe this is working in latest. Can anyone confirm? We aren't manipulating any dom or using document directly anymore.

scotteby commented 7 years ago

I just tested this using ngx-datatable 8.0.0 with Angular 4.0.2 and still get an error when it tries to render on the server.

Exception: Call to Node module failed with error: Prerendering failed because of error: ReferenceError: document is not defined at Object../src/utils/prefixes.ts

scotteby commented 7 years ago

I just went through the entire code and got it working with Universal server side rendering, but had to add a check for all the undefined Window, Document, MouseEvent, and Keyboard event references in the following files:

prefixes.ts translate.ts elm-from-point.ts body-cell.component.ts body-row-wrapper.component.ts long-press.directive.ts resizeable.directive.ts

Is it too much of a hack to just add those undefined checks into the source?

amcdnl commented 7 years ago

Can you PR that back plz?? @scotteby

earlyster commented 6 years ago

I was trying to render ngx-datatable using ng4 and noticed issues with 2 things .. scrollBar width .. I found a way to use DI to replace service (with manual changes locally) to get the scrollbar width based on server param / user agent. Next I am running into issues with DataTable.recalculateDims trying to use

        var dims = this.element.getBoundingClientRect();

This is causing error in server side rendering

It looks like width and height of grid element must be determined up front for server side rendering to work. I am thinking I can separate this into a service and DI to inject the expected dimensions if client passes the viewport height / width as a server param.

Thoughts?

And thank you for open sourcing this component

wizarrc commented 6 years ago

@earlyster one thing I noticed with scrollbar width is that they are browser specific. You could very well set the width to the most popular browser and adjust after the server rendering if it's not correct. Another would be for the server side rendering to know which browser it's talking to cough agent string, and have a pre-computed width for each vendor, with a default fall-through for unknown.

Another approach altogether is to virtualize the scrollbar and make it a theme instead, allowing the developer to pick how the scrollbar looks.

manzonif commented 6 years ago

Hi, I get this error message in SSR:

 this.document.createElement is not a function
          at ScrollbarHelper.getWidth
dllabs commented 6 years ago

@earlyster Did you solve your issues with var dims = this.element.getBoundingClientRect();? I'm hitting up against this error too, and I'd be happy with your solution where the viewport width is passed as a server param. (As for viewport height, my table just needs to be as long as it needs to be given the width and the dataset length. I guess I could get away with just passing some arbitrarily very long viewport height?) thanks

earlyster commented 6 years ago

@dllabs - sorry for delay in response. Yes I have forked ngx-datatable and then injected different implementations for server of ScrollbarHelper.getWidth -- in my server implementation I have used a cookie approach where client viewport info is provided. I was originally thinking of user agent approach but there are way too many user agents so cookie. I took the same idea that is used for responsive images -- https://github.com/igrigorik/http-client-hints.

earlyster commented 6 years ago

I have created a pull request here - https://github.com/swimlane/ngx-datatable/pull/1178

devparag commented 6 years ago

Please look into this issue..#1038

devparag commented 6 years ago

@amcdnl Please look into this issue..#1038