首页 文章

Angular材质MatTableDataSource与Firestore

提问于
浏览
7

我目前有一个数据表正在填充来自Firestore的数据 . 我还使用MatTableDataSource来实现分页,排序和过滤 . 所有3个工作正常但由于某种原因我的数据只在页面刷新时加载一次 . 如果我转到另一个页面然后回到表格,数据就会消失 . 我不知道为什么会这样 . 以下是我的代码 .

Service

import { Injectable } from '@angular/core';
import { AngularFirestore, AngularFirestoreCollection, AngularFirestoreDocument } from 'angularfire2/firestore';
import { Account } from './../models/account.model';
import { Observable } from 'rxjs/Observable';

@Injectable()
export class AccountService {
  accountsCollection: AngularFirestoreCollection<Account>;
  accounts: Observable<Account[]>;

  constructor(public afs: AngularFirestore) {
    this.accountsCollection = afs.collection('accounts');

    this.accounts = this.accountsCollection.snapshotChanges().map(changes => {
      return changes.map(a => {
        const data = a.payload.doc.data() as Account;
        data.id = a.payload.doc.id;
        return data;
      });
    });

  }

  getAccounts() {
    return this.accounts;
  }

}

Component

import { Account } from './../../../models/account.model';
import { Component, ViewChild, OnInit } from '@angular/core';
import { MatPaginator, MatSort, MatTableDataSource } from '@angular/material';
import { AccountService } from '../../../services/account.service';
import { AfterViewInit } from '@angular/core/src/metadata/lifecycle_hooks';

@Component({
  selector: 'app-account-table',
  templateUrl: './account-table.component.html',
  styleUrls: ['./account-table.component.css']
})
export class AccountTableComponent implements AfterViewInit {
  dataSource = new MatTableDataSource<Account>();
  displayedColumns = [
    'salesStep',
    'status',
    'idn',
    'hospital',
    'state',
    'regionalManager',
    'accountExecutive',
    'clientLiaison',
    'gpo'
  ];

  @ViewChild(MatPaginator) paginator: MatPaginator;
  @ViewChild(MatSort) sort: MatSort;

  constructor(private accountService: AccountService) {}

  ngAfterViewInit() {
    this.accountService.getAccounts().subscribe(data => {
      this.dataSource.data = data;
      console.log(this.dataSource.data);
    });
    this.dataSource.paginator = this.paginator;
    this.dataSource.sort = this.sort;
  }

  applyFilter(filterValue: string) {
    filterValue = filterValue.trim(); // Remove whitespace
    filterValue = filterValue.toLowerCase(); // Datasource defaults to lowercase matches
    this.dataSource.filter = filterValue;
  }

}

HTML

<div class="example-header">
  <mat-form-field>
    <input matInput #filter (keyup)="applyFilter($event.target.value)" placeholder="Search">
  </mat-form-field>
</div>

<mat-card class="example-container">

  <mat-table #table [dataSource]="dataSource" matSort>

    <!--- Note that these columns can be defined in any order.
          The actual rendered columns are set as a property on the row definition" -->

    <!-- Sales Step Column -->
    <ng-container matColumnDef="salesStep">
      <mat-header-cell *matHeaderCellDef mat-sort-header> Sales Step </mat-header-cell>
      <mat-cell *matCellDef="let row"> {{row.salesStep}} </mat-cell>
    </ng-container>

    <!-- Status Column -->
    <ng-container matColumnDef="status">
      <mat-header-cell *matHeaderCellDef mat-sort-header> Status </mat-header-cell>
      <mat-cell *matCellDef="let row"> {{row.status}} </mat-cell>
    </ng-container>

    <!-- IDN Column -->
    <ng-container matColumnDef="idn">
      <mat-header-cell *matHeaderCellDef mat-sort-header> IDN </mat-header-cell>
      <mat-cell *matCellDef="let row"> {{row.idn}} </mat-cell>
    </ng-container>

    <!-- Hospital Column -->
    <ng-container matColumnDef="hospital">
      <mat-header-cell *matHeaderCellDef mat-sort-header> Hospital </mat-header-cell>
      <mat-cell *matCellDef="let row"> {{row.hospital}} </mat-cell>
    </ng-container>

    <!-- State Column -->
    <ng-container matColumnDef="state">
      <mat-header-cell *matHeaderCellDef mat-sort-header> State </mat-header-cell>
      <mat-cell *matCellDef="let row"> {{row.state}} </mat-cell>
    </ng-container>

    <!-- Regional Manager Column -->
    <ng-container matColumnDef="regionalManager">
      <mat-header-cell *matHeaderCellDef mat-sort-header> RM </mat-header-cell>
      <mat-cell *matCellDef="let row"> {{row.regionalManager}} </mat-cell>
    </ng-container>

    <!-- Account Executive Column -->
    <ng-container matColumnDef="accountExecutive">
      <mat-header-cell *matHeaderCellDef mat-sort-header> AE </mat-header-cell>
      <mat-cell *matCellDef="let row"> {{row.accountExecutive}} </mat-cell>
    </ng-container>

    <!-- Client Liaison Column -->
    <ng-container matColumnDef="clientLiaison">
      <mat-header-cell *matHeaderCellDef mat-sort-header> CL </mat-header-cell>
      <mat-cell *matCellDef="let row"> {{row.clientLiaison}} </mat-cell>
    </ng-container>

    <!-- GPO Column -->
    <ng-container matColumnDef="gpo">
      <mat-header-cell *matHeaderCellDef mat-sort-header> GPO </mat-header-cell>
      <mat-cell *matCellDef="let row"> {{row.gpo}} </mat-cell>
    </ng-container>



    <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
    <mat-row *matRowDef="let row; columns: displayedColumns;">
    </mat-row>
  </mat-table>

  <!-- <div class="example-no-results"
       [style.display]="(accountService.accounts | async)?.length">
    No accounts found matching filter.
  </div> -->

  <mat-paginator #paginator
                [pageSize]="10"
                [pageSizeOptions]="[5, 10, 20]">
  </mat-paginator>
