在派生 class 上订阅 Angular2 事件
Subscribe Angular2 events on derived class
我正在开发一个在不同页面上使用多个网格的应用程序。我没有一次又一次地重写 HTML,而是将代码抽象为以下方式:
table.component.ts
export class TableComponent implements OnInit{
columns: Column[]= [];
rows: any[] = [];
searchText: string;
routerLinked: string;
showFilter: boolean;
constructor(private paginService: PaginationService,
private tableLoader: GridLoadUtil,
private progressBarService: ProgressBarService,
private route: ActivatedRoute,
private router: Router) {
this.paginService.itemsToDisplayChange.subscribe((value) => {
this.rows = value
});
this.tableLoader.columnsChange.subscribe((values) => {
this.columns = values;
});
}
ngOnInit() {
this.tableLoader.beforeDataLoaded();
this.progressBarService.startProgress();
this.routerLinked = this.route.snapshot.url[0].path + '/';
this.showFilter = true;
}
navigateToCreate() {
this.router.navigate([this.routerLinked]);
}
applyFilter() {
this.paginService.applyFilter(this.searchText);
}
clearFilter() {
this.searchText = '';
this.paginService.clearFilter();
}
}
table.component.html
<div class="container container-fluid">
<div class="row" *ngIf="showFilter === true">
<div class="form-group">
<label>Filter </label>
<input type="text" id="inputName" [(ngModel)]="searchText"/>
<button type="button" class="btn btn-default btn-sm" (click)="applyFilter()">
<span class="glyphicon glyphicon-ok"></span>
</button>
<button type="button" class="btn btn-default btn-sm" (click)="clearFilter()">
<span class="glyphicon glyphicon-remove"></span>
</button>
<a class="btn btn-default btn-sm pull-right" (click)="navigateToCreate()">
<span class="glyphicon glyphicon-plus-sign"></span>
</a>
</div>
</div>
<div class="row">
<table class="table table-responsive table-hover">
<thead class="thead-inverse">
<tr>
<th *ngFor="let column of columns">{{column.header}}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let row of rows;trackBy: trackByID;">
<td *ngFor="let column of columns">{{row[column.field]}}
</td>
</tr>
</tbody>
</table>
</div>
<xfd-progressbar component></xfd-progressbar>
<div class="row col-lg-offset-5">
<ngbd-pagination-basic> component</ngbd-pagination-basic>
</div>
</div>
我的 table 组件将各种服务作为依赖项,如用于分页实现的 paginService、progressBar、路由器等。它还使用 GridLoadUtil,它提供有关网格将具有的列的信息,以及as 将数据传递给它。
GridLoadUtil
@Injectable()
export class GridLoadUtil {
columns: Column[] = [];
columnsChange: Subject<Column[]> = new Subject<Column[]>();
constructor(protected paginService: PaginationService) {
}
setColumns(columnList: Column[]): void {
this.columns = columnList;
this.columnsChange.next(this.columns);
}
beforeDataLoaded(): void {
this.paginService.setPaginationDisabled(true);
this.paginService.setTotalItems([]);
}
afterDataLoaded(data): void {
this.paginService.setTotalItems(data);
this.paginService.setPaginationDisabled((data.length == 0));
this.paginService.setActivePage(1);
}
}
如果我在同一页面上有多个网格,我会使用指令来提供 GridLoadUtil 的新实现,这样所有网格都不会因为一个网格的变化而受到影响。
@Directive({
selector: '[dummy]',
providers: [{provide: GridLoadUtil, useClass: DummyGridLoadUtil}]
})
export class DummyDirective {
constructor(private el: ElementRef) {
console.error("not")
}
}
如果我的页面有多个网格,这就是它的样子 -
<xfd-table></xfd-table>
<xfd-table [dummy]></xfd-table>
我设法让两个 table 的行为有所不同。但是,对第二个网格的列的更改未被订阅,因此不会出现在页面上。即使 table.component.ts 获得了一个 DummyGridLoadUtil 实例注入,
this.tableLoader.columnsChange.subscribe((values) => {
this.columns = values;
});
以上部分订阅了变量类型 (GridLoadUtil) 而不是实现 (DummyGridLoadUtil) 的更改。
有什么办法可以克服这个问题吗?
因此,如果您希望每页有多个 table,那么您就存在严重的设计缺陷。
目前看来:
页面有加载数据的服务,并提供网格加载工具
table 组件获取注入的网格加载实用程序,然后页面包装器调用方法来设置列
您正在尝试为 gridloadutil 重新提供伪指令,但是页面包装器当然无法调用 in 上的方法,因为它在注入器树中较低。
当您希望在一个页面上有多个时,您需要做的是在不进行重组的情况下为每个 table 创建包装器。
所以 TableOneWrapperComponent 提供网格加载实用程序并具有填充它所需的任何服务,而 TableTwoWrapperComponent 提供它自己的网格加载实用程序副本并具有提供它所需的任何服务。
否则,您可以考虑进行较小的重组,以便将列作为输入而不是通过服务提供,因为它看起来像是直接的父/子关系。但是,每个组件都获得给定服务的一个副本,因此您不能让一个父组件通过同一服务独立地与两个子组件通信。
我继续修改我的设计。我并没有真正改变很多
Table.component.ts
import {Column} from "../models/Column";
import {Component, Input, OnInit} from "@angular/core";
import {PaginationService} from "../services/PaginationService";
import {GridLoadUtil} from "../util/GridLoadUtil";
import {ProgressBarService} from "../services/ProgressBarService";
import {ActivatedRoute, Router} from "@angular/router";
import {NgbProgressbarConfig} from "@ng-bootstrap/ng-bootstrap";
/**
* Created by ppandey on 6/12/2017.
*/
@Component({
selector: 'xfd-table',
templateUrl: './table.component.html',
styleUrls: ['./table.component.css'],
providers: [PaginationService, GridLoadUtil, ProgressBarService]
})
export class TableComponent implements OnInit{
get columns(): Column[] {
return this.tableLoader.columns;
}
@Input()
set columns (cols: Column[]) {
this.tableLoader.setColumns(cols);
}
get totalRows(): any[] {
return this.paginService.getTotalItems();
}
@Input()
set totalRows (row: any[]) {
this.afterDataLoaded(row);
this.progressBarService.endProgress();
}
rows: any[] = [];
searchText: string;
routerLinked: string;
showFilter: boolean;
constructor(private paginService: PaginationService,
private tableLoader: GridLoadUtil,
private progressBarService: ProgressBarService,
private route: ActivatedRoute,
private router: Router) {
this.paginService.itemsToDisplayChange.subscribe((value) => {
this.rows = value
});
}
ngOnInit() {
this.beforeDataLoaded();
this.progressBarService.startProgress();
this.routerLinked = this.route.snapshot.url[0].path.replace('Grid', '') + '/';
this.showFilter = true;
}
beforeDataLoaded(): void {
this.paginService.setPaginationDisabled(true);
this.paginService.setTotalItems([]);
}
afterDataLoaded(data): void {
this.paginService.setTotalItems(data);
this.paginService.setPaginationDisabled((data.length == 0));
this.paginService.setActivePage(1);
}
navigateToCreate() {
this.router.navigate([this.routerLinked]);
}
navigateToEdit(row) {
this.router.navigate([this.routerLinked + row.userId]);
}
applyFilter() {
this.paginService.applyFilter(this.searchText);
}
clearFilter() {
this.searchText = '';
this.paginService.clearFilter();
}
}
我现在使用分层 DI,从父组件提供列和行。对于同一页面上的多个 table,我将每个 table 放在它自己的一个组件中,这是有道理的,因为每个 table 代表不同的数据和不同的部分。
在每个父组件上,这是我所做的 -
<xfd-table [columns]="sourceTableGridColumns" [totalRows]="sourceTableGridData"></xfd-table>
<xfd-table [columns]="sourceDatabaseGridColumns" [totalRows]="sourceDatabaseGridData"></xfd-table>
这解决了一个问题!而且我的代码仍然非常可配置和可重用。
我正在开发一个在不同页面上使用多个网格的应用程序。我没有一次又一次地重写 HTML,而是将代码抽象为以下方式:
table.component.ts
export class TableComponent implements OnInit{
columns: Column[]= [];
rows: any[] = [];
searchText: string;
routerLinked: string;
showFilter: boolean;
constructor(private paginService: PaginationService,
private tableLoader: GridLoadUtil,
private progressBarService: ProgressBarService,
private route: ActivatedRoute,
private router: Router) {
this.paginService.itemsToDisplayChange.subscribe((value) => {
this.rows = value
});
this.tableLoader.columnsChange.subscribe((values) => {
this.columns = values;
});
}
ngOnInit() {
this.tableLoader.beforeDataLoaded();
this.progressBarService.startProgress();
this.routerLinked = this.route.snapshot.url[0].path + '/';
this.showFilter = true;
}
navigateToCreate() {
this.router.navigate([this.routerLinked]);
}
applyFilter() {
this.paginService.applyFilter(this.searchText);
}
clearFilter() {
this.searchText = '';
this.paginService.clearFilter();
}
}
table.component.html
<div class="container container-fluid">
<div class="row" *ngIf="showFilter === true">
<div class="form-group">
<label>Filter </label>
<input type="text" id="inputName" [(ngModel)]="searchText"/>
<button type="button" class="btn btn-default btn-sm" (click)="applyFilter()">
<span class="glyphicon glyphicon-ok"></span>
</button>
<button type="button" class="btn btn-default btn-sm" (click)="clearFilter()">
<span class="glyphicon glyphicon-remove"></span>
</button>
<a class="btn btn-default btn-sm pull-right" (click)="navigateToCreate()">
<span class="glyphicon glyphicon-plus-sign"></span>
</a>
</div>
</div>
<div class="row">
<table class="table table-responsive table-hover">
<thead class="thead-inverse">
<tr>
<th *ngFor="let column of columns">{{column.header}}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let row of rows;trackBy: trackByID;">
<td *ngFor="let column of columns">{{row[column.field]}}
</td>
</tr>
</tbody>
</table>
</div>
<xfd-progressbar component></xfd-progressbar>
<div class="row col-lg-offset-5">
<ngbd-pagination-basic> component</ngbd-pagination-basic>
</div>
</div>
我的 table 组件将各种服务作为依赖项,如用于分页实现的 paginService、progressBar、路由器等。它还使用 GridLoadUtil,它提供有关网格将具有的列的信息,以及as 将数据传递给它。
GridLoadUtil
@Injectable()
export class GridLoadUtil {
columns: Column[] = [];
columnsChange: Subject<Column[]> = new Subject<Column[]>();
constructor(protected paginService: PaginationService) {
}
setColumns(columnList: Column[]): void {
this.columns = columnList;
this.columnsChange.next(this.columns);
}
beforeDataLoaded(): void {
this.paginService.setPaginationDisabled(true);
this.paginService.setTotalItems([]);
}
afterDataLoaded(data): void {
this.paginService.setTotalItems(data);
this.paginService.setPaginationDisabled((data.length == 0));
this.paginService.setActivePage(1);
}
}
如果我在同一页面上有多个网格,我会使用指令来提供 GridLoadUtil 的新实现,这样所有网格都不会因为一个网格的变化而受到影响。
@Directive({
selector: '[dummy]',
providers: [{provide: GridLoadUtil, useClass: DummyGridLoadUtil}]
})
export class DummyDirective {
constructor(private el: ElementRef) {
console.error("not")
}
}
如果我的页面有多个网格,这就是它的样子 -
<xfd-table></xfd-table>
<xfd-table [dummy]></xfd-table>
我设法让两个 table 的行为有所不同。但是,对第二个网格的列的更改未被订阅,因此不会出现在页面上。即使 table.component.ts 获得了一个 DummyGridLoadUtil 实例注入,
this.tableLoader.columnsChange.subscribe((values) => {
this.columns = values;
});
以上部分订阅了变量类型 (GridLoadUtil) 而不是实现 (DummyGridLoadUtil) 的更改。
有什么办法可以克服这个问题吗?
因此,如果您希望每页有多个 table,那么您就存在严重的设计缺陷。
目前看来:
页面有加载数据的服务,并提供网格加载工具
table 组件获取注入的网格加载实用程序,然后页面包装器调用方法来设置列
您正在尝试为 gridloadutil 重新提供伪指令,但是页面包装器当然无法调用 in 上的方法,因为它在注入器树中较低。
当您希望在一个页面上有多个时,您需要做的是在不进行重组的情况下为每个 table 创建包装器。
所以 TableOneWrapperComponent 提供网格加载实用程序并具有填充它所需的任何服务,而 TableTwoWrapperComponent 提供它自己的网格加载实用程序副本并具有提供它所需的任何服务。
否则,您可以考虑进行较小的重组,以便将列作为输入而不是通过服务提供,因为它看起来像是直接的父/子关系。但是,每个组件都获得给定服务的一个副本,因此您不能让一个父组件通过同一服务独立地与两个子组件通信。
我继续修改我的设计。我并没有真正改变很多
Table.component.ts
import {Column} from "../models/Column";
import {Component, Input, OnInit} from "@angular/core";
import {PaginationService} from "../services/PaginationService";
import {GridLoadUtil} from "../util/GridLoadUtil";
import {ProgressBarService} from "../services/ProgressBarService";
import {ActivatedRoute, Router} from "@angular/router";
import {NgbProgressbarConfig} from "@ng-bootstrap/ng-bootstrap";
/**
* Created by ppandey on 6/12/2017.
*/
@Component({
selector: 'xfd-table',
templateUrl: './table.component.html',
styleUrls: ['./table.component.css'],
providers: [PaginationService, GridLoadUtil, ProgressBarService]
})
export class TableComponent implements OnInit{
get columns(): Column[] {
return this.tableLoader.columns;
}
@Input()
set columns (cols: Column[]) {
this.tableLoader.setColumns(cols);
}
get totalRows(): any[] {
return this.paginService.getTotalItems();
}
@Input()
set totalRows (row: any[]) {
this.afterDataLoaded(row);
this.progressBarService.endProgress();
}
rows: any[] = [];
searchText: string;
routerLinked: string;
showFilter: boolean;
constructor(private paginService: PaginationService,
private tableLoader: GridLoadUtil,
private progressBarService: ProgressBarService,
private route: ActivatedRoute,
private router: Router) {
this.paginService.itemsToDisplayChange.subscribe((value) => {
this.rows = value
});
}
ngOnInit() {
this.beforeDataLoaded();
this.progressBarService.startProgress();
this.routerLinked = this.route.snapshot.url[0].path.replace('Grid', '') + '/';
this.showFilter = true;
}
beforeDataLoaded(): void {
this.paginService.setPaginationDisabled(true);
this.paginService.setTotalItems([]);
}
afterDataLoaded(data): void {
this.paginService.setTotalItems(data);
this.paginService.setPaginationDisabled((data.length == 0));
this.paginService.setActivePage(1);
}
navigateToCreate() {
this.router.navigate([this.routerLinked]);
}
navigateToEdit(row) {
this.router.navigate([this.routerLinked + row.userId]);
}
applyFilter() {
this.paginService.applyFilter(this.searchText);
}
clearFilter() {
this.searchText = '';
this.paginService.clearFilter();
}
}
我现在使用分层 DI,从父组件提供列和行。对于同一页面上的多个 table,我将每个 table 放在它自己的一个组件中,这是有道理的,因为每个 table 代表不同的数据和不同的部分。
在每个父组件上,这是我所做的 -
<xfd-table [columns]="sourceTableGridColumns" [totalRows]="sourceTableGridData"></xfd-table>
<xfd-table [columns]="sourceDatabaseGridColumns" [totalRows]="sourceDatabaseGridData"></xfd-table>
这解决了一个问题!而且我的代码仍然非常可配置和可重用。