- Display of data elements by time order.
- Scrolling down triggers month change in upper bar.
- Clicking on a month triggers display of related elements
Prerequisites:
- @angular/material v8.2.3
1. Component - html
<div class="element-list " fxLayout="column">
<div class='month-navigation'>
<a class='monthView' *ngFor="let monthName of months; let i = index"
(click)="monthClickAction(i)" [style.left]="defineMoveToLeft(i)">
{{monthName}}
</a>
</div>
<cdk-virtual-scroll-viewport mat-list itemSize="60" minBufferPx="240" maxBufferPx="480"
class="transaction-scrolling-viewport" role="list" (scrolledIndexChange)=scrollIndexChangeAction($event) fxFlex="grow">
<!-- See step 5 -->
<jhi-element-entry *cdkVirtualFor="let element of elementsToShow"
[icon]="'grade'" [date]="element.date" [amount]="element.amount"
[mainText]="element.elementName">
</jhi-element-entry>
<div class="load-item" >
<div class="content">
<div class="text">Loading elements..</div>
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
</div>
</div>
</cdk-virtual-scroll-viewport>
</div>
<div class='month-navigation'>
<a class='monthView' *ngFor="let monthName of months; let i = index"
(click)="monthClickAction(i)" [style.left]="defineMoveToLeft(i)">
{{monthName}}
</a>
</div>
<cdk-virtual-scroll-viewport mat-list itemSize="60" minBufferPx="240" maxBufferPx="480"
class="transaction-scrolling-viewport" role="list" (scrolledIndexChange)=scrollIndexChangeAction($event) fxFlex="grow">
<!-- See step 5 -->
<jhi-element-entry *cdkVirtualFor="let element of elementsToShow"
[icon]="'grade'" [date]="element.date" [amount]="element.amount"
[mainText]="element.elementName">
</jhi-element-entry>
<div class="load-item" >
<div class="content">
<div class="text">Loading elements..</div>
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
</div>
</div>
</cdk-virtual-scroll-viewport>
</div>
2. Component - ts
import {Component, Input, OnInit, ViewChild} from '@angular/core';
import {FormBuilder} from '@angular/forms';
import {IElement} from '../model/your-element.model';
import {CdkVirtualScrollViewport} from '@angular/cdk/scrolling';
import {YourService} from '../services/yourApp.service';
import {HttpErrorResponse, HttpResponse} from '@angular/common/http';
import {HttpPage} from '../model/page.model';
@Component({
selector: 'jhi-element-list',
templateUrl: './element-list.component.html',
styleUrls: ['./element-list.component.scss']
})
export class ElementListComponent implements OnInit {
@Input()
public showMonthBar = true;
// See step 7
private elements: IElement[] = [];
public elementsToShow: IElement[] = [];
// See step 4 for this page model
private page: HttpPage = {number: 0, size: 40, totalPages: 0, totalElements: 0};
private readonly curMonth: number;
private readonly curYear: number;
private activeYear: number;
indexOfActiveMonth: number;
private lastIndex = 0;
@ViewChild(CdkVirtualScrollViewport, {static: true})
scrollViewPort!: CdkVirtualScrollViewport;
months = [
'january',
'february',
'march',
'april',
'may'
];
constructor(
private fb: FormBuilder,
private yourService: YourService
) {
const date = new Date();
this.curMonth = date.getMonth();
this.curYear = date.getFullYear();
this.activeYear = date.getFullYear();
this.indexOfActiveMonth = date.getMonth();
}
ngOnInit(): void {
this.getElements(0, false);
}
getElements(pageNumber: number, doIncrement: boolean): void {
// See step 6
this.yourService.getElementsByPageAndSize(pageNumber, this.page.size).subscribe(
(res: HttpResponse<any>) => {
if (doIncrement) {
// Your API must return paginated data - see this tutorial
this.elements = this.elements.concat(res.body._embedded.elementDTOList);
} else {
this.elements = res.body._embedded.elementDTOList;
}
this.page = res.body.page;
this.elements = this.elements.sort((t1, t2) => t2.date.getTime() - t1.date.getTime());
this.elementsToShow = this.elements;
},
(error: HttpErrorResponse) => {
// handle error...
}
);
}
scrollIndexChangeAction(index: number): void {
if (this.elements[index] === undefined) return;
this.updateMonthByIndex(this.elements[index].date.getMonth());
// Load on down scroll event
if (
this.lastIndex < index && this.scrollViewPort.measureScrollOffset('bottom') < 50) {
this.loadNextElementsBatch();
}
this.lastIndex = index;
}
monthClickAction(index: number): void {
let compareYear = this.activeYear;
if (index === 11) {
compareYear--;
}
if (index === 0) {
compareYear++;
}
if (compareYear === this.curYear && index > this.curMonth) return;
const viewportIndex = this.elements.findIndex(el => el.date.getMonth() === index);
if (viewportIndex === -1) {
this.loadNextElementsBatch();
return;
}
this.scrollViewPort.scrollToIndex(viewportIndex, 'smooth');
}
updateMonthByIndex(index: number): void {
if (index === 11 && this.indexOfActiveMonth === 0) {
this.activeYear--;
}
if (index === 0 && this.indexOfActiveMonth === 11) {
this.activeYear++;
}
this.indexOfActiveMonth = index;
}
loadNextElementsBatch(): void {
this.scrollViewPort.checkViewportSize();
setTimeout(() => this.scrollViewPort.scrollTo({bottom: 0, behavior: 'smooth'}), 25);
this.getElements(this.page.number + 1, true);
}
defineMoveToLeft(index: number): string {
if (this.indexOfActiveMonth === 0 && index === this.months.length - 1) {
return '0';
}
if (this.indexOfActiveMonth === this.months.length - 1 && index === 0) {
return '66%';
}
if (this.indexOfActiveMonth === undefined) {
return 33 * index + '%';
}
return 33 * (index - this.indexOfActiveMonth + 1) + '%';
}
}
import {FormBuilder} from '@angular/forms';
import {IElement} from '../model/your-element.model';
import {CdkVirtualScrollViewport} from '@angular/cdk/scrolling';
import {YourService} from '../services/yourApp.service';
import {HttpErrorResponse, HttpResponse} from '@angular/common/http';
import {HttpPage} from '../model/page.model';
@Component({
selector: 'jhi-element-list',
templateUrl: './element-list.component.html',
styleUrls: ['./element-list.component.scss']
})
export class ElementListComponent implements OnInit {
@Input()
public showMonthBar = true;
// See step 7
private elements: IElement[] = [];
public elementsToShow: IElement[] = [];
// See step 4 for this page model
private page: HttpPage = {number: 0, size: 40, totalPages: 0, totalElements: 0};
private readonly curMonth: number;
private readonly curYear: number;
private activeYear: number;
indexOfActiveMonth: number;
private lastIndex = 0;
@ViewChild(CdkVirtualScrollViewport, {static: true})
scrollViewPort!: CdkVirtualScrollViewport;
months = [
'january',
'february',
'march',
'april',
'may'
];
constructor(
private fb: FormBuilder,
private yourService: YourService
) {
const date = new Date();
this.curMonth = date.getMonth();
this.curYear = date.getFullYear();
this.activeYear = date.getFullYear();
this.indexOfActiveMonth = date.getMonth();
}
ngOnInit(): void {
this.getElements(0, false);
}
getElements(pageNumber: number, doIncrement: boolean): void {
// See step 6
this.yourService.getElementsByPageAndSize(pageNumber, this.page.size).subscribe(
(res: HttpResponse<any>) => {
if (doIncrement) {
// Your API must return paginated data - see this tutorial
this.elements = this.elements.concat(res.body._embedded.elementDTOList);
} else {
this.elements = res.body._embedded.elementDTOList;
}
this.page = res.body.page;
this.elements = this.elements.sort((t1, t2) => t2.date.getTime() - t1.date.getTime());
this.elementsToShow = this.elements;
},
(error: HttpErrorResponse) => {
// handle error...
}
);
}
scrollIndexChangeAction(index: number): void {
if (this.elements[index] === undefined) return;
this.updateMonthByIndex(this.elements[index].date.getMonth());
// Load on down scroll event
if (
this.lastIndex < index && this.scrollViewPort.measureScrollOffset('bottom') < 50) {
this.loadNextElementsBatch();
}
this.lastIndex = index;
}
monthClickAction(index: number): void {
let compareYear = this.activeYear;
if (index === 11) {
compareYear--;
}
if (index === 0) {
compareYear++;
}
if (compareYear === this.curYear && index > this.curMonth) return;
const viewportIndex = this.elements.findIndex(el => el.date.getMonth() === index);
if (viewportIndex === -1) {
this.loadNextElementsBatch();
return;
}
this.scrollViewPort.scrollToIndex(viewportIndex, 'smooth');
}
updateMonthByIndex(index: number): void {
if (index === 11 && this.indexOfActiveMonth === 0) {
this.activeYear--;
}
if (index === 0 && this.indexOfActiveMonth === 11) {
this.activeYear++;
}
this.indexOfActiveMonth = index;
}
loadNextElementsBatch(): void {
this.scrollViewPort.checkViewportSize();
setTimeout(() => this.scrollViewPort.scrollTo({bottom: 0, behavior: 'smooth'}), 25);
this.getElements(this.page.number + 1, true);
}
defineMoveToLeft(index: number): string {
if (this.indexOfActiveMonth === 0 && index === this.months.length - 1) {
return '0';
}
if (this.indexOfActiveMonth === this.months.length - 1 && index === 0) {
return '66%';
}
if (this.indexOfActiveMonth === undefined) {
return 33 * index + '%';
}
return 33 * (index - this.indexOfActiveMonth + 1) + '%';
}
}
3. Component - scss
.element-list {
padding-left: 15px;
padding-right: 15px;
height: 98%;
width: 100%;
.month-navigation {
width: 100%;
position: relative;
height: 4.8vh;
margin: 2vh 0;
overflow: hidden;
.monthView {
display: block;
position: absolute;
left: 33%;
transition: 0.2s ease-in-out left;
top: 0;
text-align: center;
line-height: 3.5vh;
font-size: 1.7vh;
width: 33%;
color: #aaa;
&.active {
color: #000;
}
&.active:after {
content: '';
display: block;
margin: 0 auto;
width: 50%;
border-bottom: 0.3vh solid #000;
}
}
}
mat-form-field.mat-form-field {
display: block;
font-size: 2.08vh;
line-height: 2.6vh;
}
.transaction-scrolling-viewport {
height: 100%;
width: 100%;
}
.load-item {
width: 98%;
&.bordered {
border-bottom: 1px solid #aaa;
}
.content {
width: 80%;
margin: 1.5vh 10%;
.text {
color: #aaa;
font-size: 1.6vh;
text-align: center;
line-height: 2.5vh;
}
}
}
}
4. Model.ts for page property
export interface HttpPage {
size: number;
totalElements: number;
totalPages: number;
number: number;
}
5. Create inner/entry element
Create/adjust yourself this component with date,amount and mainText @input fields, or see this tutorial (coming soon)
6. Create a service method to get paginated data
getElementsByPageAndSize(page: number, size: number): Observable<HttpResponse<IElement[]>> {
let params = new HttpParams();
httpParams = httpParams .append('page', page.toString());
httpParams = httpParams .append('size', size.toString());
return this.http.get<IElement[]>(YOUR_SERVER_API_URL + 'api/your/elements', {
params: httpParams ,
observe: 'response'
});
}
7. IElement model
export interface IElement {
elementName: string;
amount: number;
date: Date;
}
Δεν υπάρχουν σχόλια:
Δημοσίευση σχολίου
What may be missing, or could get better?