Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Universal support for DimensionsHelper and ScrollbarHelper #1178

Merged
merged 28 commits into from
Dec 13, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
99d5710
universal fixes - support server overrides for scrollbar width, dimen…
earlyster Oct 12, 2017
f92a733
fix ( MouseEvent ) undefined error
earlyster Oct 16, 2017
4959f8f
fix MouseEvent for universal server
earlyster Oct 16, 2017
6f443b4
fix events issues
earlyster Oct 16, 2017
8b066fc
fixing universal event undefined error
earlyster Oct 16, 2017
a05b0bb
event issue universal
earlyster Oct 16, 2017
45be859
event issue universal
earlyster Oct 16, 2017
2aaaa21
Merge branch 'master' of https://github.com/swimlane/ngx-datatable
earlyster Oct 20, 2017
e215dcb
Fixing build and merging
earlyster Oct 20, 2017
d61b192
fix(Updating SSR universal to work):
earlyster Nov 3, 2017
adcfaae
Merge branch 'master' of https://github.com/earlyster/ngx-datatable
earlyster Nov 3, 2017
bd0056b
Merge branch 'master' of https://github.com/swimlane/ngx-datatable
earlyster Nov 3, 2017
54f1bcd
bugfix - ssr making ssr work correctly
earlyster Nov 3, 2017
a69fd9a
Merge branch 'master' of https://github.com/swimlane/ngx-datatable
earlyster Nov 6, 2017
00dc954
Uprev release post merge
earlyster Nov 6, 2017
148b1c9
Adding @SkipSelf to test using parent injector for service to handle …
earlyster Nov 9, 2017
adb47ba
Merge branch 'master' of https://github.com/swimlane/ngx-datatable
earlyster Dec 6, 2017
2739d3f
(npe) selected rows sometimes this.rows was null
earlyster Dec 7, 2017
2b067ff
chore(universal): Cleanup based on pull request review #1178
earlyster Dec 8, 2017
6dbed39
build(release): Update built files for npm usage from gitrepo
earlyster Dec 8, 2017
f0fa429
removing release files from master
earlyster Dec 8, 2017
6c86867
removing dead code
earlyster Dec 8, 2017
655f658
docs(universal) - Adding document on how to use default implementations
earlyster Dec 11, 2017
83ad726
docs(universal) - Adding document on how to use default implementations
earlyster Dec 11, 2017
c87e798
Merge branch 'master' of https://github.com/earlyster/ngx-datatable
earlyster Dec 11, 2017
3c3f5fc
docs(universal): Adding universal setup doc
earlyster Dec 11, 2017
ba8d448
Merge branch 'master' of https://github.com/swimlane/ngx-datatable
earlyster Dec 11, 2017
d53142d
fix(tests): Using By.css since with when using Renderer2 it creates a…
earlyster Dec 11, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions docs/universal/server-side-rendering.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@

To run ngx-datatable on a server using Angular Universal you must implement 2 services. Below are examples of these
services:

1. DimensionsHelper
2. ScrollbarHelper


If you take the default implementation of these 2 services they do not work on server for valid reasons. There is no viewport on server and there is no scrollbar on server. If you take these services as they are you will get an error like this:

```
this.document.createElement is not a function
at ScrollbarHelper.getWidth
```

```
ERROR TypeError: this.element.getBoundingClientRect is not a function
```

## Example Server side Dimensions helper

This service must send back the dimensions of the grid or fallback to a default width.

I am using the ideas about dimensions from client hints that I set on the signin page. http://httpwg.org/http-extensions/client-hints.html


```typescript
import { ScrollbarHelper } from '@swimlane/ngx-datatable';
import { Injectable, Inject } from '@angular/core';
import { Request } from 'express';
import { REQUEST } from '@nguniversal/express-engine/tokens';
import { DimensionsHelper } from '@swimlane/ngx-datatable';

@Injectable()
export class ServerDimensionsHelper extends DimensionsHelper {

constructor(@Inject(REQUEST) private request: Request) {
super();
}

getDimensions(element: Element): ClientRect {
const width = parseInt(this.request.cookies['CH-DW'], 10) || 1000;
const height = parseInt(this.request.cookies['CH-DH'], 10) || 800;

const adjustedWidth = width;
const adjustedHeight = height;


return {
height: adjustedHeight,
bottom: 0,
top: 0,
width: adjustedWidth,
left: 0,
right: 0
};
}
}
```


## Scrollbar helper

In this case I just returned the scrollbar width based on what I used for css styling

```typescript
import { ScrollbarHelper } from '@swimlane/ngx-datatable';
import { Injectable, Inject } from '@angular/core';
import { DOCUMENT } from '@angular/common';

@Injectable()
export class ServerScrollBarHelper extends ScrollbarHelper {
width: number;

constructor(@Inject(DOCUMENT) document) {
super(document);
this.width = 16; // use default value
}

getWidth(): number {
return this.width;
}
}
```

I then added these two services using @NgModule providers

