filter Customer table + class TableFilter

This commit is contained in:
Michael Hoennig 2019-04-03 11:53:36 +02:00
parent d182637f41
commit 89437ca067
5 changed files with 158 additions and 6 deletions

View File

@ -45,6 +45,10 @@
<logger name="org.bson" level="WARN"/> <logger name="org.bson" level="WARN"/>
<logger name="org.hibernate.validator" level="WARN"/> <logger name="org.hibernate.validator" level="WARN"/>
<logger name="org.hibernate" level="WARN"/> <logger name="org.hibernate" level="WARN"/>
<!-- HOWTO log Hibernate SQL queries with bind parameters
<logger name="org.hibernate.SQL" additivity="false" level="DEBUG" />
<logger name="org.hibernate.type" additivity="false" level="TRACE" />
-->
<logger name="org.hibernate.ejb.HibernatePersistence" level="OFF"/> <logger name="org.hibernate.ejb.HibernatePersistence" level="OFF"/>
<logger name="org.postgresql.jdbc" level="WARN"/> <logger name="org.postgresql.jdbc" level="WARN"/>
<logger name="org.springframework" level="WARN"/> <logger name="org.springframework" level="WARN"/>

View File

@ -18,9 +18,23 @@
<th jhiSortBy="reference"><span jhiTranslate="hsadminNgApp.customer.reference">Reference</span> <fa-icon [icon]="'sort'"></fa-icon></th> <th jhiSortBy="reference"><span jhiTranslate="hsadminNgApp.customer.reference">Reference</span> <fa-icon [icon]="'sort'"></fa-icon></th>
<th jhiSortBy="prefix"><span jhiTranslate="hsadminNgApp.customer.prefix">Prefix</span> <fa-icon [icon]="'sort'"></fa-icon></th> <th jhiSortBy="prefix"><span jhiTranslate="hsadminNgApp.customer.prefix">Prefix</span> <fa-icon [icon]="'sort'"></fa-icon></th>
<th jhiSortBy="name"><span jhiTranslate="hsadminNgApp.customer.name">Name</span> <fa-icon [icon]="'sort'"></fa-icon></th> <th jhiSortBy="name"><span jhiTranslate="hsadminNgApp.customer.name">Name</span> <fa-icon [icon]="'sort'"></fa-icon></th>
<th jhiSortBy="kind"><span jhiTranslate="hsadminNgApp.customer.kind">Kind</span> <fa-icon [icon]="'sort'"></fa-icon></thn></th> <th jhiSortBy="kind"><span jhiTranslate="hsadminNgApp.customer.kind">Kind</span> <fa-icon [icon]="'sort'"></fa-icon></th>
<th></th> <th></th>
</tr> </tr>
<tr>
<th></th>
<th><input type="text" class="form-control" [(ngModel)]="filter.criteria.reference" (keyup)="filter.trigger()"></th>
<th><input type="text" class="form-control" [(ngModel)]="filter.criteria.prefix" (keyup)="filter.trigger()"></th>
<th><input type="text" class="form-control" [(ngModel)]="filter.criteria.name" (keyup)="filter.trigger()"></th>
<th>
<select class="form-control" [(ngModel)]="filter.criteria.kind" (change)="filter.trigger()">
<option value=""></option>
<option>NATURAL</option>
<option>LEGAL</option>
</select>
</th>
<th><button class="btn btn-primary float-left" (click)="filter.reset()">Reset Filter</button></th>
</tr>
</thead> </thead>
<tbody infinite-scroll (scrolled)="loadPage(page + 1)" [infiniteScrollDisabled]="page >= links['last']" [infiniteScrollDistance]="0"> <tbody infinite-scroll (scrolled)="loadPage(page + 1)" [infiniteScrollDisabled]="page >= links['last']" [infiniteScrollDistance]="0">
<tr *ngFor="let customer of customers ;trackBy: trackId"> <tr *ngFor="let customer of customers ;trackBy: trackId">

View File

