简体   繁体   中英

Angular return result of Subscription as Observable

I have a component that upon button click calls a save() method from a StorageManagerService :

// component.ts
onSaveClick(): void {
  this.storageManager
    .save(<objectToSave>)
    .pipe(take(1))
    .subscribe({
      next: (success: boolean) => {
        if (success) {
          // do stuff
        } else {
          // do other stuff
        }
      },
    });
}

That storageManager is an adapter to another service that handles conditional logic. So StorageManagerService.save() looks like the following:

// StorageManagerService
save(<objectToSave>): Observable<boolean> {
  if (<someCondition>) {
    this.modalService
      .openDialogWithComponent(
        SaveSystemContainerComponent,
        { data: { <someData> } },
        this.matDialog
      )
      .afterClosed()
      .pipe(take(1))
      .subscribe({
        next: (success: boolean) => {
          if (success) {
            // special handling

            return this.localStorageService.save(<objectToSave>);
          }

          return of(false);
        },
      });
  }
  else {
    return this.localStorageService.save(<objectToSave>);
  }
}

While LocalStorageManagerService has the concrete implementation for storing data:

// LocalStorageManagerService
save(<objectToSave>): Observable<boolean> {
  if (this.storageAvailable()) {
    localStorage.setItem(<objectToSave>.id, JSON.stringify(<objectToSave>));
    return of(true);
  }

  console.error('local storage not available or disabled');
  return of(false);
}

LocalStorageService inherits from an abstract class that all storage services have to implement. The idea is that when migrating from localStorage to an actual database in the future, I only have to change the variable in StorageManagerService and don't have to modify 20+ places in the application.

The problem I'm having is that in certain conditions the user has to input additional data before I can store it. That input happens through a modal appearing. Now TypeScript tells me that the if-case in StorageManagerService doesn't return anything (which is correct).

How can I return the result from subscribe.next() in StorageManagerService to the calling function? I think I'm missing an rxjs operator.

If someone subscribes to save, you can refactor the method to:

save(objectToSave): Observable<boolean> {
  if (someCondition) {
    return this.modalService
      .openDialogWithComponent(
        SaveSystemContainerComponent,
        { data: { someData } },
        this.matDialog
      )
      .afterClosed()
      .pipe(
        take(1),
        switchMap((success: boolean) => {
          if (success) {
            // special handling

            return this.localStorageService.save(objectToSave);
          }
          return of(false);
        })
      );
  } else {
    return this.localStorageService.save(objectToSave);
  }
}

Also, your LocalStorageManagerService should only do the actual save when a subscription is made to the return of save , not only when save is called:

save(objectToSave): Observable<boolean> {
  return defer(() => {
    if (this.storageAvailable()) {
      localStorage.setItem(objectToSave.id, JSON.stringify(objectToSave));
      return of(true);
    }

    console.error('local storage not available or disabled');
    return of(false);
  });
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM