我对Angular 2 Material Table和服务器端分页有问题。我的问题是我无法等待我的Datascource-object的connect函数中的异步请求调用。我尝试了很多方法,但没办法。结果总是表显示具有先前偏移和限制的数据。我知道在异步调用之后我有一个返回但没有这些返回会得到一个observable的错误(这是尝试获取新数据而不是以前的数据的点)。我的功能目标是获得服务器端分页。我希望你能帮助我。
我的component.ts:
import { Component, ViewChild, ElementRef } from '@angular/core';
import { UserService } from './user.service';
import { LanguagePipe } from './../system/language/language.pipe';
import { generateUUID } from './../helper/helper.component';
import { DialogComponent } from "../dialog/dialog.component";
import { generatePassword } from "./../password/passwordGenerator";
import { NgbPopover } from '@ng-bootstrap/ng-bootstrap';
import { SharedService } from './../shared/shared.service';
import { FunctionPermissionService } from './../system/permission/functionpermission.service';
import { RoleListComponent } from "./../role/rolelist.component";
import { FunctionsPermission } from './../system/globals/globals.service';
import { MdTableModule } from '@angular/material';
import { DataSource } from '@angular/cdk';
import { MdPaginator, MdSort, SelectionModel } from '@angular/material';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Observable } from 'rxjs/Observable';
import { Observer } from 'rxjs/Observer';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/observable/merge';
import 'rxjs/add/observable/fromEvent';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/debounceTime';
import { Role } from "./user.class";
@Component({
selector: 'user',
templateUrl: './user.component.html',
styleUrls: ['./user.component.scss'],
})
export class TableSortingExample {
displayedColumns = ['select', 'firstname', 'email', 'phonenumber', 'roles'];
userDatabase: UserDatabase;
selection = new SelectionModel<string>(true, []);
dataSource: ExampleDataSource | null;
@ViewChild(MdPaginator) paginator: MdPaginator;
@ViewChild(MdSort) sort: MdSort;
@ViewChild('filter') filter: ElementRef;
constructor(
private userService: UserService,
private languagePipe: LanguagePipe,
private sharedService: SharedService,
private functionPermissionService: FunctionPermissionService
) {
}
ngOnInit() {
this.getUser().then(data => {
console.log(data);
this.userDatabase = new UserDatabase(data, this.userService);
this.getUserCount().then((data: any) => {
this.dataSource = new ExampleDataSource(this.userDatabase, this.paginator, this.sort, this.userService, data);
})
})
Observable.fromEvent(this.filter.nativeElement, 'keyup')
.debounceTime(150)
.distinctUntilChanged()
.subscribe(() => {
if (!this.dataSource) { return; }
this.dataSource.filter = this.filter.nativeElement.value;
});
}
isAllSelected(): boolean {
if (!this.dataSource) { return false; }
if (this.selection.isEmpty()) { return false; }
if (this.filter.nativeElement.value) {
return this.selection.selected.length == this.dataSource.renderedData.length;
} else {
return this.selection.selected.length == this.userDatabase.data.length;
}
}
masterToggle() {
if (!this.dataSource) { return; }
if (this.isAllSelected()) {
this.selection.clear();
} else if (this.filter.nativeElement.value) {
this.dataSource.renderedData.forEach(data => this.selection.select(data.id));
} else {
this.userDatabase.data.forEach(data => this.selection.select(data.id));
}
}
getUser() {
let papingString = "offset=" + 0 + "&limit=" + 5;
return new Promise((resolve, reject) => {
this.userService.getAdministrativeUser("", papingString).subscribe(data => {
resolve(data);
}, err => {
console.error(err);
reject(err);
}, () => { });
});
}
getUserCount() {
return new Promise((resolve, reject) => {
this.userService.getAdministrativeUserSize('').subscribe((data: any) => {
resolve(data);
}, err => {
console.error(err);
reject(err);
}, () => { });
});
}
}
export interface UserData {
id: string;
firstname: string;
email: string;
phonenumber: string;
roles: Array<Role>;
}
/** An example database that the data source uses to retrieve data for the table. */
export class UserDatabase {
/** Stream that emits whenever the data has been modified. */
pagingString = "";
userData: UserData[];
dataChange: BehaviorSubject<UserData[]> = new BehaviorSubject<UserData[]>([]);
get data(): UserData[] {return this.dataChange.value;}
constructor(data: any, private userService: UserService) {
console.log(data);
this.fillDatabase(data);
}
/** Adds a new user to the database. */
fillDatabase(data: any) {
console.log(data);
const copiedData = data.slice();
for (let entry of data)
copiedData.push(convertUser(entry));
this.dataChange.next(copiedData);
}
getData(extra: string) {
return new Promise((resolve, reject) => {
this.userService.getAdministrativeUser("", extra).subscribe(data => {
let userData: Array<UserData> = [];
for (let entry of data)
userData.push(convertUser(entry));
resolve(userData);
}, err => {
console.error(err);
reject(err);
});
});
}
}
export class ExampleDataSource extends DataSource<any> {
_filterChange = new BehaviorSubject('');
get filter(): string { return this._filterChange.value; }
set filter(filter: string) { this._filterChange.next(filter); }
filteredString: string = "";
prevFilteredString: string = "";
prevSortDirection: string = "";
prevSortField: string = "";
renderedData: UserData[] = [];
change: boolean = false;
subject: BehaviorSubject<UserData[]> = new BehaviorSubject<UserData[]>([]);
constructor(private _exampleDatabase: UserDatabase,
private _paginator: MdPaginator,
private _sort: MdSort,
private userService: UserService,
private dataCount: number) {
super();
this._filterChange.subscribe(() => this._paginator.pageIndex = 0);
}
connect(): Observable<UserData[]> {
const displayDataChanges = [
this._exampleDatabase.dataChange,
this._sort.mdSortChange,
this._filterChange,
this._paginator.page,
];
return Observable.merge(...displayDataChanges).map(() => {
if (this.prevFilteredString != this.filter) {
this._paginator.pageIndex = 0;
this._exampleDatabase.data.splice(0, this._exampleDatabase.data.length);
}
// Filtering
if (this.filter != null && this.filter != "" && this.filter != undefined)
this.filteredString = "&filter=(firstname like '%" + this.filter + "%' or lastname like '%" + this.filter + "%' or phonenumber like '%" + this.filter + "%' or email like '%" + this.filter + "%')";
this.prevFilteredString = this.filter;
// Sorting
const sortInformations = this.sortData();
let pageIndex = this._paginator.pageIndex;
const startIndex = pageIndex * this._paginator.pageSize;
let pagingString = "offset=" + startIndex + "&limit=" + this._paginator.pageSize + this.filteredString + sortInformations;
this._exampleDatabase.pagingString = pagingString;
this.getDataCount(this.filteredString + sortInformations);
this.getData(pagingString).then((data:any) =>{
console.log(this._exampleDatabase.data);
this._exampleDatabase.data.splice(0, this._exampleDatabase.data.length);
for(let entry of data)
this._exampleDatabase.data.push(entry);
this.renderedData = this._exampleDatabase.data.slice();
console.log(this._exampleDatabase.data);
return this._exampleDatabase.data.slice();
},err => {
console.error(err);
});
return this._exampleDatabase.data.slice();
});
}
disconnect() { console.log("test"); }
getDataCount(extra: string) {
return new Promise((resolve, reject) => {
if (extra.charAt(0) === '&') {
extra = extra.substr(1);
}
this.userService.getAdministrativeUserSize(extra).subscribe(data => {
this.dataCount = data;
resolve();
});
})
}
getData(extra: string) {
return new Promise((resolve, reject) => {
this.userService.getAdministrativeUser("", extra).subscribe(data => {
let userData: Array<UserData> = [];
for (let entry of data)
userData.push(convertUser(entry));
resolve(userData);
}, err => {
console.error(err);
reject(err);
});
});
}
/** Returns a sorted copy of the database data. */
sortData(): string {
if (!this._sort.active) {
if (this._sort.direction != this.prevSortDirection || "" != this.prevSortField)
this._paginator.pageIndex = 0;
} else
if (this._sort.direction != this.prevSortDirection || this._sort.active.toString() != this.prevSortField)
this._paginator.pageIndex = 0;
if (!this._sort.active || this._sort.direction == '') {
this.prevSortDirection = "";
if (!this._sort.active)
this.prevSortField = ""
else
this.prevSortField = this._sort.active;
return "";
} else {
this.prevSortDirection = this._sort.direction.toString();
this.prevSortField = this._sort.active.toString();
return "&order=" + this._sort.active.toString() + " " + this._sort.direction.toString()
};
}
}
/** Builds and returns a new User. */
export function convertUser(data: any) {
let user: UserData = {
id: data.id,
firstname: data.firstname,
email: data.email,
phonenumber: data.phonenumber,
roles: []
}
let userRoles: Array<Role> = [];
for (let role of data.roles)
userRoles.push(new Role(role.name, role.id));
user["roles"] = userRoles;
return user;
};
我的HTML:
<div class="example-container mat-elevation-z8">
<div class="example-header" [style.display]="selection.isEmpty() ? '' : 'none'">
<md-input-container floatPlaceholder="never">
<input mdInput #filter placeholder="Filter users">
</md-input-container>
</div>
<div class="example-header example-selection-header"
*ngIf="!selection.isEmpty()">
{{selection.selected.length}}
{{selection.selected.length == 1 ? 'user' : 'users'}}
selected
</div>
<md-table #table [dataSource]="dataSource" mdSort>
<!--- Note that these columns can be defined in any order.
The actual rendered columns are set as a property on the row definition" -->
<!-- Checkbox Column -->
<ng-container cdkColumnDef="select">
<md-header-cell *cdkHeaderCellDef>
</md-header-cell>
<md-cell *cdkCellDef="let row">
<md-checkbox (click)="$event.stopPropagation()"
(change)="$event ? selection.toggle(row.id) : null"
[checked]="selection.isSelected(row.id)">
</md-checkbox>
</md-cell>
</ng-container>
<!-- ID Column -->
<ng-container cdkColumnDef="firstname">
<md-header-cell *cdkHeaderCellDef md-sort-header> Name </md-header-cell>
<md-cell *cdkCellDef="let row"> {{row.firstname}} </md-cell>
</ng-container>
<!-- Progress Column -->
<ng-container cdkColumnDef="email">
<md-header-cell *cdkHeaderCellDef md-sort-header> Email </md-header-cell>
<md-cell *cdkCellDef="let row"> {{row.email}} </md-cell>
</ng-container>
<!-- Name Column -->
<ng-container cdkColumnDef="phonenumber">
<md-header-cell *cdkHeaderCellDef md-sort-header> Phonenumber </md-header-cell>
<md-cell *cdkCellDef="let row"> {{row.phonenumber}} </md-cell>
</ng-container>
<!-- Color Column -->
<ng-container cdkColumnDef="roles">
<md-header-cell *cdkHeaderCellDef md-sort-header> Rolen </md-header-cell>
<md-cell *cdkCellDef="let row"> <label *ngFor="let role of row.roles">{{role.name}}</label> </md-cell>
</ng-container>
<md-header-row *cdkHeaderRowDef="displayedColumns"></md-header-row>
<md-row *cdkRowDef="let row; columns: displayedColumns;"
[class.example-selected-row]="selection.isSelected(row.id)"
(click)="selection.toggle(row.id)">
</md-row>
</md-table>
<div class="example-no-results"
[style.display]="( datasource == null || dataSource.renderedData.length == 0 )? '' : 'none'">
No users found matching filter.
</div>
<md-paginator #paginator
[length]="(dataSource == null)?0:dataSource.dataCount"
[pageIndex]="0"
[pageSize]="5"
[pageSizeOptions]="[5, 10]">
</md-paginator>
</div>
班级档案:
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Observable } from 'rxjs/Observable';
export class rowItem {
constructor(
public userInformations: UserInformations,
public roles: Array<Role>) {
}
setRoles(input: Array<Role>) {
this.roles = input;
}
setUserInformation(input: UserInformations) {
this.userInformations = input;
}
getIdList(): Array<string> {
let result: Array<string> = [];
for (let role of this.roles)
result.push(role.id);
return result;
}
}
export class UserInformations {
constructor(
public id: string,
public firstname: string,
public lastname: string,
public email: string,
public phonenumber: string) {
}
}
export class Role {
constructor(public name: string, public id: string) {
}
}
我的css文件:
/* Structure */
.example-container {
display: flex;
flex-direction: column;
width: 100%;
}
.example-header {
min-height: 56px;
max-height: 56px;
display: flex;
align-items: center;
padding: 8px 24px 0;
font-size: 20px;
justify-content: space-between;
border-bottom: 1px solid transparent;
}
.mat-input-container {
font-size: 14px;
flex-grow: 1;
margin-left: 32px;
margin-top: 8px;
}
.example-no-results {
display: flex;
justify-content: center;
padding: 24px;
font-size: 12px;
font-style: italic;
}
/** Selection styles */
.example-selection-header {
font-size: 18px;
background: rgba(255, 64, 129, 0.3);
border-bottom: 1px solid #d696ac;
}
.mat-column-select {
max-width: 54px;
}
.mat-row:hover, .example-selected-row {
background: #f5f5f5;
}
.mat-row:active, .mat-row.example-selected-row {
background: #eaeaea;
}
.mat-table {
overflow: auto;
max-height: 500px;
}