```typescript
providers: [ {
provide: ScrollbarHelper,
useClass: ServerScrollBarHelper
},
{
provide: DimensionsHelper,
useClass: ServerDimensionsHelper
}]
```

7 changes: 7 additions & 0 deletions release/services/dimensions-helper.service.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* Gets the width of the scrollbar. Nesc for windows
* http://stackoverflow.com/a/13382873/888165
*/
export declare class DimensionsHelper {
getDimensions(element: Element): ClientRect;
}
26 changes: 26 additions & 0 deletions release/services/dimensions-helper.service.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions release/services/dimensions-helper.service.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions release/services/dimensions-helper.service.metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"__symbolic":"module","version":3,"metadata":{"DimensionsHelper":{"__symbolic":"class","decorators":[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"Injectable"}}],"members":{"getDimensions":[{"__symbolic":"method"}]}}}},{"__symbolic":"module","version":1,"metadata":{"DimensionsHelper":{"__symbolic":"class","decorators":[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"Injectable"}}],"members":{"getDimensions":[{"__symbolic":"method"}]}}}}]
6 changes: 3 additions & 3 deletions src/components/body/body-row.component.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import {
Component, Input, HostBinding, ElementRef, Output, KeyValueDiffers, KeyValueDiffer,
EventEmitter, HostListener, ChangeDetectionStrategy, ChangeDetectorRef, DoCheck
EventEmitter, HostListener, ChangeDetectionStrategy, ChangeDetectorRef, DoCheck, SkipSelf
} from '@angular/core';

import {
allColumnsByPinArr, columnsByPin, columnGroupWidths, columnsByPinArr, translateXY, Keys
} from '../../utils';
import { ScrollbarHelper } from '../../services';
import { MouseEvent, KeyboardEvent } from '../../events';
import { MouseEvent, KeyboardEvent, Event } from '../../events';

