ngxtension / ngxtension-platform

Utilities for Angular
https://ngxtension.netlify.app/
MIT License
531 stars 77 forks source link

feat: function to wait for available observable from a signal, to subscribe to this observable #422

Closed manudss closed 3 weeks ago

manudss commented 3 weeks ago

Hello,

I would like to have a function that merges computed and toSignal. It would be a function, which would be a computed, in order to wait for an observable from a signal, then subscribe to it, and return the value in a signal. If the observable changes, we need to be able to unsubscribe from the previous one, and re-subscribe to the new observable.

This is what I need when I receive an object with observables in a component, via a Signal input. And then, I want to subscribe to the observables of this object, and transform them into a signal. But the object comes later in the input. So, I'm obliged to make the toSignal, in the ngOninit for example, when normally, there would be, the signal which is well initialized.

I'd like a function that can wait for the observable, subscribe to it and return all the values in the same observable.

Example of a component:

import { Component, Input, OnInit } from '@angular/core';
import { Signal, signal } from '@angular/core';
import { Observable } from 'rxjs';

interface dataList {
  name: string; 
}

interface DataSource { 
     selectList(): Observable<dataList[]>;
}

@Component({
  selector: 'app-my-component',
  template: `
    @for(let item in lists.get(); trackBy $index; let index = index)
    {
      <ul>
        <li>{{ index + 1 }}: {{ item.name }}</li>
      </ul>
    }
  `
})
export class MyComponent implements OnInit {
  @Input() dataSource: DataSource;
  public lists: Signal<dataList[]> = signal<dataList[]>([]);

  constructor() {}

  ngOnInit() {
    this.dataSource.selectList().subscribe(data => {
      this.lists.set(data);
    });
  }
}

but if you use the signal inputs with the new input function. We can't do the toSignals at the same time, because the datasource signal hasn't yet been initialized with the right values :


....

@Component({
  selector: 'app-my-component',
  template: `
    @for(let item in lists.get(); trackBy $index; let index = index)
    {
      <ul>
        <li>{{ index + 1 }}: {{ item.name }}</li>
      </ul>
    }
  `
})
export class MyComponent implements OnInit {
  dataSource: DataSource = input<DataSource>(null);
  public lists: Signal<dataList[]> = toSignal(this.dataSource.selectList());
}

So having a function to do just that :


....

@Component({
  selector: 'app-my-component',
  template: `
    @for(let item in lists.get(); trackBy $index; let index = index)
    {
      <ul>
        <li>{{ index + 1 }}: {{ item.name }}</li>
      </ul>
    }
  `
})
export class MyComponent implements OnInit {
  dataSource: DataSource = input<DataSource>(null);
  public lists: Signal<dataList[]> = waitForToSignal(() => this.dataSource()?.selectList(), [] );
}

It would be, therefore a function that merges a compute, or effect to be alerted to all changes subscribe, to it, and update the Signal, a la the ToSignal function.

I've started to think about it, a bit, and start coding. But I wanted to have your opinion on this feature, and especially what name to give it?

eneajaho commented 3 weeks ago

Have you looked at derivedAsync?

@Component({
  selector: 'app-my-component',
  template: `
    @for(let item in lists.get(); trackBy $index; let index = index)
    {
      <ul>
        <li>{{ index + 1 }}: {{ item.name }}</li>
      </ul>
    }
  `
})
export class MyComponent implements OnInit {
  dataSource: DataSource = input<DataSource>(null);
  lists = derivedAsync(() => {
    if (this.dataSource()) {
      return this.dataSource()?.selectList()
    }
    return [];
  }, {initialValue: []});
}

https://ngxtension.netlify.app/utilities/signals/derived-async/ https://justangular.com/blog/building-computed-async-for-signals-in-angular

manudss commented 3 weeks ago

Thanks, this could work like this. Thanks.