当服务器用新数据响应时,管道永远不会被执行



我在模板中创建了一个订阅来监视对象的更改。对象的初始加载显示属性tags的正确数据,当我将一个项目添加到数据中时,它会转到web服务器,并返回附加到该项目的所有tags的列表(以保持该项目与服务器同步(。但是,新添加的项目不会反映在页面上。我不能百分之百确定原因。我想这是因为我的of()声明,但我不确定。我看到的是zip().pipe()永远不会被执行。

我需要使用of以外的东西吗?

注意:我正试图遵循声明性模式来消除.subscribe()的使用

子注:一旦我开始工作,我计划尝试删除该行this.server.file().subscribe上的subscribe

Stacklitz

export interface FileInfo {
tags: string[];
}
@Component({
selector: 'my-app',
template: `
<input #tag /><button (click)="addTag(tag.value)">Add Tag</button>
<div *ngIf="data$ | async as data">
<div *ngFor="let tag of data.tags">{{ tag }}</div>
</div>
`,
})
export class AppComponent {
data$ = new Observable<FileInfo>();
constructor(
// Used to mimic server responses
private readonly server: WebServer
) {}
ngOnInit() {
// I plan on removing this subscribe once I get a grasp on this
this.server.file().subscribe((img) => {
this.data$ = of(img);
});
}
addTag(newTag: string) {
const data$ = this.server.save(newTag);
this.data$.pipe(concatMap((i) => this.zip(data$)));
}
private zip(tags$: Observable<string[]>) {
return zip(this.data$, tags$).pipe(
tap((i) => console.log('zipping', i)),
map(([img, tags]) => ({ ...img, tags } as FileInfo))
);
}
}

听起来您想要做的是拥有一个可观察的源,该源在添加新标记时会发出对象的最新状态。然后,您可以使用异步管道在模板中简单地订阅这个可观察的对象。

为了实现这一点,您可以创建一个专用流来表示文件标记的更新状态。

这里有一个例子:

private initialFileState$ = this.service.getFile();
private addTag$ = new Subject<string>();
private updatedfileTags$ = this.addTag$.pipe(
concatMap(itemName => this.service.addTag(itemName))
);
public file$ = this.initialFileState$.pipe(
switchMap(file => this.updatedfileTags$.pipe(
startWith(file.tags),
map(tags => ({ ...file, tags }))
))
);
constructor(private service: FileService) { }
addTag(tagName: string) {
this.addTag$.next(itemName);
}

这是StackBlitz演示。

您错误地使用了可观察对象。在使用它订阅之后,在使用异步管道的模板中,不应该更新它的引用。

如果需要更新数据,则必须使用Subject。

export class AppComponent {
private readonly data = new BehaviorSubject<FileInfo>(null);
data$ = this.data.asObservable();
constructor(
// Used to mimic server responses
private readonly server: WebServer
) {}
ngOnInit() {
this.server.file().subscribe((result) => this.data.next(result));
}
addTag(newTag: string) {
this.server
.save(newTag)
.subscribe((tags) => this.data.next({ ...this.data.value, tags }));
}
}

此外,您的服务可以简单得多:

@Injectable({ providedIn: 'root' })
export class WebServer {
private readonly tags = ['dog', 'cat'];
file(): Observable<FileInfo> {
return of({ tags: this.tags });
}
save(tag: string) {
this.tags.push(tag);
return of(this.tags);
}
}

这是工作代码:

https://stackblitz.com/edit/angular-ivy-my3wlu?file=src/app/app.component.ts

尝试完全转换webserver.service.ts,以提供标记和FileInfo的可观测性,如下所示:

import { Injectable } from '@angular/core';
import { concat, Observable, of, Subject } from 'rxjs';
import { delay, map, shareReplay, tap } from 'rxjs/operators';
import { FileInfo } from './app.component'; // best practice is to move this to its own file, btw
@Injectable({ providedIn: 'root' })
export class WebServer {
private fakeServerTagArray = ['dog', 'cat'];
private readonly initialTags$ = of(this.fakeServerTagArray);
private readonly tagToSave$: Subject<string> = new Subject();
public readonly tags$: Observable<string[]> = concat(
this.initialTags$,
this.tagToSave$.pipe(
tap(this.fakeServerTagArray.push),
delay(100),
map(() => this.fakeServerTagArray),
shareReplay(1) // performant if more than one thing might listen, useless if only one thing listens
)
);
public readonly file$: Observable<FileInfo> = this.tags$.pipe(
map(tags => ({tags})),
shareReplay(1) // performant if more than one thing might listen, useless if only one thing listens
);
save(tag: string): void {
this.tagToSave$.next(tag);
}
}

现在你的AppComponent可以是

@Component({
selector: 'my-app',
template: `
<input #tag /><button (click)="addTag(tag.value)">Add Tag</button>
<div *ngIf="server.file$ | async as data">
<div *ngFor="let tag of data.tags">{{ tag }}</div>
</div>
`,
})
export class AppComponent {
constructor(
private readonly server: WebServer;
) {}
addTag(newTag: string) {
this.server.save(newTag);
}
}

注意:如果您在WebServer.tags$或下游未订阅的情况下调用WebServer.save,则不会发生任何事情。在您的情况下,没有什么大不了的,因为您的模板中的| async订阅了。但是,如果您将其拆分,以便将标签保存在不同的组件中,则需要稍微修改该服务,以确保";保存新标签";服务器API调用仍在进行。

最新更新