</mat-card>

3 回答

  • 0

    保存从getAccounts()获得的订阅 . 订阅并在ngOnDestroy中调用unsubscribe() . 我没有测试但是如果af2缓存订阅可能会有所帮助,因为observable永远不会自己完成 . 无论如何,避免内存泄漏的必要和良好实践 .

  • 0

    试试最简单的兄弟 . 这里 .

    constructor(public afs: AngularFirestore) {
        this.accountsCollection = afs.collection('accounts');
    }
    
    getAccounts() {
        return this.accounts = this.accountsCollection.snapshotChanges().map(changes => {
          return changes.map(a => {
            const data = a.payload.doc.data() as Account;
            data.id = a.payload.doc.id;
            return data;
          });
        });
      }
    

    希望这可以帮助 .

  • 2

    这可能对getAccounts方法更有效:

    getAccountsX() {
        return this.afs.collection<Account[]>('accounts').snapshotChanges().map((accounts) => {
          return accounts.map(a => {
            const data = a.payload.doc.data() as Account;
            const id = a.payload.doc.id;
            return { id, ...data }
          });
        });
      }
    

    我从未尝试在服务的构造函数中进行firestore调用,但总是在我的组件中的ngOnInit期间调用的方法中进行数据库调用 .

    因此,在组件中,您可以拥有 Observable<Account[]> 类型的对象 accounts: Observable<Account[]> ,并将其设置为等于getAccountsX() . 然后在你的标记中我会像这样整个表: *ngIf="(accounts | async) as acts" . 然后dataSource实际上是 acts . 我从来没有使用过DataTable,但这只是我尝试将数据的订阅保持活跃的一种方法 . 如果你想我可以用这个编辑你的问题 .

    编辑:

    以下是处理该订阅的两种不同方法的说明:

    所以在我的组件中,我获取Observable,然后订阅它以保存您获取的任何数据模型的数组:

    accountsObservable: Observable<Account[]>;
      accountsArray: Account[];
    
      constructor(
        private ds: DatabaseService
      ) {}
    
      ngOnInit() {
        this.accountsObservable = this.ds.getAccountsX();
    
        this.accountsObservable.subscribe(accounts => {
          this.accountsArray = accounts;
        });
      }
    

    然后在我的标记中,您可以使用* ngFor和ASYNC管道创建订阅,或者只需在订阅确认后循环遍历数组:

    <!-- This div below is subscribing to the Observable in markup using the 'async' pipe to make sure it waits for data -->
    <div id="markup-subscription" *ngFor="let act of (accountsObservable | async)">
      <p>{{ act?.id }}</p>
    </div>
    
    <!-- This div below is looping through the array that was pulled from the subscription of the Observable -->
    <div id="component-subscription" *ngFor="let act of accountsArray">
      <p>{{ act?.id }}</p>
    </div>
    

    在组件代码中等待订阅的一个原因是,在将数据吐出到UI之前需要操作数据 . 我相信如果你在组件代码而不是标记中使用第二个订阅选项,你会想确保* ngFor没有尝试循环一个空数组,因为订阅可能没有在内容之前设置数组想加载DOM . 所以我会 *ngIf accountsArray确保它首先设置如下:

    <div id="component-subscription" *ngIf="accountsArray" *ngFor="let act of accountsArray">
    

    当然,这不是使用MatDataTable,因为我想展示这些订阅如何工作的示例,目标是使用一个订阅

    关于取消订阅,不是选项的原因是因为您必须将Observable订阅设置为如下变量:

    const subscription = this.accountsObservable.subscribe(accounts => {
          this.accountsArray = accounts;
        });
    
        subscription.unsubscribe();
    

    我希望这可以帮助解释订阅的状态,因为您在UI中循环收集或文档 .

相关问题