You are on page 1of 39

Header

Microsoft Virtual Academy

Mastering Angular, Part 3


Services
Eric W. Greene

Produced by
• What are Angular Services?
• Getting Started with Angular
• Demo: Setting Up the Angular Starter Application
• Creating a Service
• Demo: Create a Logging Service
• Injecting a Service into a Host Service
• Demo: Build a Color Service with Logging
• Specifying an Alternate Service Class
• Demo: Swapping out the Original Color Service
• Injecting a Value as a Service
• Demo: Replacing the Logger Class with a Simple Object
• Using a Factory to Create a Service
• Demo: Choosing the Color Service with a Factory
• Injecting Services with an Opaque Token
• Demo: Injecting a Color Service without a Class
• Angular Services are values and objects which are injected into
components, directives and other services
• Services are general purpose coding blocks that perform many different
kinds of tasks, such as:
• Request and receive data to and from remote data sources such as
REST and GraphQL servers
• Business Logic including calculations, data transformations, and
validation
• System operations such as caching and logging
• Angular Services are not instantiated directly, they are injected to into
Angular parts such as components, directives and even other services
• The injection system is hierarchical in nature and provides a configuration
mechanism to determine what is injected for a given service
• Key to the injection system is a token to know which service to inject
• For injection by class, the class of the service can serve as the token
• For injection by interface, an opaque token must be used, because
interfaces do not appear in the final JavaScript transpiled by TypeScript
• Injection can be configure at the module level and the part level
• The providers option allows the service to be registered with an
Angular application part
• The providers option is available on modules, components, structural
and attribute directives
• The providers option is an array of service configurations
• Within the component tree, service injection is hierarchical in natures
• When a parent component defines a new provider configuration for a
service, the child components of the service will receive the service per the
new configuration
• Unlike AngularJS (version 1), Angular (version 2 and later) services are not
always singletons, they can be singletons, but if desire multiple instances of a
service can be instantiated
• Among JavaScript libraries and frameworks, dependency injection is
something unique to Angular
• The configuration can be a little tricky, especially when overriding
default configurations within a hierarchy
• Among the many benefits is the ability to change implementations of a
service without having to rewrite a lot of code, and the ability to replace
concrete instances with mocked versions when unit testing, etc…
• Angular applications require some initial project configuration to get up
and running
• There are many ways to get started
• Angular CLI
• Numerous Starter Projects
• From Scratch
• This course will use a starter project configured with WebPack 2
• The WebPack development server will serve the web pages, and
WebPack will bundle the application files
• The initial starter project can be downloaded from GitHub
• Git is not required, simply fire up a web browser, and visit the following
link
• https://github.com/training4developers/mastering-angular-starter
• In the demonstration we will download, configure and fire up the project
Demo
Setting Up the Angular Starter Application
• Services can be created from classes or from values
• In this section, the focus is on classes, values will be discussed later
• To create a service with a class all that is need is to define the class,
register it with the Angular part, then inject it via the constructor
• The class is coded as nothing more than a regular JavaScript class,
nothing more is needed
• Services must be registered via the providers option
• Each Angular part which will use the service does not need to register it,
but it must be registered at the module level for all parts in the module
to use it
• In the case of the component tree, if the service is not registered in the
module, it must be registered in the component (or a parent component
of the component) which desires to use it
• When creating a service with a class, the class simply needs to be listed
in the providers array
Example of Module Registration Example of Component Registration
@NgModule({ @Component({
imports: [ BrowserModule ], selector: "widget-tool",
declarations: [ AppComponent ], template: require("./app.component.html"),
bootstrap: [ AppComponent ], styles: [ require("./app.component.scss") ],
providers: [ SomeService ], providers: [ SomeService ],
}) })
export class AppModule { } export class AppComponent { }
• A common pattern in
TypeScript (and even other class Item {

languages such as Java & C#) private name: string;

is to define a class instance constructor(name: string) {


property (usually private), this.name = name;
then directly assign to it some }
value passed in via the }

constructor with the


assignment occurring the
constructor implementation
itself
• Programming languages evolve to
eliminate the repetitive code
class Item {
constructor(
• TypeScript supports a feature known private name: string
as parameter properties which ) { }
transforms this common pattern to a }

less verbose syntax


• By adding private to the parameter This feature works for the
other access modifiers as well:
definition, a property on the instance public, protected and
will be created and assigned a value readonly
when the class is instantiated
• Commonly, parameter properties are used for injecting services into
Angular parts
• Typically, most service injections are private
• Should the injected service need to changed from the outside, its
best to do it via a provider configuration when injecting it, not
accessing a public property on the class instance
Demo
Create a Logging Service
• A plain service class cannot have other services injected into it
• For a class to accept services injected into it, it must be decorated with
the @Injectable decorator
• The injectable decorator is available from the Angular Core module and
it has no configuration parameters
• The injectable decorator indicates to Angular that the class expects
services to be injected into the constructor when its instantiated
• If a class is not marked injectable, and a constructor parameter is listed,
Angular will give an error similar to this
• Error: Can't resolve all parameters for <Service Name>
• When injecting a service into a host service, the provider configuration
for the injected service is provided by the module, component or
directive into which the host service is being injected
• Services themselves do not have a providers option
• As a best practice, decorate all services with @Injectable
Both services must
be registered to
use the
class SomeService { } HostService in the
@Component({ component
@Injectable() … omitted …
class HostService { providers: [ SomeService, HostService ]
constructor( })
private someService: SomeService class AppComponent {
) { } constructor(
} private hostService: HostService
) { }
But both services do
NOT have to be }
passed into the
component's
constructor
Demo
Build a Color Service with Logging
• The provider configuration can be customized using several different
options
• In the earlier examples, the provider configuration was nothing more
than a service class registered in the providers array
• This registration was short-hand syntax for the following
• With the shorthand syntax, the provide token and the class to be
instantiated are the same JavaScript object
• With the full syntax, the provide token object and the class can be
different JavaScript objects
• This permits alternative implementations of the service to be specified
without having to recode the implementation of the component (or
other Angular part)
• This is useful for passing in mock services when performing unit testing
@Component({
@Injectable() … omitted …
class Logger() { } providers: [ {
provide: Logger, useClass: WebSocketLogger
@Injectable() }]
class WebSocketLogger { } })
class AppComponent {
constructor(private logger: Logger) {
The provide token is the // outputs true
Logger class, but the console.log(
injected instance is from this.logger instanceof WebSocketLogger
the WebSocketLogger );
class }
}
Demo
Swapping out the Original Color Service
Injecting a Value as a Service

• While many services will be classes, its also possible to use primitive
value or other existing objects as services as well
• Using primitive values in one approach to distributing configuration or
constant values throughout the application
• Using existing objects enables the object creation process to exist
outside of Angular, while still benefiting from the dependency injection
system when actually using the object
• Using a value still requires a valid provide token, for now that will be a
class, in last section opaque tokens will be discussed
@Component({
@Injectable() … omitted …
class Logger() { } providers: [ {
provide: Logger, useValue: webSocketLogger
const webSocketLogger = { }]
… omitted … })
} class AppComponent {
constructor(private logger: Logger) {
The provide token is the // outputs true
Logger class, but the console.log(
injected value is the this.logger === webSocketLogger
webSocketLogger object );
}
}
Demo
Replacing the Logger Class with a Simple Object
• In addition to allowing Angular to instantiate the service automatically
(useClass option) or simply passing a value (useValue option) its possible
to implement a custom service creation function and register it with the
provider through the useFactory option
• The function can perform virtual any kind of logic at the moment the
service is injected to programmatically determine what will be injected
at runtime
• If the factory function requires other services to perform its logic, it can
request that other services be injected into the factory functions via the
deps option
@Injectable() @Component({
class Logger() { } … omitted …
providers: [ {
const getLogger = () => { provide: Logger, useFactory: getLogger
return new Logger(); }]
} })
class AppComponent {
The provide token is the constructor(private logger: Logger) { }
Logger class, but the
}
injected value is the new
instance of Logger
created in the factory
Demo
Choosing the Color Service with a Factory
• When a service will be implemented with a single class, then a class
provider token works well
• However there many instances when a single class is not desired for a
service
• Example include using values or factories which are not tied to a class
• Or even injecting services based upon an interface
• For these situations, a different kind of token is desired, an opaque
token
• Opaque tokens allow the service provider token to be decoupled from
how the service is created
• The decoupling logically separates the "what" from the "how"
• In general, this kind of decoupling is a good software practice, and is the
heart of dependency injection
• Practically, opaque tokens do not need to be used for all services, use
class tokens when appropriate, but when decoupling is needed use the
opaque token
• To create and use opaque tokens, the OpaqueToken class and Inject
decorator need to imported from Angular Core
• A new opaque token is created by instantiating a new object with the
OpaqueToken class
• The opaque token object is then passed into the Inject decorator to
associate a constructor parameter with the service to be injected
• Instead of the class being used, the explicitly assigned token is
• The Inject decorator decorates the constructor parameter
const Logger = new OpaqueToken("logger"); @Component({
… omitted …
const webSocketLogger = { providers: [ {
log(message: string) { provide: Logger,
console.log(message); useValue: webSocketLogger,
}, } ]
}; })
The provide token is the class AppComponent {
Logger opaque token, but constructor(
the injected value is the @Inject(Logger) private logger: any
webSocketLogger object
) { }
}
this is usually an interface
• Starting with Angular 2, regular updates to Angular will be occurring,
with new major releases every six months
• With Angular 4 (coming March 2017), a new kind of token named an
Injection token will be use instead of Opaque tokens
• Injection tokens inherit from Opaque tokens, and moving forward
Opaque tokens will be considered deprecated
• While not covered here, Injection tokens simplify the process of directly
using tokens to inject services
Demo
Injecting a Color Service without a Class
• In this course, Angular Services were explained and explored
• Services are injected via dependency injection, and they are configured
within Angular using the providers option of modules, components and
directives
• The providers option supports several configuration options including
useClass, useValue, and useFactory
• Dependency injection is hierarchical in nature, and unlike AngularJS
(version 1), services in Angular (version 2 or later) are not necessarily
singletons
• In a future lesson, additional details of services and their configuration
will be explored

You might also like