Kishor Prakash
Kishor Prakash

Reputation: 8171

Angular 6 generalize/abstract Observable function

Working on an angular 6 application. Where I have to fetch and display list of users in every page from API. I have wrote a function which works perfectly:

  listUsers() {
    let result = this.http.get('xyz.com/api/users');
    result.toPromise()
      .then(res => {
        this.userList = res.json();
       })
      .catch(err => {
        this.msgService.add({severity: 'error', summary: 'Internal Server Error',
        detail: 'There is an Server Error occurred. Please try again later.'});
    } );
  }

My issue is, I want fetch users in more than 10 components/pages. I don't want to repeat(biolerplate) the above function listUsers() in all those 10 components. I want to fetch all the users in once central service and access it from every component.

Ho do I do that?

Sorry that I did not phrases the question properly. Also I could not find any similar posts on this. Please point me to the post if it's already answered before. I will delete this duplicate post.

Upvotes: 0

Views: 345

Answers (3)

Jignesh Patel
Jignesh Patel

Reputation: 9

You can use state level management using ngxs/store. Create one class User-state.ts which need below methods

You can use selector tag to fetch user list from store which is accessible from all components.

    @Selector()
      static userList(state: UserStateModel) {
        return state.UserList;
      }

        const initialState = {
         UserList = []
        };

        // **make data base call to fetch User list**
          @Action(UserActions.GetUserList)
          getUserList(
            { patchState, dispatch }: StateContext<UserStateModel>,
            { payload }: UserActions.GetUserList
          ) {
            // call the service to backend call.
            return this.UserListService
              .getPayments(payload)
              .pipe(
                tap((apiResp: IApiResponse) => dispatch(new UserActions.GetUserListSuccess(apiResp))),
                catchError(error => {

                  return dispatch(new UserActions.UserListFail(error));
                })
              );
          }

          // Using **patchState** you can refresh value of UserList in store 
          @Action(UserActions.GetUserListSuccess)
          GetUserListSuccess(
            { patchState }: StateContext<UserStateModel>,
            { payload }: UserActions.GetUserListSuccess

          ) {

            if (!isNullOrUndefined(payload)
              && !isNullOrUndefined(payload.result)
              && !isNullOrUndefined(payload.resultdata)) {

              return patchState({
                UserList: payload.resultdata.UserList
              });
            }

            return **patchState**({ UserList: initialState.UserList});
          }

// Create one more file UserAction.ts which includes below two classes

export class GetUserList {
  static readonly type = '[UserList] Get Payment Details';  
}
export class GetUserSuccess {
  static readonly type = '[Userlist] Get User List Success';
  constructor(public payload?: any) { }
}

Upvotes: 0

maxime1992
maxime1992

Reputation: 23813

This is related to your architecture in general.

If that's pretty much the only case that's annoying you, I'd recommend using a shareReplay so that you can share the result of the observable to all the consumers.

Example:

@Injectable()
export class SomeService {
  // ...

  public users$: Observable<User[]> = this.http.get('xyz.com/api/users').pipe(
    catchError(err => {
      this.msgService.add({
        severity: 'error',
        summary: 'Internal Server Error',
        detail: 'There is an Server Error occurred. Please try again later.',
      });

      throw err;
    }),
    shareReplay({
      bufferSize: 1,
      // usually `refCount` should be true so that when all the subscribers
      // are not listening anymore, it'll close the observable but here let
      // imagine that all your components are not listening to that and then
      // later on you come back to it, it will return the previously fetched
      // data without fetching it again. If **in that special case** you'd
      // prefer to make another request turn that to true
      refCount: false,
    })
  );

  // ...
}

This way, if you don't have a method, the same reference will be shared and consumed. Thanks to the shareReplay, you won't make another request unless everyone unsubscribe and later on someone else subscribe to it. But as long as there's 1 subscribers, it doesn't matter how many will join after, the result will be shared.

You could also consider looking into this library: https://github.com/ngneat/cashew which will simplify your life and give you more control.

Another option would be to start looking into ngrx as it'd be appropriate to fetch the data once based on where you are on the routes (fetch it once), then put it in a local "store" and all the components access that store. But this would require you to learn redux/ngrx and would not be straightforward. If your app keeps growing I'd recommend to check it as it's really useful but that decision is up to you and you can go with what you're the most comfortable.

Upvotes: 2

Sanjay Lohar
Sanjay Lohar

Reputation: 528

Ngrx Over service injection

Click here (Angular 6 - Why use @ngrx/store rather than service injection).

Hope you get this great feature which can be used for this problem.

Upvotes: -1

Related Questions