but is it possible to do this just using observables / subject / behaviour subject etc.
Yes.
Since you did not provide an example, I will run with a few assumptions.
Lets a assume you run a HTTP-GET on the Init lifecylce of angular.
Then you subscribe to the incoming response, to do something with it.
Then depending on a "button is clicked state" you want to trigger the subscription without triggering a request, so applying the sort to the "cached" response.
One approach would be "mergeMap" : https://rxjs.dev/api/operators/mergeMap
@Component({
selector: 'app-root',
standalone: true,
template: `
<h1>Hello from {{ name }}!</h1>
<div>
<button (click)="sort($event)"> sort</button>
</div>
`,
})
export class App implements OnInit {
public $sortEvent = new BehaviorSubject<boolean>(false); // holds the current sort state event,
name = 'Angular';
ngOnInit(): void {
this.fetchSomeData() // this will only be called once
.pipe(
// here you merge the data of the fetch
// and declare that you want to use the data in a new observable
// this new observable is the sortEvent
// lis: is now a state within this construct, the observable before will not be triggered again
mergeMap((lis) =>
this.$sortEvent.pipe(map((isSorted) => (isSorted ? [...lis].sort() : lis)))
)
)
.subscribe((e) => {
// this will be triggered
// only on the initial fetch
// and then only when $sortEvent emits a value
console.log(e);
});
}
sort(event: Event) {
this.$sortEvent.next(true);
}
// this mocks a http request
fetchSomeData(): Observable<number[]> {
return of([8, 2, 4, 1, 6, 1, 0]).pipe(
tap(() => console.log('do I have been called?'))
);
}
}
It is important to note here, that proper clean up of subscriptions is required.
Opinionated advice:
Even though Observables and pipings are good tools to deal with complex asynchronous values, it is often underestimated, how hard to maintain mergeMaps and switchMaps are, and how frequent memoryleaks are created and are unnoticed with such approaches.
Careful considerations should be done, if a the value does not change anyways:
Do I really need this thing to be an observable?
Let me give an example. You request a list.
You do not show or render the component anyways if the value hasn't arrived.
But you want to display the list depending on a observable, that in fact changes often, and when it changes, something should be rerendered.
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule],
template: `
<h1>Hello from {{ name }}!</h1>
<div>
<button (click)="sort($event)"> sort</button>
<div>{{$maybeSortedList | async}}</div> // use the async pipe to listen to the changing arrangements of the list
<div>{{list }}</div>
</div>
`,
})
export class App implements OnInit {
name = 'Angular';
private $isSorted = new BehaviorSubject(false); // have a observable for a changing object
public list: number[] = []; // have no observable for a object that changes only on init
public $maybeSortedList = this.$isSorted.pipe(
map((isSorted) => (isSorted ? [...this.list].sort() : this.list)), // map it to the list
);
ngOnInit(): void {
this.fetchSomeData().subscribe((response) => {
this.list = response;
});
}
sort(event: Event) {
this.$isSorted.next(true);
}
fetchSomeData(): Observable<number[]> {
return of([8, 2, 4, 1, 6, 1, 0]).pipe(
tap(() => console.log('do I have been called?'))
);
}
}
Use Observables. But do not merge, flatmap, switchmap more than required.