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

Table layout #146

Draft
wants to merge 12 commits into
base: master
Choose a base branch
from
34 changes: 21 additions & 13 deletions demo/app/samples/test.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,25 @@
<br><br>
</div>

<div class="viewport" style="height: 600px" id="my-viewport">
<div
*uiScroll="let item of datasource"
(click)="doToggleItem(item)"
[style.height]="item.height + 'px'"
>
<app-samples-test-inner>
{{item.text}}
<span [style.color]="item.color">
{{item.isSelected ? '********' : ''}}
</span>
</app-samples-test-inner>
</div>
<div class="viewport tableFixHead" style="height: 600px;" id="my-viewport">
<table>
<thead><th>data</th></thead>
<tbody>
<tr class="temp"
*uiScroll="let item of datasource; table:true"
(click)="doToggleItem(item)"
>
<td>
<div [style.height]="item.height + 'px'">
<app-samples-test-inner>
{{item.text}}
<span [style.color]="item.color">
{{item.isSelected ? '********' : ''}}
</span>
</app-samples-test-inner>
</div>
</td>
</tr>
</tbody>
</table>
</div>
17 changes: 17 additions & 0 deletions demo/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -340,3 +340,20 @@ li.L1,li.L3,li.L5,li.L7,li.L9 { }
pre .typ, code .typ { font-weight: bold }
pre .tag, code .tag { font-weight: bold; color: #204a87; }
}

table tr.temp {
color: red;
position: fixed;
left: -99999px;
}

table, caption, tbody, tfoot, thead, tr, th, td {
padding: 0;
}

.tableFixHead { overflow-y: auto; height: 100px; }
.tableFixHead thead th { position: sticky; top: 0; }

