我想分配一个变量的内容,这个变量是用一个firebase订阅数组填充的



我想分配一个变量的内容,我填充了一个firebase订阅数组,我的问题是我不能分配我在订阅中创建的值。感觉我不能在订阅之外使用创建的值(来自angularfirestore)。我知道如何在.html文件中使用创建的变量,但我需要在组件类中使用它。PS:这些文件是用angular模式创建的&;ng generate @angular/material:table &;

//table-datasource.ts
import { DataSource } from '@angular/cdk/collections';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { map } from 'rxjs/operators';
import { Observable, of as observableOf, merge } from 'rxjs';
import { AuthService } from "../../services/auth.service";
// TODO: Replace this with your own data model type
export interface TableItem {
email: string;
displayName: string;
photoURL: string;
emailVerified: boolean;
}
const EXAMPLE_DATA: TableItem[] = [//this hardcoded data shows but i obviously dont want it hardcoded
{
"emailVerified": false,
"displayName": "string",
"email": "halhfla@test.de",
"photoURL": "string"
},
{
"email": "asf@gfd.de",
"photoURL": "string",
"emailVerified": false,
"displayName": "string",
},
{
"emailVerified": false,
"email": "test@test.de",
"displayName": "string",
"photoURL": "string",
},
{
"displayName": "string",
"photoURL": "string",
"emailVerified": false,
"email": "asdfasdf@hotmail.de",
},
{
"photoURL": "string",
"emailVerified": false,
"displayName": "string",
"email": "hallo@otto.de",
},
{
"email": "gacap36518@aikusy.com",
"photoURL": "string",
"displayName": "string",
"emailVerified": true,
},
{
"photoURL": "string",
"emailVerified": false,
"displayName": "string",
"email": "hahha@test.de",
},
];

/**
* Data source for the Table view. This class should
* encapsulate all logic for fetching and manipulating the displayed data
* (including sorting, pagination, and filtering).
*/
export class TableDataSource extends DataSource<TableItem> {
//data: TableItem[] = EXAMPLE_DATA; //works with hardcoded data
data: TableItem[] = []; //i basicly need data to be the res of the subscription(see below)
paginator: MatPaginator | undefined;
sort: MatSort | undefined;
userData: any;
constructor(public authService: AuthService) {
super();
this.getUsers2();
this.printUser();//works only with hardcoded EXAMPLE_DATA, else shows empty Array
}
printUser() {
console.log("this.data", this.data);
}
getUsers2() {
this.authService
.getCollection2("users")
.subscribe((res: any[]) => {
this.data = res //here this.data gets the values from res
console.log("table-datasource", this.data)//works here only
})
}
...
}
//table.component.ts
import { AfterViewInit, Component, ViewChild } from '@angular/core';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatTable } from '@angular/material/table';
import { AuthService } from "../../services/auth.service";
import { TableDataSource, TableItem } from './table-datasource';
@Component({
selector: 'app-table',
templateUrl: './table.component.html',
styleUrls: ['./table.component.scss']
})
export class TableComponent implements AfterViewInit {
@ViewChild(MatPaginator) paginator!: MatPaginator;
@ViewChild(MatSort) sort!: MatSort;
@ViewChild(MatTable) table!: MatTable<TableItem>;
dataSource: TableDataSource;
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
displayedColumns = ['id', 'name'];
constructor(public authService: AuthService) {
this.dataSource = new TableDataSource(authService);
}
ngAfterViewInit(): void { 
this.dataSource.sort = this.sort;
this.dataSource.paginator = this.paginator;
this.table.dataSource = this.dataSource;
}
}
//table.component.html
<div class="mat-elevation-z8">
<table mat-table class="full-width-table" matSort aria-label="Elements">
<!-- Id Column -->
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Id</th>
<td mat-cell *matCellDef="let row ">{{row.uid}}</td>
</ng-container>
<!-- Name Column -->
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Name</th>
<td mat-cell *matCellDef="let row">{{row.email}}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
<mat-paginator #paginator
[length]="dataSource?.data?.length"
[pageIndex]="0"
[pageSize]="10"
[pageSizeOptions]="[5, 10, 20]"
aria-label="Select page">
</mat-paginator>
</div>

您忘记了api调用需要时间来完成。如果您还没有得到您的数据,您就不能打印用户!

例如:

getUsers2() {
this.authService
.getCollection2("users")
.subscribe((res: any[]) => {
this.data = res //here this.data gets the values from res
this.printUser();
})
}

Javascript不等待订阅完成,它同时执行其他代码。它是单线程的,但是它将一个动作推到队列的后面,说:检查我们是否已经收到响应-如果没有继续执行其他代码。这就是异步调用非阻塞的方式。

所以你会订阅,执行你压入堆栈的所有其余代码(即。您的构造函数),然后它将开始检查操作队列,其中包括检查API是否已响应。只有当api响应后,subscribe的回调函数才会执行。否则,它将再次将此check推到队列的后面。

了解更多Javascript异步代码的工作原理:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop


在订阅回调之后做某事的最好方法是简单地将您想做的任何事情放入函数中,并在订阅结束时调用它。

getUsers2() {
this.authService
.getCollection2("users")
.subscribe((res: any[]) => {
this.data = res //here this.data gets the values from res
this.doWhateverWithMyData();
})
}
然而,如果你坚持让阻塞代码执行来等待响应,你可以使用async/await。RXJS有firstValueFrom()接收Promise,在收到一个响应后解析。
constructor(public authService: AuthService) {
super();
}
async ngOnInit() {
const res: any[] = await firstValueFrom(this.authService.getCollection2('users'))
console.log(res);
}

但是要注意firstValueFrom()的警告——这是为什么使用可观察对象比使用承诺更好的一个例子。

如果可观察流在发出任何值之前完成,返回的承诺将以EmptyError拒绝,或者如果指定了默认值,将使用默认值解决。

如果可观察流发出了一个错误,返回的promise将会被拒绝,并伴有该错误。

警告:只对你知道会发出至少一个值的可观察对象使用此方法,否则将完成。如果源可观察对象没有发出一个值或没有完成,你最终会得到一个挂起的承诺,并且可能会在内存中挂起异步函数的所有状态。为了避免这种情况,可以考虑添加一些类似timeout、take、takeWhile或takeUntil之类的东西。

来源:https://rxjs.dev/api/index/function/firstValueFrom


如果你想添加5秒超时和一些错误处理,它看起来像这样:

async ngOnInit() {
const res = await firstValueFrom(
this.authService.getCollection2('users').pipe(timeout({ each: 5000 }))
).catch((err) => console.error(err));
console.log(res);
}

最新更新