liveBook · Manning

Angular service

As explained in Modern Angular

An Angular service is an application class that encapsulates reusable logic or state and can be provided through Angular dependency injection.

An Angular service packages application behavior or shared state behind dependency injection. It can handle work that components should not own directly, such as authentication requests, state updates, and asynchronous operations. section 2.3.1 section 7.3.4

In a standalone component, a service can be added to the component’s providers array and then used by the component. A login component, for example, delegates its authentication API call to AuthService rather than embedding that call in the template logic. section 2.3.1

Listing 7.13 State management with advanced RxJS interoperability
@Injectable({ providedIn: 'root' })
export class TimeOffManagementService {
  private readonly timeOffRequestService = inject(TimeOffRequestService);












  deleteRequest$ = new Subject<TimeOffRequest>();  #1
  approveRequest$ = new Subject<TimeOffRequest>();
  rejectRequest$ = new Subject<TimeOffRequest>();
  selectedType = signal<
    'Vacation' | 'Sick Leave' |  'Maternity Leave' | 'Paternity Leave' | 'Other' | ''
  >((localStorage.getItem('selectedType') as any) ?? 
     '');  #2
  requests = toSignal(  #3
    merge(  #4
      toObservable(this.selectedType),  #5
      this.deleteRequest$.pipe(switchMap((r) => this.timeOffRequestService.deleteRequest(r.id))),  #6
      this.approveRequest$.pipe(switchMap((r) => this.timeOffRequestService.approveRequest(r.id))),
      this.rejectRequest$.pipe(switchMap((r) => this.timeOffRequestService.rejectRequest(r.id))),
    ).pipe(
      switchMap(() => {
        return this.timeOffRequestService
          .getRequestsByType(  #7
            this.selectedType(),
          );
      })
    ),
    {
      initialValue: [] as TimeOffRequest[],
    }
  );
  resolvedRequests = computed(() =>
    this.requests().filter((r) => r.status !== 'Pending')
  );

  constructor() {  #8
    effect(() => {
      localStorage.setItem('selectedType', this.selectedType());
    });
  }

  approveRequest(request: TimeOffRequest) {
    this.approveRequest$.next(request);  #9
  }

  rejectRequest(request: TimeOffRequest) {
    this.rejectRequest$.next(request);
  }

  deleteRequest(request: TimeOffRequest) {
    this.deleteRequest$.next(request);
  }
}

A root-provided service combines RxJS event streams with signals to centralize time-off request state and actions. Components expose the service’s signals and delegate approve, reject, and delete operations back to it. section 7.3.4

Keep services practical in larger applications by separating responsibilities:

  • Share data and behavior across components through a single service. section 7.3.4
  • Move asynchronous work into the service so consuming components remain small and easier to test. section 7.3.4
  • Use RxJS subjects for events and signals for reactive state derived from those events. section 7.3.4

Services that use inject() need an injection context in unit tests. Configure TestBed with required dependencies, run construction through runInInjectionContext, and use HttpTestingController to mock HTTP calls instead of making real requests. section 8.3.4

How Modern Angular covers it

How deep it goes
Demonstrates provider setup, inject()-based state services, reactive event handling, and unit-test initialization.
Examples
Uses authentication login and time-off request management services, plus mocked HTTP and localStorage tests.
What you can do after
Apply it via Angular dependency injection to move shared state, API work, and actions out of components.
How it connects
Connects dependency injection with standalone components, RxJS, signals, HttpClient, and unit testing.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Modern Angular. The section links point to the parts of the book it draws on.