我从Angular material table中制作了一个自定义组件。它基本上是一个递归表,单击某行时显示一个子表,而单击后一行时显示一个子表。它需要一个递归模板。表输入数据非常大,并采用带有JSON数组等形式的JSON形式。这种格式由项目的其余部分决定,并且不够灵活。
数据存储在名为data
的变量中。我的表具有用于修改data
中条目的输入字段,并且检查它是否可以正常工作,我的问题是,如果从表外部更新data
,则表视图不会更改,不会更新。我对将data
传递给组件时使用绑定感到惊讶。我是Angular的初学者,对Subjects
和Observables
尚不满意,所以我没有朝这个方向看。
这是代码。
对于递归组件:
@Component({
selector: 'app-rectable',
template: `
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<div class="example-container mat-elevation-z8">
<mat-table [dataSource]="theData">
<ng-container matColumnDef="name">
<mat-header-cell *matHeaderCellDef> channel </mat-header-cell>
<mat-cell *matCellDef="let element">
<button *ngIf="element.isexpandable" mat-icon-button (click)="element.isexpanded = !element.isexpanded;">
<mat-icon class="mat-icon-rtl-mirror">
{{element.isexpanded ? 'expand_more' : 'chevron_right'}}
</mat-icon>
</button>
<mat-form-field class="example-full-width">
<input matInput [(ngModel)]="element.name" (input)="update($event)">
</mat-form-field>
</mat-cell>
</ng-container>
<ng-container matColumnDef="min">
<mat-header-cell *matHeaderCellDef> min </mat-header-cell>
<mat-cell *matCellDef="let element">
<input matInput type=number [(ngModel)]="element.min" (input)="update($event)">
</mat-cell>
</ng-container>
<ng-container matColumnDef="max">
<mat-header-cell *matHeaderCellDef> max </mat-header-cell>
<mat-cell *matCellDef="let element">
<input matInput type=number [(ngModel)]="element.max" (input)="update($event)">
</mat-cell>
</ng-container>
<ng-container matColumnDef="value">
<mat-header-cell *matHeaderCellDef> value </mat-header-cell>
<mat-cell *matCellDef="let element">
{{element.value}}
</mat-cell>
</ng-container>
<ng-container matColumnDef="expandedDetail">
<mat-cell *matCellDef="let detail">
<app-rectable *ngIf="detail.element.isexpandable" [array]="detail.element.variables" [isTop]="false">
</app-rectable>
</mat-cell>
</ng-container>
<div *ngIf="isTop">
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
</div>
<mat-row *matRowDef="let row; columns: displayedColumns;" matRipple class="element-row" [class.expanded]="row.isexpanded" >
</mat-row>
<mat-row *matRowDef="let row; columns: ['expandedDetail']; when: isExpansionDetailRow" [@detailExpand]="row.element.isexpanded ? 'expanded' : 'collapsed'" style="overflow: hidden">
</mat-row>
</mat-table>
</div>
<div *ngIf="!theData.value.length" > EMPTY DATA INPUT </div>
`,
styleUrls: ['./rectable.component.css'],
animations: [
trigger('detailExpand', [
state('collapsed', style({ height: '0px', minHeight: '0', visibility: 'hidden' })),
state('expanded', style({ height: '*', visibility: 'visible' })),
transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')),
]),
],
})
export class RectableComponent implements OnInit {
@Input() array;
@Input() isTop: boolean;
theData = [];
displayedColumns = ["name", "min", "max", "value"];
isExpansionDetailRow = (i: number, row: Object) => row.hasOwnProperty('detailRow');
constructor() {
}
ngOnInit() {
this.theData = observableOf(this.array);
}
}
html的根组件是
<app-rectable [array]='data' [isTop]="true"></app-rectable>
data
是一个JSON对象,在这里我不希望对其进行明确说明。
答案 0 :(得分:3)
因为在您的ngOnInit
中,您正在将data
的类型从Array
更改为Observable
。
在这种情况下,由于绑定到[dataSource]
的类型为dataSource: DataSource<T> | Observable<T[]> | T[]
,因此可以简单地让父组件通过异步管道将数据传递到子组件。
父母:
@Component({
selector: 'parent-component',
template: `
<div>
<app-rectable [data]="data$ | async" [isTop]="true"></app-rectable>
...
</div>
`,
})
export class ParentComponent implements OnInit {
data$: Observable<SomeType[]>;
constructor(private dataService: DataService) {
}
ngOnInit() {
// fetch your data via the service
this.data$ = this.dataService.fetchData();
}
}
孩子:
@Component({
selector: 'app-rectable',
template: `
<div class="example-container mat-elevation-z8">
<mat-table [dataSource]="data">
...
</div>
`,
})
export class RectableComponent implements OnInit {
@Input() data: SomeType[];
@Input() isTop: boolean;
displayedColumns = ["name", "min", "max", "value"];
isExpansionDetailRow = (i: number, row: Object) => row.hasOwnProperty('detailRow');
constructor() {
}
}
答案 1 :(得分:1)
我不知道此行this.theData = observableOf(this.array);
的用途是什么,但这是破坏代码的原因(我认为导致您这样做的原因是对Angular @Input()
装饰器和{ {1}}运算符,它使用数组的初始值发出一个事件。
通过执行此操作,您断开了Observable.of()
的引用,因此从父组件更新引用时,模板不会更新,因为它已绑定到@Input() array
和{{1} }仅在组件初始化时设置。
那么解决方法是:
theData
有实际用途,请在您的组件中实现theData
并将this.theData = observableOf(this.array);
更新为OnChanges
theData
没有实际目的(并且我坚信没有),那么只需完全删除ngOnChanges()
并将模板绑定到this.theData = observableOf(this.array);
答案 2 :(得分:1)
Angular 2+是数据绑定的一种方式。
因此,如果您更改根目录中的数据,则可以看到组件内部的更改,但是当根目录中的数据更改时,您就看不到根目录中的更改。
您可以通过以下方式使其工作:
import { EventEmitter} from '@angular/core';
export class YourComponent {
@Input() item: any;
@Output() itemChange = new EventEmitter();
emitItem() {
this.itemChange.emit(this.item);
}
}
这可以解决问题。
this.itemChange.emit(this.item);
我用这种方式
<input matInput type=number [(ngModel)]="item" (ngModelChange)="emitItem()" (input)="update($event)">
因此,当项目更改时,将调用generateItem,然后在根组件中进行更改。
我希望这会有所帮助。