npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

ngx-inject

v4.1.0

Published

Dependency injection utilities for Angular

Downloads

9,382

Readme

Build Status NPM Version

ngx-inject - dependency injection utilities for Angular

This package provides a variety of utility functions and types related to Angular's dependency injection framework. It ships with the following features:

Installation and usage

Add the module to your package.json file:

npm install --save ngx-inject

After having installed the ngx-inject package you might need to update your project configuration depending on the build tools you use, e.g. SystemJS or Karma. The ngx-inject package is published in the Angular Package Format, so it should work with most build tools. If you use the Angular CLI then you don't need to perform any additional configuration steps.

Once the dependency has been installed and your build tooling configuration has been updated (if necessary), you can start using the library by importing the features you need. The following sections describe how to import and use these features.

Angular version compatibility matrix

Use the compatibility matrix below to determine which version of this module works with your project's Angular version.

| Library version | Angular version | | ------------------------ | --------------- | | ngx-inject - 1.x.x | >= 6.0.0 | | ngx-inject - 2.x.x | >= 8.0.0 | | ngx-inject - 3.x.x | >= 10.0.0 | | ngx-inject - 4.x.x | >= 13.0.0 |

Provider binding

The provider binding feature from ngx-inject lets you separate the definition of providers in the what and how: which token vs how should a value for injection be obtained for that token. This separation is made possible by introducing the concept of an unbound provider: a provider definition without a provide property.

An unbound provider is represented using the UnboundProvider<T> type which has a type argument T that determines the type of the value that is obtained by the provider. To convert an unbound provider into a full provider that can be used by Angular's dependency injection framework it has to be bound to a token. This is done using the bindProvider function which takes a token and an unbound provider as arguments and combines them to yield a Provider instance.

The bindProvider function is compatible with Angular's AoT compiler, so it can safely be used without running into function calls are not supported errors or injected dependencies being undefined at runtime.

Together with the bindProvider function the unbound provider concept offers a nice method for modules to specify dependencies that need to be provided outside of the module (e.g. configuration) in a type-safe manner. You can read more about this concept in the article Explicit and type-safe Angular module configuration. One way of defining these dependencies is by creating a static function in your module that returns a ModuleWithProviders object. This is illustrated in the following example:

import { Inject, InjectionToken, ModuleWithProviders, NgModule } from '@angular/core';
import { UnboundProvider, bindProvider } from 'nxg-inject';

export interface MyConfig {
    importantData: string;
}

export const MY_CONFIG = new InjectionToken<MyConfig>('MY_CONFIG');

export class MyService {
    constructor(
        @Inject(MY_CONFIG) private config: MyConfig
    ) { }
}

@NgModule({
    providers: [
        MyService
    ]
})
export class MyModule {
    public static withConfiguration(config: UnboundProvider<MyConfig>): ModuleWithProviders<MyModule> {
        return {
            ngModule: MyModule,
            providers: [
                bindProvider(MY_CONFIG, config) // Module consumer specifies how to resolve the config
            ]
        };
    }
}

As can be seen from this example: a consumer of MyModule has to specify how to resolve a value for an instance of MyConfig, while the the module itself takes care of binding it to the correct token (so it will later be available for injection in the MyService constructor). This construct makes it explicit what dependencies (in the form of providers) are needed by a module, without consumers having to know exactly which tokens need to be provided. Also, since the bindProvider function and UnboundProvider model are fully type-safe a consumer cannot make the mistake of providing a value of an incorrect type.

MyModule.withConfiguration({ useValue: 5 }); // <-- TYPE ERROR!
MyModule.withConfiguration({ useValue: { importantData: 'hmmm pie!' } }); // OK :)

Provider binding API

