A T
A T

Reputation: 13826

Force subscribers to re-request data from Service?

I have a simple structure, with multiple components and a single service [StackBlitz]:

<code>digraph G {   s[label="Service"];   c0[label="Component0"];   c1[label="Component1"];   c2[label="Component2"];      c0->c1;   c0->c2;   c1->s;   c2->s; }</code>

Service

@Injectable()
export class Service {
  constructor(private http: HttpClient) {}

  post(entity: IEntity): Observable<IEntity> {
    return this.http
               .post<IEntity>('/api/entity', entity)
               .pipe(map(parseDates));
  }

  get(): Observable<IEntity[]> {
    return this.http
               .get<IEntity[]>('/api/entity')
               .pipe(map(parseDates));
  }
}

Component0

@Component({
  selector: 'app-component0',
  template: `<app-component1></app-component1>
             <app-component2></app-component2>`,
  styleUrls: []
})
export class Component0 {}

Component1

@Component({
  selector: 'app-component1-create',
  template: `<button (click)="submit()">Submit</button>`,
  styleUrls: []
})
export class Component1 {    
    constructor(private service: Service) { }

    submit() {
        this.service.post({foo: 'bar'})
                    .subscribe(console.info, console.error)
    }
}

Component2

@Component({
  selector: 'app-component2-table',
  template: `<pre>{{ entities | json }}</pre>`,
  styleUrls: []
})
export class Component2 implements OnInit {
    entities: IEntity[];

    constructor(private service: Service) { }

    ngOnInit() {
        this.service.get()
                    .subscribe(entities => this.entities = entities,
                               console.error)
    }
}

How do I get Component1 to update Component2, through the Service?

Upvotes: 0

Views: 78

Answers (1)

Yanis-git
Yanis-git

Reputation: 7875

you should have another observable to mitigate your component communication :

@Injectable()
export class StoreService {

  private _employes$ = new BehaviorSubject<{id:number,employee_name:string,employee_salary:number,employee_age:number}[]>([]);

  constructor(private _http: HttpClient) {
    this.refresh();
  }

  refresh() {
    this._http.get<{id:number,employee_name:string,employee_salary:number,employee_age:number}[]>('https://dummy.restapiexample.com/api/v1/employees').subscribe(
      data => this._employes$.next(data)
    );
  }

  post() {
      const payload = {"name":"test","salary":"123","age":"23"};

      this._http.post('https://dummy.restapiexample.com/api/v1/create', payload).subscribe(() => {
        this.refresh();
      })
  }

  get employes$(): Observable<{id:number,employee_name:string,employee_salary:number,employee_age:number}[]> {
    return this._employes$.asObservable();
  }
}

Which are compose by two part :

Observable use by your store, when you want to update your stream, you can next to it.

private _employes$ = new BehaviorSubject<{...}[]>([]);

Exposed getter. Any component which need to be inform by any change on your store can subscribe to it.

  get employes$(): Observable<{...}[]> {
    return this._employes$.asObservable();
  }

From now all is relative to your actual case, you have various way to update this internal observable :

the quickest way :

refresh() {
    this._http.get<{...}[]>('...').subscribe(
      data => this._employes$.next(data)
    );
  }

Inside your service, you subscribe to your http call and update your internal observable.

Pro :

  • you don't expose the request.
  • Very easy to maintain as only one place can make API call.

Con :

  • Is not adapted to some case where you want to actually control, update, ... do something specific with the API answer.
  • Is not always good idea to subscribe on service.

Observable gymnastic

If you don't want to subscribe on your service. You can do something like :

return this._http.get<{...}[]>('...')
    .pipe(switchMap(result => {
        this._employes$.next(result);
        return this.employes$;
    }));

you transform your http get observable to the internal observable. At the same time you next the new result of your API answer.

If tomorrow you need to delegate a special traitment on the API response before make it available accross your application.

you can do :

refresh(myCustomTraitment: = null) {
    return this._http.get<{...}[]>('...')
        .pipe(switchMap(result => {
            if (myCustomTraitment) {
                result = result.map(myCustomTraitment);
            }
            this._employes$.next(result);
            return this._employes$;
        }));
}

Online sample

Upvotes: 1

Related Questions