@Component({
selector: 'datatable-body-row',
Expand Down Expand Up @@ -122,7 +122,7 @@ export class DataTableBodyRowComponent implements DoCheck {

constructor(
private differs: KeyValueDiffers,
private scrollbarHelper: ScrollbarHelper,
@SkipSelf() private scrollbarHelper: ScrollbarHelper,
private cd: ChangeDetectorRef,
element: ElementRef) {
this._element = element.nativeElement;
Expand Down
12 changes: 7 additions & 5 deletions src/components/body/scroller.component.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
Component, Input, ElementRef, Output, EventEmitter, NgZone,
Component, Input, ElementRef, Output, EventEmitter, Renderer2, NgZone,
OnInit, OnDestroy, HostBinding, ChangeDetectionStrategy
} from '@angular/core';

Expand Down Expand Up @@ -36,17 +36,19 @@ export class ScrollerComponent implements OnInit, OnDestroy {
parentElement: any;
onScrollListener: any;

constructor(private ngZone: NgZone, element: ElementRef) {
constructor(private ngZone: NgZone, element: ElementRef, private renderer: Renderer2) {

this.element = element.nativeElement;
}

ngOnInit(): void {
// manual bind so we don't always listen
if (this.scrollbarV || this.scrollbarH) {
this.parentElement = this.element.parentElement.parentElement;
const renderer = this.renderer;
this.parentElement = renderer.parentNode(renderer.parentNode(this.element));
this.ngZone.runOutsideAngular(() => {
this.parentElement.addEventListener('scroll', this.onScrolled.bind(this));
});
this.parentElement.addEventListener('scroll', this.onScrolled.bind(this));
});
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/components/body/selection.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { SelectionType } from '../../types';
import { MouseEvent, KeyboardEvent } from '../../events';

export interface Model {
type: string;
type: string;
event: MouseEvent | KeyboardEvent;
row: any;
rowElement: any;
Expand Down
3 changes: 1 addition & 2 deletions src/components/datatable.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -396,9 +396,8 @@ function setupTest() {
function sortBy({ column }: { column: number }) {
const columnIndex = column - 1;
const headerCellDe = fixture.debugElement
.queryAll(By.directive(DataTableHeaderCellComponent))[columnIndex];
.queryAll(By.css('datatable-header-cell'))[columnIndex];
const de = headerCellDe.query(By.css('span:last-child'));

de.triggerEventHandler('click', null);
}

Expand Down
15 changes: 10 additions & 5 deletions src/components/datatable.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import {
HostListener, ContentChildren, OnInit, QueryList, AfterViewInit,
HostBinding, ContentChild, TemplateRef, IterableDiffer,
DoCheck, KeyValueDiffers, KeyValueDiffer, ViewEncapsulation,
ChangeDetectionStrategy, ChangeDetectorRef
ChangeDetectionStrategy, ChangeDetectorRef, SkipSelf
} from '@angular/core';

import {
forceFillColumnWidths, adjustColumnWidths, sortRows,
setColumnDefaults, throttleable, translateTemplates
} from '../utils';
import { ScrollbarHelper } from '../services';
import { ScrollbarHelper, DimensionsHelper } from '../services';
import { ColumnMode, SortType, SelectionType, TableColumn, ContextmenuType } from '../types';
import { DataTableBodyComponent } from './body';
import { DatatableGroupHeaderDirective } from './body/body-group-header.directive';
Expand Down Expand Up @@ -605,7 +605,7 @@ export class DatatableComponent implements OnInit, DoCheck, AfterViewInit {
* Returns if all rows are selected.
*/
get allRowsSelected(): boolean {
let allRowsSelected = (this.selected.length === this.rows.length);
let allRowsSelected = (this.rows && this.selected && this.selected.length === this.rows.length);

if (this.selectAllRowsOnPage) {
const indexes = this.bodyComponent.indexes;
Expand Down Expand Up @@ -636,7 +636,8 @@ export class DatatableComponent implements OnInit, DoCheck, AfterViewInit {
_columnTemplates: QueryList<DataTableColumnDirective>;

constructor(
private scrollbarHelper: ScrollbarHelper,
@SkipSelf() private scrollbarHelper: ScrollbarHelper,
@SkipSelf() private dimensionsHelper: DimensionsHelper,
private cd: ChangeDetectorRef,
element: ElementRef,
differs: KeyValueDiffers) {
Expand Down Expand Up @@ -668,6 +669,10 @@ export class DatatableComponent implements OnInit, DoCheck, AfterViewInit {

// this has to be done to prevent the change detection
// tree from freaking out because we are readjusting
if (typeof requestAnimationFrame === 'undefined') {
return;
}

requestAnimationFrame(() => {
this.recalculate();

Expand Down Expand Up @@ -808,7 +813,7 @@ export class DatatableComponent implements OnInit, DoCheck, AfterViewInit {
*
*/
recalculateDims(): void {
const dims = this.element.getBoundingClientRect();
const dims = this.dimensionsHelper.getDimensions(this.element);
this._innerWidth = Math.floor(dims.width);

if (this.scrollbarV) {
Expand Down
8 changes: 6 additions & 2 deletions src/datatable.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,18 @@ import {
DraggableDirective
} from './directives';

import { ScrollbarHelper } from './services';
import {
ScrollbarHelper,
DimensionsHelper
} from './services';

@NgModule({
imports: [
CommonModule
],
providers: [
ScrollbarHelper
ScrollbarHelper,
DimensionsHelper
],
declarations: [
DataTableFooterTemplateDirective,
Expand Down
11 changes: 6 additions & 5 deletions src/directives/resizeable.directive.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
Directive, ElementRef, HostListener, Input, Output, EventEmitter, OnDestroy, AfterViewInit
Directive, ElementRef, HostListener, Input, Output, EventEmitter, OnDestroy, AfterViewInit, Renderer2
} from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
Expand All @@ -24,15 +24,16 @@ export class ResizeableDirective implements OnDestroy, AfterViewInit {
subscription: Subscription;
resizing: boolean = false;

constructor(element: ElementRef) {
constructor(element: ElementRef, private renderer: Renderer2) {
this.element = element.nativeElement;
}

ngAfterViewInit(): void {
const renderer2 = this.renderer;
if (this.resizeEnabled) {
const node = document.createElement('span');
node.classList.add('resize-handle');
this.element.appendChild(node);
const node = renderer2.createElement('span');
renderer2.addClass(node, 'resize-handle');
renderer2.appendChild(this.element, node);
}
}

Expand Down
1 change: 1 addition & 0 deletions src/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ declare let global: any;
/* tslint:disable:variable-name */
export const MouseEvent = (global as any).MouseEvent as MouseEvent;
export const KeyboardEvent = (global as any).KeyboardEvent as KeyboardEvent;
export const Event = (global as any).Event as Event;
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './datatable.module';
export * from './types';
export * from './components';
export * from './services';
14 changes: 14 additions & 0 deletions src/services/dimensions-helper.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Inject, Injectable } from '@angular/core';

/**
* Gets the width of the scrollbar. Nesc for windows
* http://stackoverflow.com/a/13382873/888165
*/
@Injectable()
export class DimensionsHelper {

getDimensions(element: Element): ClientRect {
return element.getBoundingClientRect();
}

}
1 change: 1 addition & 0 deletions src/services/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './scrollbar-helper.service';
export * from './dimensions-helper.service';
26 changes: 26 additions & 0 deletions src/utils/facade/browser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*tslint:disable */
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

/**
* JS version of browser APIs. This library can only run in the browser.
*/
var win = (typeof window !== 'undefined' && window) || <any>{};

export { win as window };
export var document = win.document;
export var location = win.location;
export var gc = win['gc'] ? () => win['gc']() : (): any => null;
export var performance = win['performance'] ? win['performance'] : null;
export const Event = win['Event'];
export const MouseEvent = win['MouseEvent'];
export const KeyboardEvent = win['KeyboardEvent'];
export const EventTarget = win['EventTarget'];
export const History = win['History'];
export const Location = win['Location'];
export const EventListener = win['EventListener'];