4

Ich habe derzeit eine Datentabelle, die mit Daten gefüllt wird, die von Firestore stammen. Ich habe auch MatTableDataSource verwendet, um Paginierung, Sortierung und Filterung zu implementieren. Alle 3 funktionieren gut, aber aus irgendeinem Grund werden meine Daten nur einmal geladen, wenn die Seite aktualisiert wird. Wenn ich auf eine andere Seite und dann zurück zum Tisch gehe, sind die Daten verschwunden. Ich weiß nicht, warum das passiert. Unten ist mein Code.Eckiges Material MatTableDataSource mit Firestore

Dienst

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; 
    } 

} 

Komponente

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> 
+0

Mögliche Duplikate von [Daten einmal mit Observables in Angular 2 abrufen] (https://stackoverflow.com/questions/40530108/fetch-data-once-with-observables-in-angular-2) –

+0

Ich denke, mein Problem ist, dass ich es nur einmal hole, was erklärt, warum die Daten nach einem Routerwechsel verschwinden. Ich denke, es hat etwas mit MatTableDataSource zu tun, aber ich bin zu viel Newb zu wissen. – Kyle

+0

überprüfen Sie einfach den obigen Link..Es erklärt das gleiche –

Antwort

1

Dies kann für die getAccounts Verfahren besser funktionieren:

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 } 
     }); 
    }); 
    } 

ich nie einen Fires Anruf im Konstruktor des Dienstes aber immer die Datenbank-Anrufe in einem Verfahren versucht haben, die in während der ngOnInit aufgerufen wird meine Komponente.

Also in der Komponente könnten Sie ein Objekt accounts: Observable<Account[]> haben, das vom Typ Observable<Account[]> ist und setzen Sie es gleich getAccountsX(). Dann in Ihrem Markup würde ich * ngIf die gesamte Tabelle so: *ngIf="(accounts | async) as acts". Dann wäre die dataSource eigentlich acts. Ich habe die DataTable noch nie verwendet, aber dies ist nur ein Ansatz, den ich versuchen würde, um das Abonnement für die Daten aktiv zu halten. Wenn du willst, kann ich deine Frage damit BEARBEITEN.

EDIT:

Hier ist eine Erklärung von zwei getrennten Wegen dieses Abonnement zu handhaben:

Also hier in meiner Komponente I die beobachtbaren sind holen und dann abonnieren möchte auch sie das Array jegliche Daten zu speichern Modell, das Sie holen:

accountsObservable: Observable<Account[]>; 
    accountsArray: Account[]; 

    constructor(
    private ds: DatabaseService 
) {} 

    ngOnInit() { 
    this.accountsObservable = this.ds.getAccountsX(); 

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

Dann hier in meinem Markup können Sie das Abonnement erstellen mit * ngFor und dem Asynchron-Rohr oder einfach Schleife durch das Array, nachdem er durch das Abonnement bestätigt wurde:

Ein Grund für das Warten auf das Abonnement im Komponentencode ist, wenn die Daten vor dem Ausspucken auf der Benutzeroberfläche manipuliert werden müssen. Ich glaube, wenn Sie die zweite Option des Abonnierens im Komponentencode anstelle Ihres Markups verwenden, möchten Sie sicherstellen, dass * ngFor nicht versucht, ein leeres Array zu durchlaufen, da das Abonnement das Array möglicherweise nicht vor dem Inhalt festgelegt hat möchte das DOM laden.So würde ich die accountsArray *ngIf sicherzustellen, dass es eingestellt ist, zuerst wie folgt:

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

Zugegeben ist dies nicht die MatDataTable Verwendung als ich ein Beispiel dafür, wie diese Abonnements arbeiten, und das Ziel zeigen wollte, ist ein Abonnement zu verwenden

In Bezug auf Abbestellen, der Grund, dass keine Option ist, weil Sie das beobachtbaren Abonnement für eine Variable wie so einstellen:

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

    subscription.unsubscribe(); 

hoffe, dass ich dabei helfen, den Zustand des Abonnements, wie Sie erklären kann, Durchlaufen die Sammlung oder das Dokument in der Benutzeroberfläche.

+0

Dies endete tatsächlich ohne * ngIf = "(accounts | async) als Taten". In der Konsole greift es die Daten jedes Mal, wenn ich den Router in die Tabelle wechseln. Muss ich noch ngOnDestroy implementieren und abmelden? Wenn ich die neue getAccountsX-Methode in der Komponente abonniere, wird eine zurückgegeben, die ich an MatTableDataSource übergebe. Es ist kein Abonnement, daher kann ich mich nicht abmelden. Ich bin immer noch etwas verwirrt, wenn ich mich von einem Abonnement abmelde. Ich schätze deine Hilfe sehr, deine Antwort war perfekt und führte mich in die richtige Richtung! – Kyle

+0

Hey Kyle, ich habe einige Bearbeitungen vorgenommen, die hoffentlich helfen werden zu erklären, wie dieses Abonnement funktioniert. –

+0

Danke Nick das hilft mir wirklich! Also, wenn ich sowas habe ... 'const subscriptionOne = this.route.paramMap.subscribe (params => { const id = params.get ('account'); const subscriptionTwo = this.accountService.getAccount (id) .subscribe ((Konto: Konto) => { this.dataSource.data = this.account; }); }); ' Würde ich muss sagen ... ' ngOnDestroy() { this.subscriptionOne.abbestellen() this.subscriptionTwo.abbestellen() } ' ? – Kyle

0

Speichern Sie das Abonnement, das Sie Suche von getAccounts(). Abonnieren und abmelden() in ngOnDestroy aufrufen. Ich habe es nicht getestet, aber es könnte hilfreich sein, wenn af2 die Subskriptionen zwischenspeichert, da das Observable niemals eigenständig abgeschlossen wird. Notwendige und gute Praxis zur Vermeidung von Speicherlecks sowieso.

0

versuchen Sie den einfachsten Bruder. Hier.

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; 
     }); 
    }); 
    } 

hoffe das hilft.