Angular - 对 FormArray 进行排序

问题描述 投票:0回答:1

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>
angular typescript material-ui
1个回答
1
投票

这是所做更改的摘要。

  1. 我们需要在表格顶部添加

    [formGroup]
    ,因为它是根位置。您可能不需要它,因为您在未共享代码的地方有它,所以如果需要请使用

  2. 我将

    formArrayName
    移至
    tbody
    ,因为它应该是
    *ngFor

    的父元素
  3. 我将

    [formGroupName]
    移至
    *ngFor
    行,因为它应该是表单元素的父级

  4. 确保已将

    MatSortModule
    导入到子组件

  5. 确保将

    provideAnimations()
    导入到
    bootstrapApplication

    的提供者数组中
  6. 您交替使用

    Books
    books
    ,这是不正确的,我将全部重命名为
    books

  7. 主要问题是我们对数据而不是表单控件进行排序;由于我们使用表单控件来创建

    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()],
});

Stackblitz 演示

© www.soinside.com 2019 - 2024. All rights reserved.