当前位置: 首页 > 知识库问答 >
问题:

使用角度异步管道以完全反应的方式刷新数据

臧彭亮
2023-03-14

我觉得问这个有点尴尬,但我肯定我错过了什么。花了很长时间寻找/研究,但只想出复杂而错综复杂的解决方案,需要主题或行为主题或通过合并地图等管道。

在组件超文本标记语言模板中,我将从命令式HTTP可观察方法(手动订阅)转移到反应式HTTP可观察方法(可观察$|async)。

当页面初始化时,使用异步管道工作没有问题。

但是,什么是“动态”刷新模板的最佳方式,即我知道我已将一条记录插入基础数据库,并希望它反映在我的模板中。

使用命令式方法,我可以简单地再次订阅(例如从单击处理程序),并将结果重新分配给与ngfor指令相关联的属性,更改检测将被触发,DOM将被更新。

我想出的唯一解决方案是简单地从服务中再次获取可观察到的,这会触发更改检测。

为了让它更清楚,这里有一系列代码(只关注解决方案非常简单)。

必须有一个简单的解决方案吗?

<div *ngIf="(model$ | async)?.length > 0" class="card mb-3 shadow">

  <div class="card-header">Actions Notes</div>

  <div class="card-body">

    <mat-list *ngFor="let action of model$ | async">
      <div class="small">
        <strong>
          {{action.createdDate | date:'dd/MM/yyyy HH:mm'}}
          {{action.createdBy}}
        </strong>
      </div>
      <div>{{action.notes}}</div>
    </mat-list>

  </div>
</div>

<button (click)="onClick()">name</button>
 model$: Observable<any>;

  constructor(
    private changeTransferActionsService: ChangeTransferActionsService
  ) {}

  ngOnInit(): void {
    this.getObservable();
  }

  private getObservable(): void {
    this.model$ = this.changeTransferActionsService
      .getActionsWithNotesById(this.model.id)
      .pipe(
        map((x) => x.result),
        share()
      );
  }

  onClick(): void {

    //insert row into database here (via HTTP post service call)

    this.getObservable();
  }

下面是我最初的问题的三个工作解决方案和解释。感谢所有做出回应的人。

使用带有任意类型值和初始值的RxJSBehaviorSubject。使用展平运算符开关映射将其与高阶可观测值结合起来。

单击按钮时,Behavior Subject发出(下一个)一个值(在本例中为0),该值被馈送到HTTP observable(忽略传递值),从而导致内部observable发出一个值并启动更改检测。

当使用行为主体时,可观察对象会在订阅时发出一个值。

在组件超文本标记语言模板中。

<button (click)="onClick()">Click Me</button>

在组件类型脚本中。

import { BehaviorSubject, Observable } from 'rxjs';
.
.
.
subject = new BehaviorSubject(0);
.
.
.

this.model$ = this.subject.asObservable().pipe(
      // startWith(0),
      switchMap(() => this.changeTransferActionsService
        .getActionsWithNotesById(this.model.id)
        .pipe(
          map((x) => x.result)
        )));
.
.
.
onClick(): void {
    this.subject.next(0);
  }

使用RxJS主题将此与使用平坦运算符SwitchMap可观察到的高阶相结合。

单击按钮时,主题发出(下一个)一个值(本例中未定义),该值被馈送到HTTP observable(忽略传递值),从而导致内部observable发出一个值并启动更改检测。

主体需要一个startWith(0),否则当组件首次初始化并通过异步管道首次订阅时,可观察对象将不会发出任何值。

import {Observable, Subject } from 'rxjs';
.
.
.
subject = new Subject();
.
.
.
 this.model$ = this.subject.asObservable().pipe(
      startWith(0),
      switchMap(() => this.changeTransferActionsService
        .getActionsWithNotesById(this.model.id)
        .pipe(
          map((x) => x.result)
        )));
.
.
.
onClick(): void {
    this.subject.next(0);
  }
<button (click)="onClick()">Click Me</button>

请注意,在上述两种情况下,主题的下一个方法都可以在HTML组件模板中直接调用,因此主题具有公共访问修饰符(默认值)。这样就不需要在Typescript中使用onCLick方法。

<button (click)="subject.next(0)">Click Me</button>

按照哈南的建议

向按钮添加模板变量名,并使用@ViewChilds获取元素引用。创建一个RxJS可观察使用fromEvents,然后使用SwitchMap高阶可观察的方法。

.
.
.
 @ViewChild('refreshButton') refreshButton: ElementRef;
.
.
.

 ngAfterViewInit(): void {
    const click$ = fromEvent(this.refreshButton.nativeElement, 'click');

    this.model$ = click$.pipe(
      startWith(0),
      switchMap(_ => {
        return this.changeTransferActionsService
          .getActionsWithNotesById(this.model.id)
          .pipe(
            map((x) => x.result),
          );
      }));
  }