/* Just common table stuff. Really. */
table { border-collapse: collapse; }
th { background:#eee; }
8 changes: 7 additions & 1 deletion src/component/processes/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,18 @@ export default class Render {
}
scroller.innerLoopSubscriptions.push(
scroller.bindData().subscribe(() => {
const elts = scroller.viewport.element.querySelectorAll(`tr:not([data-sid])`);
if (elts && elts.length && elts.length === scroller.state.fetch.items.length) {
elts.forEach((elt, index) => {
(<any>elt).dataset['sid'] = scroller.state.fetch.items[index].nodeId;
});
if (Render.processElements(scroller)) {
workflow.call({
process: Process.render,
status: render.noSize ? ProcessStatus.done : ProcessStatus.next,
payload: { noClip: clip.noClip }
});
} else {
} } else {
workflow.call({
process: Process.render,
status: ProcessStatus.error,
Expand Down Expand Up @@ -59,6 +64,7 @@ export default class Render {
item.element = element as HTMLElement;
item.element.style.left = '';
item.element.style.position = '';
item.element.classList.remove('temp');
item.invisible = false;
item.setSize();
buffer.cacheItem(item);
Expand Down
77 changes: 56 additions & 21 deletions src/ui-scroll.component.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,76 @@
import {
Component, OnInit, OnDestroy,
TemplateRef, ElementRef,
ChangeDetectionStrategy, ChangeDetectorRef
ChangeDetectionStrategy, ChangeDetectorRef,
ViewChild
} from '@angular/core';

import { Workflow } from './component/workflow';
import { IDatasource } from './component/interfaces/index';
import { Datasource } from './component/classes/datasource';
import { Item } from './component/classes/item';

const tableTemplate = `
<ng-container *ngIf="isTable">
<tr data-sid="backward">
<td><div data-padding-backward></div></td>
</tr>
<ng-template
*ngFor="let item of items"
[ngTemplateOutlet]="template"
[ngTemplateOutletContext]="{
$implicit: item.data,
index: item.$index,
odd: item.$index % 2,
even: !(item.$index % 2)
}"></ng-template>
<tr data-sid="forward">
<td><div data-padding-forward></div></td>
</tr>
</ng-container>
`;

const commonTemplate = `
<ng-container *ngIf="!isTable">
<div data-padding-backward></div>
<div
*ngFor="let item of items"
[attr.data-sid]="item.nodeId"
[style.position]="item.invisible ? 'fixed' : null"
[style.left]="item.invisible ? '-99999px' : null">
<ng-template
[ngTemplateOutlet]="template"
[ngTemplateOutletContext]="{
$implicit: item.data,
index: item.$index,
odd: item.$index % 2,
even: !(item.$index % 2)
}"></ng-template>
</div>
<div data-padding-forward></div>
</ng-container>
`;

/* tslint:disable:component-selector */
@Component({
selector: '[ui-scroll]',
selector: 'tbody [ui-scroll]',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div data-padding-backward></div>
<div
*ngFor="let item of items"
[attr.data-sid]="item.nodeId"
[style.position]="item.invisible ? 'fixed' : null"
[style.left]="item.invisible ? '-99999px' : null">
<ng-template
[ngTemplateOutlet]="template"
[ngTemplateOutletContext]="{
$implicit: item.data,
index: item.$index,
odd: item.$index % 2,
even: !(item.$index % 2)
}"></ng-template>
</div>
<div data-padding-forward></div>`
template: `<ng-template #uiScrollTemplateRef>
${commonTemplate}
${tableTemplate}
</ng-template>`
})
export class UiScrollComponent implements OnInit, OnDestroy {

@ViewChild('uiScrollTemplateRef', { static: true })
public uiScrollTemplateRef: TemplateRef<any>;

// these should come from the directive
public version: string;
public template: TemplateRef<any>;
public datasource: IDatasource | Datasource;
public isTable: boolean;
public parentElement: HTMLElement;

// the only template variable
public items: Item[] = [];
Expand All @@ -46,11 +80,12 @@ export class UiScrollComponent implements OnInit, OnDestroy {

constructor(
public changeDetector: ChangeDetectorRef,
public elementRef: ElementRef) { }
public elementRef: ElementRef
) { }

ngOnInit() {
this.workflow = new Workflow(
this.elementRef.nativeElement,
this.parentElement, // this.elementRef.nativeElement,
this.datasource,
this.version,
(items: Item[]) => {
Expand Down
13 changes: 10 additions & 3 deletions src/ui-scroll.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { IDatasource } from './component/interfaces/datasource';
@Directive({ selector: '[uiScroll][uiScrollOf]' })
export class UiScrollDirective implements OnInit {
private datasource: IDatasource;
private isTable: boolean;

constructor(
private templateRef: TemplateRef<any>,
Expand All @@ -19,13 +20,19 @@ export class UiScrollDirective implements OnInit {
this.datasource = datasource;
}

@Input() set uiScrollTable(value: any) {
this.isTable = !!value;
}

ngOnInit() {
const compFactory = this.resolver.resolveComponentFactory(UiScrollComponent);
const componentRef = this.viewContainer.createComponent(
compFactory, void 0, this.viewContainer.injector
);
const componentRef = compFactory.create(this.viewContainer.injector);
componentRef.instance.datasource = this.datasource;
componentRef.instance.template = this.templateRef;
componentRef.instance.isTable = this.isTable;
componentRef.instance.version = version;
componentRef.instance.parentElement = this.templateRef.elementRef.nativeElement.parentElement;
this.viewContainer.createEmbeddedView(componentRef.instance.uiScrollTemplateRef);
setTimeout(() => componentRef.changeDetectorRef.detectChanges()); // 😢
Copy link
Owner Author

@dhilt dhilt Feb 28, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@markdBC I admire your deep insight of the Angular internal things and very grateful for your explanation regarding ngOnInit. As you can see I had to wrap changes detection into another setTimeout, the problem is that the onInit hook is being triggered (with manual approach) before the initial content of the component is rendered. Do you know, what can be done here? I tried to replace OnInit with AfterViewInit (the latest init hook), but it also runs too early.

The initialization, I need to run on the components first render, includes DOM parsing, in particular, the Scroller tries to find elements needed for virtualization. And immediate initialization (before DOM gets necessary elements during first render) just fails.

Also, calling componentRef.changeDetectorRef.detectChanges() seems equal to calling componentRef.instance.ngOnInit(), but I agree this is better than calling this.ngOnInit() on the component's constructor.

That's what we have now... And it would be great to dig a little dipper and find a way to catch the time of the first render properly. The last thing I want to add is that the demo starts working, not perfect, but something can be seen running localhost:4200/#/test

}
}