1

This might be a common problem but somehow i keep hitting wall with it

HTML

@if(data$ | async; as data) {
<span>{{data.status}}</span>
}

TS

dataId = input.required<string>();
data$ = EMPTY;
ngOnInit() {
if(this.dataId()) {
data$ = this.getData(this.dataId()).pipe(tap(console.log));
}
}

We make a change in another component, we can see in the console that getData() emits original value followed by the updated value

ready
"Data change submited" // this occurs with restful call has been made
not-ready

In HTML it still renders ready and doesn't update it.

Now my two options are:

  1. trigger change detection manually through CDR
  2. convert to signal and let the signal handle it

Im not a fan of either options:

  1. Excessive boilerplate code just to trigger change detection
  2. Takes away the simplicity of AsynPipe managing the subscription and forces you to conver into signal

Are there any other options?

5
  • Are you sure, your code correct? Because to alias the value in template you need to write as data . Commented Jun 17 at 5:10
  • Btw. At the moment nothing happens if you change the input after ngOnInit has been called, that is the reason why you should think about using resource: angular.dev/guide/signals/resource Commented Jun 17 at 5:15
  • @ThomasRenger i updated typo, input is not being changed, and i can see events being emited by the observable because of the tap, its just not reflected in HTML Commented Jun 17 at 5:40
  • Maybe you're using zoneless and changing the reference of the field is not registering? Have you tried doing it like so: readonly id = input<string>(); readonly data$ = toObservable(this.id).pipe(switchMap((id) => id ? this.http.get<Data>(``/api/data/${id}``) : of(null))); Commented Jun 17 at 7:12
  • @Silvermind issue is not with observable, as its emiting expected values, but more that HTML is not being updated. Its still Default change detections and not using zoneless. Basically not using any experimental features. Commented Jun 17 at 9:11

1 Answer 1

1

First remove the EMPTY it could be causing the async pipe to complete before it gets the data. Then we use effect to set the API call based on the input received.

dataId = input.required<string>();
data$ = null;
constructor() {
  effect(() => {
    this.data$ = this.getData(this.dataId()).pipe(tap(console.log));
  });
}

Update the @if condition to validate that a value is present then get the data from the Observable.

@if((data$ && (data$ | async)); as data) {
  <span>{{data.status}}</span>
  <!-- OR -->
  <!-- <span>{{data}}</span> -->
}

Use rxResource to asynchronously get the data using the input observable, or we can use httpResource to fetch the data directly from the URL without any hassle, all that is needed is the API URL.

Both Resource API follow the same principle, when the signals inside the rxResource request or callback of httpResource are changed, the API is retriggered.

dataId = input.required<string>();

// use for observable API
dataResource = rxResource({
  request: () => this.dataId(),
  loader: ({ request: dataId }) => this.getData(dataId),
});
// directly use the URL, simpler!
dataResource = httpResource(() => `<<URL here>>${this.dataId()}`);

HTML:

@if(!dataResource.isLoading()) {
  <span>{{dataResource.value()?.status}}</span>
} @else {
  Loading...
}

The httpResource might trigger the API, even when dataId is undefined, so we can check that and return undefined, so that the initial call is made only when dataId is available.

dataResource = httpResource(() => this.dataId() ? `<<URL here>>${this.dataId()}` : undefined);
Sign up to request clarification or add additional context in comments.

3 Comments

i absolutely love resource and rxResource but its still experimental, so hesistant to put it up on prod env. I cannot wait when its promoted to stable, will simplify so much of codebase
@Aeseir you should it'll become stable next release and helps me write less code
@Aeseir updated my answer

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.