.
.
.
<button #refreshButton>Click Me</button>
.
.
.

总之,如果用户通过点击随意启动刷新,解决方案3最有意义,因为它不需要引入“虚拟”主题。

然而,subject方法有助于判断可观察对象是否应该从任何地方以编程方式发出……只需在subject上调用下一个方法,可观察对象就可以在命令上发出值。

您知道,这个主题还有很多内容,一种真正的反应式方法可能会启动您关于使用基于主题/行为主题/异步主题等的共享可注入单例服务和订阅的思考过程。不过,这些都是架构和设计决策。谢谢你的建议。

共有1个答案

阴高刚
2023-03-14

您可以获取刷新Btn引用并从其单击事件创建流,然后使用虚拟条目启动它。现在你有一个刷新流,然后像这样把它切换到你的数据流:

组成部分

@ViewChild('refreshButton') refreshBtn:ElementRef;  
  model$: Observable<any>;

  ngAfterViewInit() {
    let click$ = fromEvent(this.refreshBtn.nativeElement, 'click')
    this.model$ = click$.pipe(startWith(0),
    switchMap(_=> {
       return this.changeTransferActionsService
      .getActionsWithNotesById(this.model.id)
      .pipe(
        map((x) => x.result),
      );
    }));
  }

样板

与使用2个异步管道不同,只使用1个异步管道和ngLet,这将简化模板,您不必使用share()

<button #refreshButton >Refresh</button>

<ng-container *ngLet="(model$ | async) as model">

<div *ngIf="model.length > 0" class="card mb-3 shadow">

  <div class="card-header">Actions Notes</div>

  <div class="card-body">

    <mat-list *ngFor="let action of model">
      <div class="small">
        <strong>
          {{action.createdDate | date:'dd/MM/yyyy HH:mm'}}
          {{action.createdBy}}
        </strong>
      </div>
      <div>{{action.notes}}</div>
    </mat-list>

  </div>
</div>

<button (click)="onClick()">name</button>

</ng-container>

但要务实,不要花太多的精力让它完全被动。您以前的解决方案也很好,您只需要对模板进行改进。

注意:在这种情况下,您可以使用*ngIF来代替*ngLet

 类似资料:
  • 我对angular是个新手,开始学习angular 4。数据未与使用异步管道的组件上的指令绑定。请帮忙 用户服务使用HTTP请求从API获取数据: user.service.ts 在这里,我使用用户列表的可观察用户[]界面: user.component.ts 订阅可观察用户变量的异步管道模板: user.component.html

  • 关于Mono用法的小问题。反应管道中出现错误(new MyCustomException()) 我有一个非常简单的业务用例:使用Spring Webflow 步骤1:对第三方rest API进行HTTP调用 根据第一方rest API的响应,如果只有第一个响应符合条件,则使用map函数将响应转换为MyPojo。但如果不符合条件,则应引发自定义异常 因此,我写了以下内容: 请问正确的方法是什么? 如

  • 我正在处理一个反应式quarkus后端服务,它执行以下操作。 使用与postgres交互的Hibernate反应式Panache从后端获取记录列表 使用记录的标识符,从另一个远程服务获取数据 我正在使用Mutiny来执行反应式管道。远程服务和数据库集成都以非阻塞方式单独工作。我只是需要帮助写一个连接这些的管道。例如:下面的例子 我被困在处理统一包装列表,然后试图处理列表中的单个项目。要么是Uni

  • 问题内容: 在过去的几个小时中,我一直在努力解决这个问题,但无法解决。我想我仍然必须习惯于函数式编程风格;) 我写了一个递归函数,它遍历目录结构并对某些文件进行处理。此功能使用异步IO方法。现在,我要在完成整个遍历后执行一些操作。 如何确保在执行完所有调用但仍使用异步IO功能后执行此操作? 问题答案: 查找“ 步骤”模块。它可以链接异步函数调用,并将结果从一个传递到另一个。

  • 问题内容: 我正在尝试使用新的react useReducer API来获取一些数据,并停留在需要异步获取的阶段。我只是不知道如何:/ 如何将数据获取放置在switch语句中,或者这不是应该完成的方式? 我试图这样做,但它不能与异步一起工作;( 问题答案: 这是一个有趣的案例,示例没有涉及。我认为减速器不是异步加载的正确位置。来自Redux的心态,您通常会将数据加载到其他位置,例如以thunk,可

  • 问题内容: 我有一个初始化的角度指令,如下所示: 我希望它足够聪明,可以在更改时刷新指令,因为这意味着它应该显示完全不同的内容。 我已经对其进行了测试,但没有任何反应,更改时甚至没有调用链接函数。有没有办法做到这一点? 问题答案: 链接函数仅被调用一次,因此不会直接执行您期望的操作。您需要使用angular 来观看模型变量。 该手表需要在链接功能中设置。 如果您将隔离范围用于指令,则范围将是 在链