角度信号输入效果从服务器加载数据

问题描述 投票:0回答:1

我正在迁移角度应用程序以使用信号和输入信号,但发现了一个我不满意的情况。

在之前的

@Input()
中,我可以定义一个setter来在特定输入更改时触发“效果”,并且我可以为对服务器的请求设置一个可观察对象,例如

  @Input()
  set id(value: number) {
    this.loading = true;
    this.entity$ = this.service.findById(value).pipe(finalize(() => { 
      this.loading = false;
    }));
  }

我喜欢避免使用

ngOnChanges
,因为我可以避免验证哪个输入已更改。

我能找到的最接近信号是使用

effect
来触发查询,但文档并不真正推荐它

大多数应用程序代码中很少需要效果,但在特定情况下可能很有用

避免使用效果来传播状态变化。这可能会导致 ExpressionChangedAfterItHasBeenChecked 错误、无限循环更新或不必要的更改检测周期。

由于这些风险,默认情况下不允许设置信号,但如果绝对必要,可以启用。

因为我必须将

loading
信号设置为 true,所以我会破坏最后一部分

  effect(() => {
    this.loading.set(true);
    this.entity$ = this.service.findById(value).pipe(finalize(() => {
      this.loading.set(false);
    }));
  })

我还找到了一种在输入信号上使用

toObservable()
的方法,但它在幕后使用
effetc()
所以我想我也受到同样的限制。

有推荐的方法吗?对于这种情况,我是否依赖

ngOnChanges

angular signals angular-signals
1个回答
0
投票

解决这个问题的一种方法是使用

toObservable
将信号转换为可观察量,然后我们可以应用 switchMap 并进行 API 调用,因为可观察量没有限制,我们可以根据需要设置加载信号!

import { AsyncPipe, CommonModule } from '@angular/common';
import {
  Component,
  inject,
  Injectable,
  input,
  InputSignal,
  signal,
} from '@angular/core';
import { toObservable } from '@angular/core/rxjs-interop';
import { bootstrapApplication } from '@angular/platform-browser';
import { of, delay, Observable } from 'rxjs';
import { finalize, switchMap } from 'rxjs/operators';
import 'zone.js';
@Injectable({ providedIn: 'root' })
export class TestService {
  findById(id: string): Observable<any> {
    return of({ id: Math.random.toString(), name: 'test' }).pipe(delay(2000));
  }
}

@Component({
  selector: 'app-child',
  standalone: true,
  imports: [CommonModule],
  template: `
  {{loading()}}
    @if(!loading() && (observable$ | async); as data) {
      API DONE -> for ID {{id()}}: {{data | json}}
    } @else {
      Loading...
    }
  `,
})
export class Child {
  service = inject(TestService);
  id: InputSignal<string | null> = input<string | null>(null);
  loading = signal(false);
  observable$ = toObservable(this.id).pipe(
    switchMap((id: string | null): any => {
      this.loading.set(true);
      return id
        ? this.service.findById(id).pipe(
            finalize(() => {
              this.loading.set(false);
            })
          )
        : of(false);
    })
  );

  ngOnInit() {}
}

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [Child],
  template: `
    <app-child [id]="id"/>
  `,
})
export class App {
  id: string = Math.random().toString();

  constructor() {
    setInterval(() => {
      this.id = Math.random().toString();
    }, 5000);
  }
}

bootstrapApplication(App);

Stackblitz 演示

© www.soinside.com 2019 - 2024. All rights reserved.