CHAPTER 11
In this chapter, we will go over authoring services for encapsulating business logic that can be shared across all view-models via dependency injection. We will also discuss HTTP Services, focusing on the aurelia-fetch-client. Let’s start with discussing services in general.
Services are a great way to encapsulate logic and data that may need to be referenced in multiple places in your application and across multiple classes. We will introduce a simple service called AppService. The AppService is a general-purpose property bag that will allow us to have common data in one place across our application. We will be able to inject this service into any view-model that desires to have access to properties and methods on this class. For our purposes, we will just have a simple API. Let’s take a look at the file app-service.js:
Code Listing 110
export class AppService { user; constructor() { } } |
This simple class allows us to assign a user object to the AppService class, and then share it with any other view-model that desires access to the current user.
We use this service in our order-history.js file, as shown in the following code:
Code Listing 111
imports {AppService} from './services/app-service'; imports {DataService} from './services/data-service'; export class OrderHistory { static inject = [AppService, DataService]; constructor(appService, dataService) { this.appService = appService; this.dataService = dataService; } async activate() { this.data = await this.dataService.getOrdersByUserId( this.appService.user.id); } } |
In this example, we are bringing in two dependencies: AppService and DataService. We need the AppService so that we can make a call in our DataService to get Orders for a given user by their ID. By using the AppService singleton, we are able to attach application-level data to the object and inject it into any of the screens and services that need it.
Besides generic services, let’s focus our attention on HTTP Services and see how we might be able to access data. With that said, Aurelia comes with two services available:
We will be using the aurelia-fetch-client for our example. Please refer to the Aurelia documentation here to better help you make the right decision about which one to use.
We are now ready to take a look at what the DataService class might look like. Consider the following:
Code Listing 112
import {HttpClient} from 'aurelia-fetch-client'; // polyfill fetch client conditionally const fetch = !self.fetch ? System.import('isomorphic-fetch') : Promise.resolve(self.fetch); export class DataService { static inject = [HttpClient]; constructor(http) { http.configure(config => { config .useStandardConfiguration() .withBaseUrl('<add your base URL here>'); }); this.http = http; } async getOrdersByUserId(userId) { try { let response = await this.http.fetch(`orders/${userId}`); let data = await response.json(); return data; } catch(error) { console.log(error); return null; } } } |
After we import aurelia-fetch-client, we are ensuring that the browser we are running under supports the Fetch API. Otherwise, we are bringing in a polyfill. Next, we are injecting the dependency into the constructor where we configure the service. We are using a standard configuration and leaving the base URL to be relative to whatever backend service you wish to call. Finally, we have our getOrdersByUserId function that takes in a userId as a parameter. This function is marked as async, and we see we are using the await keywords inside the method for any asynchronous operations. We finish by returning the data or by returning null, depending on if we encountered an exception or not.
The hardest part is done, and you should be able to extend this service out to handle POST, PUT, and DELETE verbs to make your DataService complete.