当服务器响应新数据时,管道永远不会执行
Pipe never gets executed when server response with new data
我在我的模板中创建了一个订阅来监视对象的变化。对象的初始加载显示 属性 tags
的正确数据,当我将项目添加到数据时,它会转到 Web 服务器和 returns 所有 tags
附加到项目(以保持项目与服务器同步)。但是,新添加的项目不会反映在页面上。我不是 100% 确定为什么。我认为这是因为我的 of()
声明,但我不确定。我看到的是 zip().pipe()
永远不会被执行。
我需要使用 of
以外的东西吗?
注意:我正在尝试遵循声明模式来消除 .subscribe()
的使用
子注:一旦我开始工作,我计划尝试删除这一行的 subscribe
this.server.file().subscribe
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))
);
}
}
尝试完全转换 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 调用。
你误用了 observable。在你订阅它之后,在带有异步管道的模板中,你不应该更新它的引用。
如果需要更新数据,必须使用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
听起来你想要做的是拥有一个单一的可观察源,在添加新标签时发出对象的最新状态。然后,您可以使用异步管道简单地订阅模板中的这个单个可观察对象。
为了实现这一点,您可以创建一个专用流来表示文件标签的更新状态。
这是一个例子:
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 演示。
我在我的模板中创建了一个订阅来监视对象的变化。对象的初始加载显示 属性 tags
的正确数据,当我将项目添加到数据时,它会转到 Web 服务器和 returns 所有 tags
附加到项目(以保持项目与服务器同步)。但是,新添加的项目不会反映在页面上。我不是 100% 确定为什么。我认为这是因为我的 of()
声明,但我不确定。我看到的是 zip().pipe()
永远不会被执行。
我需要使用 of
以外的东西吗?
注意:我正在尝试遵循声明模式来消除 .subscribe()
子注:一旦我开始工作,我计划尝试删除这一行的 subscribe
this.server.file().subscribe
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))
);
}
}
尝试完全转换 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 调用。
你误用了 observable。在你订阅它之后,在带有异步管道的模板中,你不应该更新它的引用。
如果需要更新数据,必须使用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
听起来你想要做的是拥有一个单一的可观察源,在添加新标签时发出对象的最新状态。然后,您可以使用异步管道简单地订阅模板中的这个单个可观察对象。
为了实现这一点,您可以创建一个专用流来表示文件标签的更新状态。
这是一个例子:
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 演示。