簡體   English   中英

MatTable 數據源上的異步 pipe 未排序,表格內容更新正常

[英]Async pipe on MatTable datasource doesn't sort, table contents update fine

我有一個 MatTable 可以在基礎數據更改/添加/刪除時正確加載和刷新,我遇到的唯一問題是單擊其中一個標題時它沒有排序,出現小箭頭圖標但沒有變化。 我在 html 中對 Observable 使用條件,並在 ngOnInit() 中設置了排序,但我嘗試過的所有操作都不會觸發任何排序。 我找不到任何同時執行異步 pipe 和 matSort 的示例,其中有很多示例。 任何見解將不勝感激。

html:

   <mat-card-header layout="row">
      <mat-card-title style="margin-bottom: 4vh">
         <span>{{ msg }}</span>
      </mat-card-title>
   </mat-card-header>
   <mat-card-content>
      <table
      *ngIf="expenseDataSource$ | async as expenses"
      mat-table
      [dataSource]="expenses"
      matSort
      matSortDisableClear
      #expTbSort="matSort"
      class="mat-elevation-z8"
      >
      <ng-container matColumnDef="id">
         <th mat-header-cell *matHeaderCellDef mat-sort-header>
            <div class="center-header" style="width: 50%">Expense</div>
         </th>
         <td mat-cell *matCellDef="let element">{{ element.id }}</td>
      </ng-container>
      <!-- Date Column -->
      <ng-container matColumnDef="dateincurred">
         <th
            mat-header-cell
            *matHeaderCellDef
            mat-sort-header
            >
            <div class="center-header">Expense Date</div>
         </th>
         <td mat-cell *matCellDef="let element">{{ element.dateincurred }}</td>
      </ng-container>
      <!-- Employee Id Column  -->
      <ng-container matColumnDef="employeeid">
         <th mat-header-cell *matHeaderCellDef mat-sort-header>
            <div class="center-header">Employee</div>
         </th>
         <td mat-cell *matCellDef="let element">{{ element.employeeid }}</td>
      </ng-container>
      <tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
      <tr
      mat-row
      *matRowDef="let row; columns: displayedColumns"
      (click)="select(row)"
      ></tr>
      </table>
      <div class="padtop15">
         <mat-icon
            (click)="newExpense()"
            matTooltip="Add New Expense"
            class="addicon"
            color="primary"
            >
            control_point
         </mat-icon>
      </div>
   </mat-card-content>
</mat-card>
<mat-card *ngIf="!hideEditForm">
   <mat-card-header layout="row">
      <mat-card-title
         ><span>{{ msg }}</span></mat-card-title
         >
   </mat-card-header>
   <mat-card-content>
      <app-expense-detail
      [selectedExpense]="expense"
      [employees]="employees$ | async"
      (cancelled)="cancel('cancelled')"
      (saved)="save($event)"
      (deleted)="delete($event)"
      >
      </app-expense-detail>
   </mat-card-content>
</mat-card>

ts:

import { Component, OnInit, ViewChild } from '@angular/core';
import { MatTableDataSource } from '@angular/material/table';
import { MatSort, Sort } from '@angular/material/sort';
import { Expense } from '@app/expense/expense';
import { Employee } from '@app/employee/employee';
import { EmployeeService } from '@app/employee/employeev3.service';
import { ExpenseService } from '@app/expense/expense.service';
import { Observable } from 'rxjs';
import { catchError, tap, map } from 'rxjs/operators';

@Component({
  selector: 'app-expense',
  templateUrl: 'expense-home.component.html',
})
export class ExpenseHomeComponent implements OnInit {
  employees$?: Observable<Employee[]>;
  expenses: Expense[];
  expenses$?: Observable<Expense[]>;
  expenseDataSource$: Observable<MatTableDataSource<Expense>> | undefined;
  expense: Expense;
  hideEditForm: boolean;
  initialLoad: boolean;
  msg: string;
  todo: string;
  url: string;
  size: number = 0;
  displayedColumns: string[] = ['id', 'dateincurred', 'employeeid'];

  @ViewChild('expTbSort') expTbSort = new MatSort();

