Artful DI in Angular – Part 5: How to Inject a Dependency at the Right Point

To recap the first four parts of this series, we have built a service that looks up grocery-store produce information based on a PLU Code (Parts 1, 2, and 3) and have created an Angular attribute directive that will trigger the lookup when the PLU Code field loses focus (Part 4). The attribute used an abstract, generically typed LookupService<TRecord> and that starts to make things get interesting.

If you create a service with the ng generate service command, your generated class gets decorated with

@Injectable({
  providedIn: 'root'
})

The result will be that Angular will create a singleton instance of the service, at the root level of your application (although it may tree-shake it out later). There are two reasons why we don’t want this.

  1. If a single LookupService were used for the entire application, there would be only one lookup status and one looked-up record behind all of our Produce Lookups. A lookup on one row would return data used by all the rows. The service is designed to feed data to one and only one lookup.
  2. LookupService is an abstract class, so it’s not suitable for use at any level of the application. To be sure this does not happen, we have commented out LookupService‘s @Injectable decorator. As you will see shortly, we will always inject a derived class instead.

The derived class in our application is ProduceLookupService. We will inject it in a new component, PluTrioComponent, that Angular will instantiate once for each trio of Code, Name, and Image in our page.

Here is the code for PluTrioComponent. After the code, we’ll take up the discussion with some interesting aspects of the @Component decorator.

The selector property of the object passed to @Component lets Angular know that when it encounters an <app-plu-trio> in the HTML, it should create a new instance of our component and plug it in at that point. “New instance” is the key phrase; it means that our constructor will be called and the dependencies it requires will be injected.

There is only one dependency, which is for a ProduceLookupService. Well, that’s easy enough; that was the first thing in the providers property of the object passed to @Component. But how can we ensure that each instance of the component gets its own ProduceLookupService?

Also easy. We have modified the ProduceLookupService‘s @Injectable()decorator so it has no { providedIn: 'root' } argument. It is now up to us to inject the service by hand, so to speak, and that is exactly what happens in the first element of PluTrioComponent‘s providers array. Each trio component will get its own injected ProduceLookupService. So far, so good; we have now fulfilled the constructor’s dependency injection.

Next, Angular will process the component’s template, plu-trio.component.html. There, it will encounter the <input> control for the PLU code, with the appLookupKey attribute described in the last post.

That attribute has a dependency on LookupService<any> (<any> because the attribute is generic, designed to work for any type of record). How will Angular resolve that vague dependency?

Angular resolves its dependencies in two stages. First is “element injection,” in which Angular works its way up the DOM tree looking for providers that are specified in the components. Second is the “module injector” phase. This is the one that will terminate in the root module ('inRoot').

In our case, we have configured PluTrioComponent‘s providers so that a request for a LookupService will be fulfilled with the existing ProduceLookupService. That is the meaning of the useExisting property in the second provider. Angular will ask itself if it has one of those lying around and yes, it does: one that was injected into the constructor. Perfect!

Almost as a side note, let’s see how this component gets the results of the lookup. Here is the HTML.

The second <input> element has a [value] attribute (square brackets for one-way binding) that brings data in from a method in the lookup service. The method, which is in the abstract base class LookupService<TRecord>, is this simple:

  safeGetRecordProperty(propName: string): any {
    return this.record ? this.record[propName] : '';
  }

The <img> element also uses one-way binding, but with the curly-brace syntax. The getImgSrc() method that it calls is in the PluTrioComponent and is also quite simple. It just uses the PLU as part of an image filename, or returns an empty string if there is no PLU.

getImgSrc() {
  const imageKey = 
      this.produceLookupService.safeGetRecordProperty('imageKey');
  return imageKey ? `https://i.imgur.com/${imageKey}.jpg` : '';
}

The astute reader might wonder what happens if we end up with a bogus image name because there is no PLU. Will we get one of those ugly squares that means “image not found”? You will know the answer by the end of the next post.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.