angular - 从发送到父模型Angular 9的子组件获取值
问题描述
我创建了一个简单的表单,作为该表单的一部分,我想使用材料表选择而不是选择字段来实现用户选择,以便在选择用户之前查看用户的详细信息。
我得到了表单和 selectmodel 工作但是我不知道如何将 selectionmodel 数据返回到父模型并将其绑定到表单控件。所以希望得到一些帮助
示例代码:
分配.组件.HTML
<form [formGroup]="newAllocationForm">
<mat-horizontal-stepper formArrayName="formArray" linear #stepper>
<mat-step formGroupName="0" [stepControl]="formArray.get([0])">
<ng-template matStepLabel>New Capability Details</ng-template>
<div class="row">
<div class="col">
<mat-form-field class="full-width">
<input matInput placeholder="Vehicle ID" formControlName="vehicleId" required>
<mat-error>
Vehicle Id is <strong>required</strong>
</mat-error>
</mat-form-field>
</div>
</div>
<div>
<button mat-button matStepperNext type="button">Next</button>
</div>
</mat-step>
<mat-step formGroupName="1" [stepControl]="formArray.get([1])">
<ng-template matStepLabel>Select Users</ng-template>
<app-user-table></app-user-table>
<div>
<button mat-button matStepperPrevious type="button">Back</button>
<button mat-button matStepperNext type="button">Next</button>
</div>
</mat-step>
<mat-step>
<ng-template matStepLabel>Done</ng-template>
<p>You are now done.</p>
<div>
<button mat-raised-button color="primary" type="submit" (click)="onSubmit()"
[disabled]="newAllocationForm.invalid">Submit</button>
<button mat-button matStepperPrevious>Back</button>
<button mat-button (click)="stepper.reset()">Reset</button>
</div>
</mat-step>
</mat-horizontal-stepper>
</form>
分配组件.ts
export class CapabilityNewComponent implements OnInit {
newAllocationForm= this.fb.group({
formArray: this.fb.array([
this.fb.group({
vehicleId: ['', Validators.required],
}),
this.fb.group({
users: ['']
})
])
});
constructor(private fb: FormBuilder) { }
ngOnInit(): void {
}
get formArray(): FormArray | null { return this.newAllocationForm.get('formArray') as FormArray; }
onSubmit() {
// get values from child component table and attach to parent form
}
}
用户表.component.ts
export class UserTableComponent implements OnInit {
@ViewChild(MatPaginator) paginator: MatPaginator;
@ViewChild(MatSort) sort: MatSort;
@ViewChild(MatTable) table: MatTable<UserTableItem>;
dataSource: MatTableDataSource<any>;
selection = new SelectionModel<any>(true, []);
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
displayedColumns = ['select', 'firstName', 'lastName', 'position'];
constructor(public userService: UserService) {
userService.getUsers().subscribe(res => console.log(res));
}
ngOnInit() {
this.userService.getUsers().subscribe(res => {
this.dataSource = new MatTableDataSource(res);
this.dataSource.sort = this.sort;
this.dataSource.paginator = this.paginator;
});
}
applyFilter(event: Event) {
const filterValue = (event.target as HTMLInputElement).value;
this.dataSource.filter = filterValue.trim().toLowerCase();
}
isAllSelected() {
const numSelected = this.selection.selected.length;
const numRows = this.dataSource.data.length;
return numSelected === numRows;
}
masterToggle() {
this.isAllSelected() ?
this.selection.clear() :
this.dataSource.data.forEach(row => this.selection.select(row));
}
}
user-table.component.html
<div class="mat-elevation-z8">
<table mat-table [dataSource]="dataSource" class="full-width-table" matSort aria-label="Elements">
<ng-container matColumnDef="select">
<th mat-header-cell *matHeaderCellDef>
<mat-checkbox (change)="$event ? masterToggle() : null" [checked]="selection.hasValue() && isAllSelected()"
[indeterminate]="selection.hasValue() && !isAllSelected()">
</mat-checkbox>
</th>
<td mat-cell *matCellDef="let row">
<mat-checkbox (click)="$event.stopPropagation()" (change)="$event ? selection.toggle(row) : null"
[checked]="selection.isSelected(row)">
</mat-checkbox>
</td>
</ng-container>
<ng-container matColumnDef="firstName">
<th mat-header-cell *matHeaderCellDef mat-sort-header>First Name</th>
<td mat-cell *matCellDef="let row">{{row.FirstName}}</td>
</ng-container>
<ng-container matColumnDef="lastName">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Last Name</th>
<td mat-cell *matCellDef="let row">{{row.LastName}}</td>
</ng-container>
<ng-container matColumnDef="position">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Position</th>
<td mat-cell *matCellDef="let row">{{row.Position}}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
<mat-paginator #paginator [length]="dataSource?.data.length" [pageIndex]="0" [pageSize]="10"
[pageSizeOptions]="[25, 50, 100, 250]">
</mat-paginator>
</div>
如您所见,这主要是取自 Angular Material 网站的标准代码(没什么花哨的)。我研究了@Output,但无法理解它在这个模型上的工作方式。
任何意见,将不胜感激。
解决方案
要将实例UserTableComponent
用作常规表单控件,您需要它来实现ControlValueAccessor
接口。
我在这里整理了一个 Stackblitz 演示。要查看输出,请不要使用 Stackblitz 嵌入式开发工具。使用浏览器的原生开发工具。
此接口允许任何自定义角度组件成为角度形式的一部分。
它由4种方法组成:
writeValue(obj: any): void 这个方法被 angular 用来在你使用
AbstractControl#setValue
.registerOnChange(fn: any): void 当
fn
组件的整体值发生变化时,必须调用通过 angular 传递给组件的方法 ( )。新值必须作为参数传递。registerOnTouched(fn: any): void
fn
当你认为你的组件必须通知 angular 它被触摸时,必须调用由 angular 传递给你的组件的方法。setDisabledState(isDisabled: boolean)?: void 这个方法是可选的,每当父组件调用
AbstractControl#enable
和AbstractControl#disable
做完以上4项后,还需要提供你的类作为控件值访问器提供者。
所以:
@Component({
...
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => UserTableComponent),
multi: true
}]
})
export class UserTableComponent implements ControlValueAccessor, OnInit {
onChange: (any[] | null) => void =
(SelectionModel<any> | null) => {};
onTouched: () => void = () => {};
writeValue(value: SelectionModel<any> | null) {
// set the values of your component
}
registerOnChange(fn: (any | null) => void) {
this.onChange = fn;
}
registerOnTouched(fn: () => void) {
this.onTouched = fn;
}
setDisabledState(isDisabled: boolean) {
if(isDisabled) {
// prevent your table to be selected
} else {
// enable your table for being selected
}
}
// whenever the selection changes, you should emit it again
notifyChanges() {
this.onChange(this.selection);
}
}
在您的表格组件中,您将拥有以下内容:
<mat-checkbox (click)="$event.stopPropagation()"
(change)="_setSelection($event, row)"
[checked]="selection.isSelected(row)">
</mat-checkbox>
在你的打字稿中:
_setSelection(row: any, change: EventEmitter<MatCheckboxChange>) {
if(change) {
selection.toggle(row);
this.onChange(this.selection.selected);
}
}
之后你可以这样做:
<app-user-table formControlName="controlName"></app-user-name>
推荐阅读
- javascript - 这样的逻辑可以写成单行吗?
- javascript - scrollTop 到 div 的全高
- php - Yii2 检查迁移中是否存在表
- c++ - 按位代码解释
- javascript - Firestore 安全规则 - 查询集合时检查文档字段
- javascript - localhost - Mozilla 无法加载 js 文件
- java - 在我的情况下,同步关键字不能保证线程安全
- python - NameError:未定义名称“generate_dendrogram”
- java - 如何在 java 中的 char 上使用 .set() 或 .setvalue()
- javascript - 如何访问同一 javascript 对象的成员?