2

I have a loader service from Angular University course:


@Injectable()
export class LoadingService {
  private loadingSubject = new BehaviorSubject<boolean>(false);
  loading$: Observable<boolean> = this.loadingSubject.asObservable();

  showLoaderUntilCompleted<T>(obs$: Observable<T>): Observable<T> {
    return of(null).pipe(
      tap(() => this.on()),
      concatMap(() => obs$),
      finalize(() => this.off())
    );
  }

  on() {
    this.loadingSubject.next(true);
  }

  off() {
    this.loadingSubject.next(false);
  }
}

And it's usage in a component:

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"],
  providers: [LoadingService]
})
export class AppComponent {
  loading$: Observable<boolean>;
  constructor(private loader: LoadingService) {
    this.loading$ = loader.loading$;
  }
  submit() {
    this.loader.showLoaderUntilCompleted(timer(1000)).subscribe();
  }

  submit2() {
    this.loader.showLoaderUntilCompleted(timer(1000)).subscribe();
  }
}

And the template:

<form (ngSubmit)="submit()">
  {{ loading$ | async }}
  <button type="submit">Submit</button>
</form>

<form (ngSubmit)="submit2()">
  {{ loading$ | async }}
  <button type="submit">Submit</button>
</form>

It works perfectly in a case when component has only one form, can someone suggest how can I reuse it with multiple forms on the page? The goal is to have different loader states for each form.

Link to stackblitz

2 Answers 2

2

You answered the question yourself. "The goal is to have different loader states for each form" - so each form needs it's own loader.

You could create two different instances of the service for two separate unrelated loaders.

Controller

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"],
  providers: [LoadingService]
})
export class AppComponent {
  private loader1: LoadingService;
  private loader2: LoadingService;

  loading1$: Observable<boolean>;
  loading2$: Observable<boolean>;

  constructor() {
    this.loader1 = new LoadingService();
    this.loader2 = new LoadingService();

    this.loading1$ = this.loader1.loading$;
    this.loading2$ = this.loader2.loading$;
  }

  submit() {
    this.loader1.showLoaderUntilCompleted(timer(1000)).subscribe();
  }

  submit2() {
    this.loader2.showLoaderUntilCompleted(timer(1000)).subscribe();
  }
}

Template

<form (ngSubmit)="submit()">
    {{ loading1$ | async }}
    <button type="submit">Submit</button>
</form>

<form (ngSubmit)="submit2()">
    {{ loading2$ | async }}
    <button type="submit">Submit</button>
</form>

I've modified your Stackblitz.

Sign up to request clarification or add additional context in comments.

2 Comments

is it legal to inject a service like this? Will I have any other limitations using new LoadingService(); ?
Injecting the service takes advantage of Typescript's Parameter Properties. It approximately gets expanded to the same form as shown here. You could also see here in this Playground. If you're still doubtful you could also do constructor(private loader1: LoadingService, private loader2: LoadingService) and remove the manual definitions and assignment.
1

This aslo can be done with injection token injection token doc

const FORM1_LOADER_TOKEN = new InjectionToken('form1-loader-token');
const FORM2_LOADER_TOKEN = new InjectionToken('form2-loader-token');

@Component({
    selector: 'my-app',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css'],
    providers: [
        {
            provide: FORM1_LOADER_TOKEN,
            useClass: LoadingService
        },
        {
            provide: FORM2_LOADER_TOKEN,
            useClass: LoadingService
        }
    ]
})
export class AppComponent {
    loading1$: Observable<boolean>;
    loading2$: Observable<boolean>;

    constructor(
        @Inject(FORM1_LOADER_TOKEN) private loader1: LoadingService,
        @Inject(FORM2_LOADER_TOKEN) private loader2: LoadingService
    ) {
        this.loading1$ = this.loader1.loading$;
        this.loading2$ = this.loader2.loading$;
    }

    submit() {
        this.loader1.showLoaderUntilCompleted(timer(1000)).subscribe();
    }

    submit2() {
        this.loader2.showLoaderUntilCompleted(timer(1000)).subscribe();
    }
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.