I have a service (MessageService) that emits some messages based on some other app events.
Then, I have a component MessagesComponent and I'd like to render all the messages that were emitted by my Service. My approach is to use an Observable<Message[]> to store and reactively update the UI every time a new Message arrives. Here's my working solution

@Component({
  selector: 'app-messages',
  template: `
    <h2>Received messages</h2>
    <ol>
      <li class="message" *ngFor="let message of messages$ | async">
        {{ message }}
      </li>
    </ol>
  `,
  styles: [``]
})
export class MessagesComponent implements OnInit, OnDestroy {
  destroy$ = new Subject<void>();
  messages$ = new BehaviorSubject<Message[]>([]);
  
  constructor(private messagesService: MessagesService) {}

  ngOnInit(): void {
    this.messagesService.message$
      .pipe(
        takeUntil(this.destroy$),
        switchMap((message: Message) =>
          this.messages$.pipe(
            take(1),
            map((all: Message[]) => {
              return [...all, message];
            })
          )
        )
      )
      .subscribe((messages: Message[]) => {
        this.messages$.next(messages);
      });
  }

  ngOnDestroy(): void {
    this.destroy$.next();
    this.destroy$.complete();
  }
}

现在,我从一个空的BehaviorSubject数组开始存储所有消息.然后,在ngOnInit钩子中,我订阅了MessagesService's message$,在新的发布之后,我将把BehaviorSubject中的内容与新消息结合起来,从而发布一个包含所有项目的新array.

messages$: Observable<Message[]> = this.messagesService.message$.pipe(
        switchMap((message: Message) =>
          this.messages$.pipe(
            startWith([]),
            take(1),
            map((all: Message[]) => [...all, message])
          )
        )
      );

我试图使用数组本身作为他们自己使用SwitchMap+StartWith进行初始化的一部分,但它似乎不起作用,所以我在寻找一些其他 idea :)

哦,消息类型只是一个带有一些属性的简单接口.

export interface Message {
  id: string;
  value: string;
}

推荐答案

可以使用scan运算符来累加状态.它将获得以前的排放量,你可以在此基础上继续发展.

export class AppComponent {
  
  messages$ = this.messagesService.message$.pipe(
    scan((all, message) => all.concat(message), [])
  );

  constructor(private messagesService: MessagesService) { }

}

scan接收到来自mesageService.message$的emits 时,它将运行该功能

(all, message) => all.concat(message)

并发出结果.message是从服务接收到的值.第一次,all的值是我们的初始种子值([]).之后,它是之前发出的值;

注意,组件代码大大简化了;你不需要:Subject、BehaviorSubject、subscription、ngOnInit、ngondstroy、takeUntil、switchMap或take:-)

这里有一个小演示.

Typescript相关问答推荐

为什么typescript要把这个空合并转换成三元?

类型脚本中没有接口的中间静态类

为什么我的条件类型不能像我预期的那样工作?

等待用户在Angular 函数中输入

类型脚本映射类型:从对象和字符串列表到键值对象

如何在Type脚本中动态扩展函数的参数类型?

TS如何不立即将表达式转换为完全不可变?

ANGLING v17 CLI默认设置为独立:延迟加载是否仍需要@NgModule进行路由?

下载量怎么会超过下载量?

是否有可能避免此泛型函数体中的类型断言?

try 使Angular依赖注入工作

棱角分明-什么是...接口类型<;T>;扩展函数{new(...args:any[]):t;}...卑鄙?

当传递带有或不带有参数的函数作为组件props 时,我应该如何指定类型?

在TS泛型和记录类型映射方面有问题

组件使用forwardRef类型脚本时传递props

换行TypeScript';s具有泛型类型的推断数据类型

TypeScript:方法获胜';t接受较窄类型作为参数

为什么TS条件返回要求不明确的强制转换而不是精确的强制转换?

对于通过 Axios 获取的数据数组,属性map在类型never上不存在

如何在 TypeScript 类型定义中简化定义长联合类型