Angular 5中如何用RxJS观测无特定名称的响应JSON对象
Hey there! Let's break down how to dynamically populate your mat-table in Angular 5 using RxJS, even with those anonymous JSON objects from your API response.
First off, even though the objects in the files array are "anonymous", we can still work with them cleanly in Angular (especially with TypeScript) by defining an interface to represent their structure. This gives us type safety and better IDE support—though you could use any if you really want to skip it, interfaces are way better for maintainability.
Step 1: Define an Interface for Your File Objects
Create a new interface file (like file-item.interface.ts) to map the structure of each object in the files array:
export interface FileItem { title: string; link: string; attachment: string; userTypes: string[]; itemid: string; type: number; itemproviderid: number; detailtype: number; }
Step 2: Create a Service to Fetch the Data
Use Angular's HttpClient (available in Angular 4.3+) to call your API, and use RxJS operators to extract the files array from the response.
Here's what the service (e.g., file.service.ts) might look like:
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { FileItem } from './file-item.interface'; @Injectable() export class FileService { constructor(private http: HttpClient) {} getFiles(): Observable<FileItem[]> { // Replace 'your-api-endpoint' with your actual API URL return this.http.get<any>('your-api-endpoint') .pipe( // Extract the 'files' array from the API response map(apiResponse => apiResponse.files) ); } }
Step 3: Connect the Data to Your mat-table Component
Now, in your component, you can either subscribe to the observable manually or use Angular's async pipe (the latter is preferred because it handles subscription cleanup automatically).
Option 1: Using the Async Pipe (Recommended)
This keeps your component code lean and avoids memory leaks from forgotten subscriptions.
Component code (e.g., file-table.component.ts):
import { Component, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; import { FileItem } from './file-item.interface'; import { FileService } from './file.service'; @Component({ selector: 'app-file-table', templateUrl: './file-table.component.html', styleUrls: ['./file-table.component.css'] }) export class FileTableComponent implements OnInit { // Define which columns you want to display in the table displayedColumns: string[] = ['title', 'attachment', 'userTypes', 'type']; // Observable holding our files data files$: Observable<FileItem[]>; constructor(private fileService: FileService) {} ngOnInit(): void { // Fetch the data when the component initializes this.files$ = this.fileService.getFiles(); } }
Template code (file-table.component.html):
<table mat-table [dataSource]="files$ | async" class="mat-elevation-z8"> <!-- Title Column --> <ng-container matColumnDef="title"> <th mat-header-cell *matHeaderCellDef> Title </th> <td mat-cell *matCellDef="let file"> {{file.title}} </td> </ng-container> <!-- Attachment Column --> <ng-container matColumnDef="attachment"> <th mat-header-cell *matHeaderCellDef> Attachment Name </th> <td mat-cell *matCellDef="let file"> {{file.attachment}} </td> </ng-container> <!-- User Types Column (join the array into a string) --> <ng-container matColumnDef="userTypes"> <th mat-header-cell *matHeaderCellDef> Allowed User Types </th> <td mat-cell *matCellDef="let file"> {{file.userTypes.join(', ')}} </td> </ng-container> <!-- Type Column --> <ng-container matColumnDef="type"> <th mat-header-cell *matHeaderCellDef> File Type </th> <td mat-cell *matCellDef="let file"> {{file.type}} </td> </ng-container> <!-- Header and Row Definitions --> <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> </table>
Option 2: Manual Subscription
If you need to do additional processing on the data before passing it to the table, you can subscribe manually. Just remember to unsubscribe in ngOnDestroy to prevent memory leaks!
Component code update:
import { Component, OnInit, OnDestroy } from '@angular/core'; import { Subscription } from 'rxjs'; import { FileItem } from './file-item.interface'; import { FileService } from './file.service'; import { MatTableDataSource } from '@angular/material/table'; @Component({ selector: 'app-file-table', templateUrl: './file-table.component.html', styleUrls: ['./file-table.component.css'] }) export class FileTableComponent implements OnInit, OnDestroy { displayedColumns: string[] = ['title', 'attachment', 'userTypes', 'type']; dataSource: MatTableDataSource<FileItem>; private filesSubscription: Subscription; constructor(private fileService: FileService) {} ngOnInit(): void { this.filesSubscription = this.fileService.getFiles().subscribe(files => { // Optional: Do any data transformation here this.dataSource = new MatTableDataSource(files); }); } ngOnDestroy(): void { // Clean up the subscription when the component is destroyed this.filesSubscription.unsubscribe(); } }
Template update (remove the async pipe):
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8"> <!-- Column definitions same as above --> </table>
Key Notes
- Angular's
HttpClientautomatically parses JSON responses, so you don't need to callJSON.parse()manually. - The RxJS
mapoperator lets us pluck thefilesarray out of the API response, transforming the observable from the full API object to just the array we need. - Using an interface isn't mandatory, but it makes your code more predictable and catches typos early.
- The
asyncpipe is the cleaner approach for most cases, as it manages subscription lifecycle for you.
内容的提问来源于stack exchange,提问作者kamilp

