Angular 新手。我有一个输入类 Foo,其中有一个书籍列表,其中 Book 类具有 Id、Title 和 Description 属性。我将其显示在带有输入的表格中,以便用户可以添加、编辑或删除图书列表。某处有一个按钮可以添加和删除一本书。这工作正常。
我想添加一个排序,但它不起作用。我在应用程序模块中添加了 MatSort 和 Sort。我放置了 Angular 本身的排序代码块。我做错了什么?
我应该在 MatTable 中更改它而不是循环遍历表单数组吗?如果是这样,我该如何使用输入来做到这一点,而不是将每个数据显示为变量 {{element.title}} 等?
感谢所有的帮助。
TS
@Input() foo: Foo;
@ViewChild(MatSort, {static: true}) sort: MatSort;
bookForm: FormArray;
orderForm: FormGroup;
bookList !: Book[];
bookSorted : Book[];
initForm() {
this.orderForm= this._formBuilder.group( {
customerForm: this._formBuilder.array( [] ),
bookForm: this._formBuilder.array( [] )
} );
this.addedBooks()
this.bookList = this.foo.Books;
}
addedBooks() {
this.bookForm= this.orderForm.get( 'bookForm' ) as FormArray;
this.bookForm.clear();
let _bookForm = this.foo.books?.map( _book => this.addBook( _book ) );
_bookForm?.forEach( _addBook => this.bookForm.push( _addBook ) );
}
addBook( _book) {
return this._formBuilder.group( {
title: new FormControl( _book?.title),
description: new FormControl( _book?.description ),
id: new FormControl( _book?.id ?? Guid.EMPTY ),
} );
}
get bookFormControls() {
return ( this.orderForm.get( 'bookForm' ) as FormArray ).controls;
}
sortBook(sort: Sort) {
const data = this.bookList.slice();
if (!sort.active || sort.direction == '') {
this.bookSorted = data;
return;
}
this.bookSorted = data.sort((a, b) => {
let isAsc = sort.direction == 'asc';
switch (sort.active) {
case 'title': return this.compare(a.title, b.title, isAsc);
case 'description': return this.compare(+a.description, +b.description, isAsc);
default: return 0;
}
});
}
compare(a, b, isAsc) {
return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
}
removeBooksAt( index ) {
this.dialogName = "Book"
this.modalRef = this.dialog.open( this.deleteBook, {
width: '600px',
} );
this.modalRef.afterClosed().subscribe( res => {
if ( res ) this.bookForm.removeAt( index );
} );
}
addNewBook() {
let formValue = this.orderForm.controls['bookForm'] as FormArray;
formValue.status == 'VALID' ? this.createBooksForm() : this.showToast();
}
createBooksForm(data?: any) {
this.booksForm = this.orderForm.get( 'booksForm ' ) as FormArray;
this.booksForm .push( this.addBooksControls(data) );
}
addBooksControls(data?: any): FormGroup {
return this._formBuilder.group( {
title: [data?.title ??'', Validators.required],
description: [data?.description ??'', Validators.required],
id: [data?.id ??Guid.EMPTY]
} );
}
HTML
<!--Mat Sort Test-->
<fieldset>
<div>
<legend>Books</legend>
<table matSort (matSortChange)="sortBook($event)" class="card-table">
<thead class="primary-color">
<tr>
<th mat-sort-header="title">
Book Title
</th>
<th mat-sort-header="description">
Description
</th>
<th class="colums-name">
Actions
</th>
</tr>
</thead>
<tbody>
<tr class="margin-1" formArrayName="bookForm"
*ngFor="let group of bookFormControls; let _i = index;">
<td [formGroupName]="_i">
<input type="text" formControlName="title" class="margin-1 readonly" placeholder="Add title">
</td>
<td [formGroupName]="_i">
<input type="text" formControlName="description" class="margin-1 readonly"
placeholder="Add description">
<input type="hidden" formControlName="id">
</td>
<td style="text-align: center;">
<i (click)="removeBooksAt(_i, 'Title')" class="fa fa-trash margin-right-mini"
style="color:darkgrey; font-size: xx-large;;" aria-hidden="true"></i>
</td>
</tr>
</tbody>
</table>
</div>
</fieldset>
这是所做更改的摘要。
我们需要在表格顶部添加
[formGroup]
,因为它是根位置。您可能不需要它,因为您在未共享代码的地方有它,所以如果需要请使用
我将
formArrayName
移至 tbody
,因为它应该是 *ngFor
的父元素
我将
[formGroupName]
移至 *ngFor
行,因为它应该是表单元素的父级
确保已将
MatSortModule
导入到子组件
确保将
provideAnimations()
导入到 bootstrapApplication
的提供者数组中
您交替使用
Books
和books
,这是不正确的,我将全部重命名为books
主要问题是我们对数据而不是表单控件进行排序;由于我们使用表单控件来创建
for
循环,因此我们也应该使用相同的 formGroup array
进行排序。
排序代码更改:
sortBook(sort: Sort) {
debugger;
if (!sort.active || sort.direction == '') {
return;
}
(<Array<FormGroup>>this.bookFormControls).sort(
(a: FormGroup, b: FormGroup) => {
let isAsc = sort.direction == 'asc';
switch (sort.active) {
case 'title':
return this.compare(
a?.controls?.['title']?.value,
a?.controls?.['title']?.value,
isAsc
);
case 'description':
return this.compare(
a?.controls?.['description']?.value,
b?.controls?.['description']?.value,
isAsc
);
default:
return 0;
}
}
);
}
我可能遗漏了一些要点,请仔细阅读代码,如果有任何疑问请告诉我,请找到下面的完整代码和 stackblitz
儿童TS
import { CommonModule } from '@angular/common';
import { Component, Input, ViewChild } from '@angular/core';
import {
FormArray,
FormBuilder,
FormControl,
FormGroup,
ReactiveFormsModule,
Validators,
} from '@angular/forms';
import { MatSort, MatSortModule, Sort } from '@angular/material/sort';
import { MatTableModule } from '@angular/material/table';
@Component({
selector: 'app-child',
standalone: true,
imports: [MatTableModule, CommonModule, ReactiveFormsModule, MatSortModule],
templateUrl: './child.component.html',
styleUrl: './child.component.css',
})
export class ChildComponent {
@Input() foo: any;
@ViewChild(MatSort, { static: true }) sort!: MatSort;
bookForm!: FormArray;
orderForm!: FormGroup;
bookList!: any[];
bookSorted!: any[];
constructor(private _formBuilder: FormBuilder) {}
ngOnInit() {
this.initForm();
}
initForm() {
this.orderForm = this._formBuilder.group({
customerForm: this._formBuilder.array([]),
bookForm: this._formBuilder.array([]),
});
this.addedBooks();
this.bookList = this.foo.books;
}
addedBooks() {
this.bookForm = this.orderForm.get('bookForm') as FormArray;
this.bookForm.clear();
let _bookForm = this.foo.books?.map((_book: any) => this.addBook(_book));
_bookForm?.forEach((_addBook: any) => this.bookForm.push(_addBook));
}
addBook(_book: any) {
return this._formBuilder.group({
title: new FormControl(_book?.title),
description: new FormControl(_book?.description),
id: new FormControl(_book?.id ?? Math.random()),
});
}
get bookFormControls() {
return (this.orderForm.get('bookForm') as FormArray).controls;
}
sortBook(sort: Sort) {
debugger;
if (!sort.active || sort.direction == '') {
return;
}
(<Array<FormGroup>>this.bookFormControls).sort(
(a: FormGroup, b: FormGroup) => {
let isAsc = sort.direction == 'asc';
switch (sort.active) {
case 'title':
return this.compare(
a?.controls?.['title']?.value,
a?.controls?.['title']?.value,
isAsc
);
case 'description':
return this.compare(
a?.controls?.['description']?.value,
b?.controls?.['description']?.value,
isAsc
);
default:
return 0;
}
}
);
}
compare(a: any, b: any, isAsc: any) {
return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
}
removeBooksAt(index: number) {
// this.dialogName = "Book"
// this.modalRef = this.dialog.open( this.deleteBook, {
// width: '600px',
// } );
// this.modalRef.afterClosed().subscribe( res => {
// if ( res )
this.bookForm.removeAt(index);
// } );
}
addNewBook() {
let formValue = this.orderForm.controls['bookForm'] as FormArray;
formValue.status == 'VALID' ? this.createBooksForm() : this.showToast();
}
showToast() {
alert('show status');
}
createBooksForm(data?: any) {
this.bookForm = this.orderForm.get('booksForm') as FormArray;
this.bookForm.push(this.addBooksControls(data));
}
addBooksControls(data?: any): FormGroup {
return this._formBuilder.group({
role: [data?.title ?? '', Validators.required],
description: [data?.description ?? '', Validators.required],
id: [data?.id ?? ''],
});
}
}
子 HTML
<table
matSort
(matSortChange)="sortBook($event)"
class="card-table"
[formGroup]="orderForm"
>
<thead class="primary-color">
<tr>
<th mat-sort-header="title">Book Title</th>
<th mat-sort-header="description">Description</th>
<th class="colums-name">Actions</th>
</tr>
</thead>
<tbody formArrayName="bookForm">
<tr
class="margin-1"
*ngFor="let group of bookFormControls; let _i = index"
[formGroupName]="_i"
>
<td>
<input
type="text"
formControlName="title"
class="margin-1 readonly"
placeholder="Add title"
/>
</td>
<td>
<input
type="text"
formControlName="description"
class="margin-1 readonly"
placeholder="Add description"
/>
<input type="hidden" formControlName="id" />
</td>
<td style="text-align: center;">
<i
(click)="removeBooksAt(_i)"
class="fa fa-trash margin-right-mini"
style="color:darkgrey; font-size: xx-large;;"
aria-hidden="true"
></i>
</td>
</tr>
</tbody>
</table>
家长
import { Component } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import 'zone.js';
import { ChildComponent } from './app/child/child.component';
import { provideAnimations } from '@angular/platform-browser/animations';
@Component({
selector: 'app-root',
standalone: true,
imports: [ChildComponent],
template: `
<app-child [foo]="foo"></app-child>
`,
})
export class App {
foo = {
books: [
{ id: 1, title: 'test', description: 'test' },
{ id: 2, title: 'test2', description: 'test2' },
{ id: 3, title: 'test3', description: 'test3' },
],
};
name = 'Angular';
}
bootstrapApplication(App, {
providers: [provideAnimations()],
});