@ -8,6 +8,7 @@ import { AccountService } from 'app/core';
import { ITEMS_PER_PAGE } from 'app/shared'; import { ITEMS_PER_PAGE } from 'app/shared';
import { CustomerService } from './customer.service'; import { CustomerService } from './customer.service';
import { TableFilter } from 'app/shared/util/tablefilter';
@Component({ @Component({
selector: 'jhi-customer', selector: 'jhi-customer',
@ -23,6 +24,7 @@ export class CustomerComponent implements OnInit, OnDestroy {
predicate: any; predicate: any;
reverse: any; reverse: any;
totalItems: number; totalItems: number;
filter: TableFilter<{ reference?: string; prefix?: string; name?: string; kind?: string }>;
constructor( constructor(
protected customerService: CustomerService, protected customerService: CustomerService,
@ -39,11 +41,15 @@ export class CustomerComponent implements OnInit, OnDestroy {
}; };
this.predicate = 'id'; this.predicate = 'id';
this.reverse = true; this.reverse = true;
this.filter = new TableFilter({ reference: 'equals', prefix: 'contains', name: 'contains', kind: 'equals' }, 500, () => {
this.loadAll();
});
} }
loadAll() { loadAll() {
this.customerService this.customerService
.query({ .query({
...this.filter.buildQueryCriteria(),
page: this.page, page: this.page,
size: this.itemsPerPage, size: this.itemsPerPage,
sort: this.sort() sort: this.sort()
@ -96,6 +102,8 @@ export class CustomerComponent implements OnInit, OnDestroy {
protected paginateCustomers(data: ICustomer[], headers: HttpHeaders) { protected paginateCustomers(data: ICustomer[], headers: HttpHeaders) {
this.links = this.parseLinks.parse(headers.get('link')); this.links = this.parseLinks.parse(headers.get('link'));
this.totalItems = parseInt(headers.get('X-Total-Count'), 10); this.totalItems = parseInt(headers.get('X-Total-Count'), 10);
this.page = 0;
this.customers = [];
for (let i = 0; i < data.length; i++) { for (let i = 0; i < data.length; i++) {
this.customers.push(data[i]); this.customers.push(data[i]);
} }

View File

@ -0,0 +1,40 @@
import { Subject, Subscription } from 'rxjs';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
/**
* Handles filtering in data tables by converting the user input to query criteria of the JHipster REST API.
*
* It also does not reload during a given debounce period.
*/
export class TableFilter<T extends {}> {
criteria: T;
private criteriaChangedSubject = new Subject<void>();
private criteriaChangedDebouncer: Subscription;
constructor(private query: T, private debounceMillis: number, private reload: () => void) {
this.criteria = {} as any;
this.criteriaChangedDebouncer = this.criteriaChangedSubject.pipe(debounceTime(debounceMillis)).subscribe(() => this.reload());
}
trigger() {
this.debounce();
}
reset() {
this.criteria = {} as any;
this.debounce();
}
buildQueryCriteria() {
let queryCriteria: T = {} as any;
Object.keys(this.criteria).forEach(e => {
queryCriteria[e + '.' + this.query[e]] = this.criteria[e];
});
return queryCriteria;
}
private debounce() {
this.criteriaChangedSubject.next();
}
}

View File

@ -0,0 +1,86 @@
import { TableFilter } from 'app/shared/util/tablefilter';
/* To run these tests in IntelliJ IDEA, you need a run configuration with
Configuration File:
~/Projekte/Hostsharing/hsadmin-ng/src/test/javascript/jest.conf.js
and a Node Interpreter, e.g. if installed with nvm, this could be:
~/.nvm/versions/node/v10.15.3/bin/node
*/
describe('TableFilter Tests', () => {
describe('TableFilter', () => {
let filter: TableFilter<{ name: string; number: string }>;
let asynchronously: () => void;
beforeEach(() => {
filter = new TableFilter({ name: 'contains', number: 'equals' }, 100, () => {
asynchronously();
});
});
it('trigger() asynchronously calls the reload-handler', done => {
// given
filter.criteria.name = 'Test Filter Value';
// when
filter.trigger();
let triggerStartedAtMillis = Date.now();
// then
asynchronously = () => {
expect(Date.now()).toBeGreaterThan(triggerStartedAtMillis);
done();
};
});
it('if trigger() is called multiple times during debounce, debounce period ands after last trigger()', done => {
// given
filter.criteria.name = 'Test Filter Value';
// when
filter.trigger();
let triggerStartedAtMillis = null;
setTimeout(() => {
filter.trigger();
triggerStartedAtMillis = Date.now();
}, 50);
// then
asynchronously = () => {
expect(triggerStartedAtMillis).not.toBeNull();
expect(Date.now()).toBeGreaterThan(triggerStartedAtMillis);
done();
};
});
it('when filter "name" is set to "test value", buildQueryCriteria() returns { "name.contains": "test value" }', () => {
// given
filter.criteria.name = 'test value';
// when
let actual = filter.buildQueryCriteria();
// then
expect(filter.buildQueryCriteria()).toEqual({ 'name.contains': 'test value' });
});
it('reset() clears criteria and calls reload-handler, considering debounce period', done => {
// given
filter.criteria.name = 'Test Filter Value';
// when
filter.trigger();
let triggerStartedAtMillis = null;
setTimeout(() => {
filter.reset();
triggerStartedAtMillis = Date.now();
}, 50);
// then
asynchronously = () => {
expect(triggerStartedAtMillis).not.toBeNull();
expect(Date.now()).toBeGreaterThan(triggerStartedAtMillis);
done();
};
});
});
});