How can I prevent the Angular asynchronous pipe from frequent server calls when results are not returned?

I use the tube asyncin ngFor to view the Observable. The observable is created by a service that gets to my server, and during boot, when the ngFor loop lists, the service correctly makes a call to the server.

Now for the part I don’t understand: when some results come back, everything happens as expected. But if the server responds with, say, 404, and there are no results to list, then the tag asyncforces the service to continue to run requests in milliseconds. This is obviously not good. What is the expected design here and how can I gracefully handle an Observable that returns an error when using an asynchronous channel?

In the component template:

<li *ngFor="let person of persons | async">{{person.id}}</li>

In the component case:

get persons: Observable<Person[]> {
    return this.personService.list();
}

In the service:

list(): Observable<Person[]> {
    if (this.persons && this.persons.length) {
        return Observable.from([this.persons]);
    } else {
        return this.http.get('/person')
            .map((response: Response) => response.json())
            .map((data: Person[]) => {
                this.persons = data;
                return data;
            })
            .catch((error: any) => {
                let errMsg = "some error..."
                return Observable.throw(errMsg);
            });
        }
    }
}
+7
source share
3 answers

I had a very similar problem, and this was due to how the Angular2 detection function works. There are several solutions that worked for me.

Save the link to the observable so that it does not change

Right now, when you call personsgetter, it always returns a new instance Observable(for example, another object), so Angular reevaluates all of this, and during the next change detection cycle, it does it again and then again ... So, here's how this can be solved:

@Component({
    selector: 'my-comp',
    template: `{{ _persons | async }}`,
}) export class MyComponent implements OnInit {
    ngOnInit() {
        this._persons = this.personService.list();
    }
}

Change ChangeDetectionStrategytoOnPush

You can tell Angular: "I know what I'm doing, I’ll tell you myself when some changes occurred":

import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';

@Component({
    changeDetection: ChangeDetectionStrategy.OnPush,
    selector: 'my-comp',
    template: `
      {{ persons | async }}
      <button (click)="reload">Reload</button>
    `,
}) export class MyComponent implements OnInit {
    constructor(private cd: ChangeDetectorRef) {}

    ngOnInit() {
        this.persons = this.personService.list();
    }

    reload() {
        this.persons = this.personService.list();
        // this tells Angular that a change occurred
        this.cd.markForCheck();
    }
}

, ,

+6

Angular , . , , , , .

( ) angular , ( ).

"", , .

, . - :

class myComponent {
public $persons:Observable<Person[]>;
constructor(){
this.$persons = this.$getPersons();
}

private $getPersons():Observable<Person[]>{
return this.personService.list();
}
}

<li *ngFor="let person of $persons | async">{{person.id}}</li>

- @Memoize, .

https://www.npmjs.com/package/typescript-memoize

:

@Memoize()
get persons: Observable<Person[]> {
    return this.personService.list();
}

Bonus: , , (, "" ). (publishLast publishReplay)

0

switchMap :

 this.asyncPipedObservable$ = of().pipe(switchMap(() => 
    this.apiService.getData().pipe(share())));
0

Source: https://habr.com/ru/post/1649038/


All Articles