Unbound providers can be represented using the UnboundProvider<T> type. The type parameter T is the type of value that will be resolved by the provider. Under the hood, the UnboundProvider<T> type, is simply the union of the following types:

  • UnboundTypeProvider - Uses the specified class to instantiate a value for injection. The instance will be reused for subsequent usages of the same class as UnboundTypeProvider. In addition the class itself will also be made available as token for injection.

    Model Definition: UnboundTypeProvider<T> = Type<T>

  • UnboundValueProvider - Uses the value of the useValue field for injection.

    Model Definition: UnboundValueProvider<T> { useValue: T }

    Values of this type can either by created manually using an object literal or using the useValue function:

    import { useValue } from 'ngx-inject';
    
    useValue(42); // UnboundValueProvider<number>
  • UnboundClassProvider - Creates an instance of the specified class as injection value. Note that unlike UnboundTypeProvider multiple uses of the same class as UnboundClassProvider will also result in multiple instances of that class. Also keep in mind that the class needs to be decorated with @Injectable for Angular to be able to resolve its dependencies.

    Model Definition: UnboundClassProvider<T> { useClass: Type<T>;}

    Values of this type can either by created manually using an object literal or using the useClass function:

    import { useClass } from 'ngx-inject';
    
    useClass(MyService); // UnboundClassProvider<MyService>
  • UnboundExistingProvider - Reuses another provider, which is referenced using the specified token, to resolve the value that will be used for injection.

    Model Definition: UnboundExistingProvider<T> { useExisting: ProviderToken<T> }

    The useExisting field can either be an instance of Angular's InjectionToken class or a direct reference to a class (abstract classes are supported as well).

    Values of this type can either by created manually using an object literal or using the useExisting function:

    import { useExisting } from 'ngx-inject';
    
    useExisting(HttpClient); // UnboundExistingProvider<HttpClient>
  • UnboundFactoryProvider - Uses the specified factory function to create an injection value.

    Model Definition: UnboundFactoryProvider<T> { useFactory: (...deps: any[]) => T; deps?: unknown[] }

    The factory function can take an arbitrary number of parameters. It is invoked with resolved values of tokens in the deps field.

    Values of this type can either by created manually using an object literal or using the useFactory function:

    import { useFactory } from 'ngx-inject';
    
    function createUuidStream(httpClient: HttpClient): Observable<string> {
        return httpClient.get<string>('https://my-uuid-server.com');
    }
    
    useFactory(createUuidStream, HttpClient); // UnboundFactoryProvider<Observable<string>>

    Using the useFactory function is recommended over object literals as it will enforce type checking on the dependencies (which resolve to the arguments of the factory function).

  • UnboundDirectValueProvider - Short version of UnboundValueProvider, where you can just use the value directly instead wrapping it in an object with a useValue property.

    Model Definition: UnboundDirectValueProvider<T> = T

bindProvider function

Signature:

function bindProvider<T, U extends T>(
    token: ProviderToken<T>,
    unboundProvider: UnboundProvider<U> | undefined,
    options: {
        multi?: boolean;
        default?: UnboundProvider<U>;
    } = {}
): Provider;

Parameters:

  • token - Token which for which the provider is to be defined. This can either be an instance of Angular's InjectionToken class or a direct reference to an (abstract) class.
  • unboundProvider - Definition of a provider which should be bound to the specified token. This should be unbound provider that yields a value of type T or subtype of T. Another valid argument value for this parameter is undefined. If a default provider (options.default) is specified, then that provider will be used instead. When this is not the case, the bindProvider function returns an empty array, which is a valid Angular provider value to represent nothing.
  • options - Extra binding options. This is an optional parameter.
  • options.multi - Whether the provider should be contribute to a "multi-provider" (resulting an array of instances when injected).
  • options.default - Default provider definition to use when the argument value of the unboundProvider parameter is undefined.

Return value:

A value that conforms to Angular's Provider type, which can be used in the providers list of module metadata.

Eager provider loading