  constructor(
    private employeeService: EmployeeService,
    private expenseService: ExpenseService
  ) {
    this.hideEditForm = true;
    this.initialLoad = true;
    this.expenses = [];
    this.expense = {
      id: 0,
      employeeid: 0,
      categoryid: '',
      description: '',
      amount: 0.0,
      dateincurred: '',
      receipt: false,
      receiptscan: '',
    };
    this.msg = '';
    this.todo = '';
    this.url = '';
  } // constructor

  ngOnInit(): void {
    this.msg = 'loading expenses from server...';
    this.expenses$ = this.expenseService.get();
    this.expenseDataSource$ = this.expenses$.pipe(
      map((expenses) => {
        const dataSource = new MatTableDataSource<Expense>(expenses);
        // dataSource.data = expenses;
        dataSource.sort = this.expTbSort;
        return dataSource;
      }),
      tap(() => {
        this.employees$ = this.employeeService.get();
        if (this.initialLoad === true) {
          this.msg = 'expenses and employees loaded!';
          this.initialLoad = false;
        }
      })
    );
  }

  select(selectedExpense: Expense): void {
    this.todo = 'update';
    this.expense = selectedExpense;
    this.msg = `Expense ${selectedExpense.id} selected`;
    this.hideEditForm = !this.hideEditForm;
  } // select

  /**
   * cancelled - event handler for cancel button
   */
  cancel(msg?: string): void {
    this.hideEditForm = !this.hideEditForm;
    this.msg = 'operation cancelled';
  } // cancel

  /**
   * update - send changed update to service update local array
   */
  update(selectedExpense: Expense): void {
    this.expenseService.update(selectedExpense).subscribe({
      // Create observer object
      next: (exp: Expense) => (this.msg = `Expense ${exp.id} updated!`),
      error: (err: Error) => (this.msg = `Update failed! - ${err.message}`),
      complete: () => {
        this.hideEditForm = !this.hideEditForm;
      },
    });
  } // update

  /**
   * save - determine whether we're doing and add or an update
   */
  save(expense: Expense): void {
    expense.id ? this.update(expense) : this.add(expense);
  } // save

  /**
   * add - send expense to service, receive newid back
   */
  add(newExpense: Expense): void {
    this.msg = 'Adding expense...';
    newExpense.id = 0;
    this.expenseService.add(newExpense).subscribe({
      // Create observer object
      next: (exp: Expense) => {
        this.msg = `Expense ${exp.id} added!`;
      },
      error: (err: Error) => (this.msg = `Expense not added! - ${err.message}`),
      complete: () => {
        this.hideEditForm = !this.hideEditForm;
      },
    });
  } // add

  /**
   * newExpense - create new expense instance
   */
  newExpense(): void {
    this.expense = {
      id: 0,
      employeeid: 0,
      categoryid: '',
      description: '',
      amount: 0.0,
      dateincurred: '',
      receipt: false,
      receiptscan: '',
    };
    this.msg = 'New expense';
    this.hideEditForm = !this.hideEditForm;
  } // newExpense

  /**
   * delete - send expense id to service for deletion
   */
  delete(selectedExpense: Expense): void {
    this.expenseService.delete(selectedExpense.id).subscribe({
      // Create observer object
      next: (numOfExpensesDeleted: number) => {
        numOfExpensesDeleted === 1
          ? (this.msg = `Expense ${selectedExpense.id} deleted!`)
          : (this.msg = `Expense ${selectedExpense.id} not deleted!`);
      },
      error: (err: Error) => (this.msg = `Delete failed! - ${err.message}`),
      complete: () => {
        this.hideEditForm = !this.hideEditForm;
      },
    });
  }
} // ExpenseHomeComponent

我通過添加自定義排序方法並在表格上添加了 matSortChange 屬性來使排序功能正常工作

我自己一直在處理這個問題。 我認為問題出在您的行: dataSource.sort = this.expTbSort; 在渲染視圖之前運行。

@ViewChild('expTbSort') expTbSort = new MatSort(); 表示變量expTbSort將填充從視圖中獲取的數據,因此視圖必須在分配dataSource.sort之前渲染。

您實現異步 pipe 的方式可確保在 ngOnInit 運行並定義this.expenseDataSource$之前視圖不會呈現。

我不確定您的案例的理想解決方案,但希望這種觀點對您有所幫助。 就我而言,我的表填充了來自 API 調用的響應,因此我在.subscribe()回調中定義了dataSource.sort 我不需要像 Angular Material 文檔中的示例那樣使用ngAfterViewInit生命周期掛鈎。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM