0

I'm new to using observables and am starting to learn about declarative programming. I have a situation where I have an observable that gets data from a http request. I know can sort this data using the map operator.

What I don't understand is, if I want to sort this data based on a button click (so after the http request has completed) how can I do this without the observable calling the http request again. I know I can get the data out in to an array and sort that, but is it possible to do this just using observables / subject / behaviour subject etc.

I sure it is something simple, but as I'm just starting out with observables I'm finding this to be somewhat confusing.

Hoping someone can point me in the right direction.

Warm Regards

2 Answers 2

1

I guess you should save response of your http service as component class property and use it as you need then.

It should be similar to:

// .ts
myData: MyDataInterface[];

ngOnInit() {
  this.httpService.getMyData()
    .subscribe(response => this.myData = response);  
}

onButtonClick() {
  this.sortMyData();
}

sortMyData() {
  this.myData = /** do sorting*/;
}

// .html
<button (click)="onButtonClick()">Sort my data</button>
Sign up to request clarification or add additional context in comments.

1 Comment

Hi, yes I will probably end up doing it this way, however I'm trying to understand observables. I would have thought I could something using them. Apologies I could not give an example. (could not add code the this reply)
1

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.

2 Comments

This is the sort of example(s) I was looking for. I understood that I can get the data out of the observable and place it in to an array and sort it as you would normally. Angular however seems to be based around using observables for everything. I knew it should be able to sort items with observables but just had no idea how to go about it. I guess that just because you can sort items with observables does not mean you should try to do everything with them. Many thanks for the examples.
"Angular seems to be based around using observables for everything". Angular uses observables as a better abstraction and extension of promises. Web development is often built around asynchronous arriving values that should be synchronized at some point. Rxjs is a utility that helps you with that. Angular does not force you to use observables. Angular just uses observables as a way to communicate changes. You should always decide when to rely on subscriptions or observables+pipes. Because it also can lead to unmaintainable spaghetthi code. Its good to know whats possible

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.