This feature enables your application to eager load providers if necessary. By default Angular loads all providers in a lazy manner, i.e. only whenever other components or services that are instantiated (via Angular's dependency injection framework) require them as a dependency. While this is actually a good thing and is desirable for the majority of the cases, sometimes you might have a valid reason to have your provider(s) loaded directly. This is the case in particular for providers that play an active role in the background of your application, but are never referenced by other components or providers. For more information when this is useful and the rationale for this package see the following article: Eager loading in Angular 2

Eager loading API

To enable eager loading for your provider(s), first find the @NgModule class in which the provider should be loaded. Usually you'll want to set this up in your application root module (often called AppModule). Once you've found the module in which the provider should be loaded, import the EagerProviderLoaderModule:

import { EagerProviderLoaderModule } from 'ngx-inject';

@NgModule({
    imports: [ EagerProviderLoaderModule ]
})
export class AppModule { }

By importing the EagerProviderLoaderModule your application will load all providers on startup that have been registered as eagerly loaded providers. Although not strictly necessary, it is best to import this module in every module in which you mark providers for eager loading. This improves the reusability of those modules, since they don't depend on other modules to import the EagerProviderLoaderModule.

After having imported the EagerProviderLoaderModule, you need to define which providers you want to have loaded eagerly. This is done using the eagerLoad function, which you simply wrap around the provider(s) that should be eagerly loaded, for example:

import { EagerProviderLoaderModule, eagerLoad } from 'ngx-inject';

@NgModule({
    imports: [ EagerProviderLoaderModule ],
    providers: [
        eagerLoad(AppTitleUpdater)
    ]
})
export class AppModule { }

The eagerLoad function takes a Provider as input and returns a new provider instead. That new provider is simply an array of two providers:

  • The original input provider
  • A special marker provider used by the EagerProviderLoaderModule to discover which providers need to be eagerly loaded.

Since Angular's Provider type also supports arrays (of providers) you can fold multiple eagerLoad calls into one. This is shown in following example:

import { EagerProviderLoaderModule, eagerLoad } from 'ngx-inject';

@NgModule({
    imports: [ EagerProviderLoaderModule ],
    providers: [
        eagerLoad([
            AppTitleUpdater,
            ConsoleLogger,
            LocalizationInitializer
        ])
    ]
})
export class AppModule { }

The pattern of importing the EagerProviderLoaderModule within a module, together with one or more eagerLoad function calls is quite common. Because of this common pattern a convenience method has been added to combine this all into a single function: the static for function of the EagerProviderLoaderModule class. Using this function the previous example can be rewritten as:

import { EagerProviderLoaderModule, eagerLoad } from 'ngx-inject';

@NgModule({
    imports: [
        EagerProviderLoaderModule.for([
            AppTitleUpdater,
            ConsoleLogger,
            LocalizationInitializer
        ])
    ]
})
export class AppModule { }

Both the eagerLoad and EagerProviderLoaderModule.for functions are compatible with Angular's AoT compiler.

Lazy module loading support

The eager provider loader package has support for lazy loaded modules. However, before using eager provider loading for lazy loaded modules ask yourself whether it actually makes sense that the provider should only be loaded when the module itself is loaded. In most cases I expect that the provider must be loaded on application startup anyway. If so, you can simply move the eager loading registration to the module that is being used to bootstrap the application (or to another module that is transitively imported by the root module).

If you still feel that you need eager provider loading for a lazy loaded module, then you can use it in the same way as you would for modules that get loaded on application startup. Just don't forget to import the EagerProviderLoaderModule within these lazy loaded modules.

Note that for providers defined in lazy loaded modules there is a small difference for eager loaded providers compared to normal providers. Services defined in lazy loaded modules might be instantiated multiple times by Angular itself. The eager provider loader, however, will only load providers marked for eager loading once. If a provider has already been eagerly loaded, then the EagerProviderLoaderModule will do not this again for the same provider. This ensures that the service defined by the provider is only instantiated once.