Defining Optional Dependencies / Arguments Using ES5 In Angular 2 Beta 3
On the heels of my last post, about building a better mental model of decorators and meta-data in Angular 2, I thought I would follow-up with a quick post on optional arguments. When you're using ES6 and the fancy decorators, defining an optional dependency-injection argument is straightforward. But, when you're using ES5, defining an optional argument is not quite as obvious.
Run this demo in my JavaScript Demos project on GitHub.
The optionality (and other properties like host-visibility) of an injectable argument is defined as meta-data about that argument. We've already seen that we can define the injectable types using the ".parameters" property on the constructor. But, in order to define meta-data about an individual argument within that parameters list, we have to provide each relevant argument as an array of meta-data.
In the following demo, I've created a service that takes three arguments. The first argument is required. The second and third arguments are optional. You'll see that the first argument is just a naked Inject() meta-data reference; but, the latter two arguments are individual arrays that contain both the Inject() meta-data as well as the Optional() meta-data.
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>
Defining Optional Dependencies / Arguments Using ES5 In Angular 2 Beta 3
</title>
</head>
<body>
<h1>
Defining Optional Dependencies / Arguments Using ES5 In Angular 2 Beta 3
</h1>
<!-- Load demo scripts. -->
<script type="text/javascript" src="../../vendor/angularjs-2-beta/3/es6-shim.min.js"></script>
<script type="text/javascript" src="../../vendor/angularjs-2-beta/3/Rx.umd.min.js"></script>
<script type="text/javascript" src="../../vendor/angularjs-2-beta/3/angular2-polyfills.min.js"></script>
<script type="text/javascript" src="../../vendor/angularjs-2-beta/3/angular2-all.umd.js"></script>
<!-- AlmondJS - minimal implementation of RequireJS. -->
<script type="text/javascript" src="../../vendor/angularjs-2-beta/3/almond.js"></script>
<script type="text/javascript">
// Defer bootstrapping until all of the components have been declared.
// --
// NOTE: Not all components have to be required here since they will be
// implicitly required by other components.
requirejs(
[ "MyService" ],
function run( MyService ) {
// Here, we are explicitly creating an Angular 2 injector without
// bootstrapping an entire application.
var injector = ng.core.Injector.resolveAndCreate(
[
MyService,
ng.core.provide(
"REQUIRED_ARG_A",
{
useValue: "A"
}
)
]
);
// Get the MyService instance - this will cause the class to be
// instantiated with the optional and required arguments.
var myService = injector.get( MyService );
console.log( "Successfully created MyService instance:" );
console.dir( myService );
}
);
// --------------------------------------------------------------------------- //
// --------------------------------------------------------------------------- //
// I provide a service that has both optional and required arguments.
define(
"MyService",
function registerMyService() {
// When we configure the dependency-injection parameters, we're allowing
// two of the three arguments to be OPTIONAL. The optionality of each
// argument is defined as meta-data for that argument. And, when we
// provide meta-data for an argument, we have to define the parameter
// entry as an array that contains the injectable type as well as any
// modifiers (such as Optional() and Host()).
MyService.parameters = [
// Define required argument.
new ng.core.Inject( "REQUIRED_ARG_A" ),
// Define optional argument. Notice the Optional() meta-data is
// the SECOND item in the array (order doesn't matter).
[ new ng.core.Inject( "OPTIONAL_ARG_B" ), new ng.core.Optional() ],
// Define optional argument. Notice the Optional() meta-data is
// FIRST item in the array (order doesn't matter).
[ new ng.core.Optional(), new ng.core.Inject( "OPTIONAL_ARG_C" ) ]
];
return( MyService );
// I initialize the service using the required and optional arguments.
function MyService( argA, argB, argC ) {
this.argA = argA;
this.argB = ( argB || "not provided" );
this.argC = ( argC || "not provided" );
}
}
);
</script>
</body>
</html>
As you can see, the second and third arguments are defined as collections of meta-data references. And, you can also see that the order of the meta-data references is irrelevant. In fact, if you look at the source code (in Angular 2 Beta 3), you can see that Angular is looping over this array and tracking specific values as it finds them.
When we run this code, we can see that the MyService instance was successfully instantiated; and, that it only required one of the three arguments:
When you're configuring the dependency-injection arguments for a class in Angular 2, not all arguments have to be required; but, optimality has to be explicitly identified for each optional argument. If you're using ES6 and the Angular 2 decorators, you'll probably never have to worry about this syntax. But, if you're using ES5, hopefully this post was helpful.
Want to use code from this post? Check out the license.
Reader Comments