Converting AngularJS services to Angular services

Loading

When migrating an application from AngularJS (1.x) to Angular (2+), a crucial step is transitioning from the AngularJS service system to the Angular service system. AngularJS services are based on constructor functions, whereas Angular services are typically provided as classes with dependency injection (DI).

In this guide, we will walk through the process of converting an AngularJS service to an Angular service, explaining the key differences and providing examples.


1. Overview of Services in AngularJS vs Angular

In AngularJS (1.x)

In AngularJS, services can be created in a few different ways, such as using factories, services, or providers. Services are typically singleton objects that can be injected into controllers or other services.

Example of a service in AngularJS:

angular.module('app')
.service('MyService', function() {
this.getData = function() {
return 'Data from AngularJS Service';
};
});

In Angular (2+)

In Angular, services are typically classes that are decorated with the @Injectable() decorator to specify that they can be injected into other components, services, or modules via dependency injection. Angular services are instantiated by Angular’s DI system.

Example of a service in Angular:

import { Injectable } from '@angular/core';

@Injectable({
providedIn: 'root', // Automatically provided at the root level
})
export class MyService {
getData() {
return 'Data from Angular Service';
}
}

2. Key Differences Between AngularJS and Angular Services

AspectAngularJSAngular (2+)
Service CreationUses constructor functions or object literalsDefined as classes using @Injectable decorator
SingletonServices are singletons by defaultServices are singletons by default, or based on scope (providedIn)
Dependency InjectionInjected using angular.module.service() or $injectorInjected via constructor and DI system (automatic)
Module-Level DeclarationDefined at module level (angular.module())Declared and provided in @Injectable or in NgModules
ScopeServices have access to $scope, $rootScopeServices are independent and have no access to scopes

3. Converting AngularJS Service to Angular Service

Step 1: Understand the AngularJS Service Structure

Let’s start by examining a simple AngularJS service.

javascriptCopyEdit// AngularJS Service (Old)
angular.module('app')
  .service('UserService', function($http) {
    this.getUser = function(id) {
      return $http.get('/api/user/' + id);
    };
  });

Step 2: Convert to Angular Service

In Angular, the UserService is now a class decorated with @Injectable(), and dependency injection is done through the constructor. Angular services are generally not tied to $scope, and they are provided in modules via Angular’s dependency injection system.

// Angular Service (New)
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
providedIn: 'root', // Singleton provided at the root of the app
})
export class UserService {
constructor(private http: HttpClient) {}

getUser(id: string): Observable<any> {
return this.http.get(`/api/user/${id}`);
}
}

Here are the changes made:

  • Class-based service: The service is now a TypeScript class.
  • @Injectable() decorator: This decorator allows Angular to inject the service’s dependencies. By using providedIn: 'root', the service is available throughout the entire application.
  • Dependency Injection: Angular uses its DI system to inject the HttpClient service, which is Angular’s HTTP service.
  • Observable: The HTTP request now returns an Observable rather than a promise, which is a more modern approach in Angular.

4. Replacing $scope or $rootScope in AngularJS Services

In AngularJS, services could directly access $scope or $rootScope. In Angular, there is no $scope, so we need to use Angular’s BehaviorSubject, Subject, or services for global state management if required.

Example of Using a Service to Manage Global State

In AngularJS, a common pattern was to manage shared data in a service and bind it to $scope.

// AngularJS Service Example with $scope
angular.module('app')
.service('UserService', function($rootScope) {
this.getUser = function(id) {
// Simulating a data update
$rootScope.currentUser = { id: id, name: 'John Doe' };
};
});

In Angular, we can achieve the same functionality using a service with RxJS subjects.

// Angular Service Example with BehaviorSubject
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';

@Injectable({
providedIn: 'root',
})
export class UserService {
private currentUserSubject = new BehaviorSubject<any>(null); // Holds the current user data
currentUser$ = this.currentUserSubject.asObservable(); // Observable to subscribe to changes

constructor() {}

setUser(user: any) {
this.currentUserSubject.next(user); // Update the user data
}
}

With this approach:

  • BehaviorSubject holds and emits the latest value to any subscribers.
  • currentUser$ is the observable that components can subscribe to, enabling reactive state updates.
  • setUser updates the state (similar to $rootScope.currentUser in AngularJS).

Component Usage

To consume the UserService in a component, we can use the Angular async pipe to automatically subscribe to the observable and update the UI when the data changes.

// Component Example using UserService
import { Component } from '@angular/core';
import { UserService } from './user.service';

@Component({
selector: 'app-user',
template: `
<div *ngIf="userService.currentUser$ | async as user">
<h1>{{ user.name }}</h1>
</div>
`
})
export class UserComponent {
constructor(public userService: UserService) {}
}

Here, the component is automatically subscribed to currentUser$ and displays the user data when it changes.


5. Handling Error Handling and Promises

In AngularJS, services often dealt with $http promises directly. In Angular, you generally work with Observables from the HttpClient service, which gives you more flexibility.

AngularJS Example (with $http promise):

angular.module('app')
.service('UserService', function($http) {
this.getUser = function(id) {
return $http.get('/api/user/' + id)
.then(response => response.data)
.catch(error => {
console.error('Error fetching user', error);
throw error;
});
};
});

Angular Example (with HttpClient and Observable):

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';

@Injectable({
providedIn: 'root',
})
export class UserService {
constructor(private http: HttpClient) {}

getUser(id: string): Observable<any> {
return this.http.get(`/api/user/${id}`).pipe(
catchError((error) => {
console.error('Error fetching user', error);
return throwError(error);
})
);
}
}

In this Angular example:

  • Observables are used for handling HTTP requests.
  • The catchError operator is used to handle errors.

Leave a Reply

Your email address will not be published. Required fields are marked *