Dependency Injection with FrintJS

Image by Saba Talat

Since the v1 release in early 2017, the core of the FrintJS framework has been mostly responsible for handling dependencies in your Apps. While other packages are mostly built around the core frint package’s API.

Providers

We have a terminology for dependencies in your FrintJS apps. We call them providers. Providers are any value that you can define in your FrintJS App, which can later be obtained by their unique names.

Creating a FrintJS app is as simple as:

import { createApp } from 'frint';
const MyApp = createApp({
name: 'MyAppName',
});

Below we will show you how providers can be defined in your App in three different ways.

(You will find some similarities with Angular 2+, that’s because frint uses a fork of diyai, which is a port of the Angular’s Injector API built in a way to be used without needing TypeScript)

Known values

If you already know the value of your provider, you can use the useValue property like this:

import { createApp } from 'frint';
const MyApp = createApp({
name: 'MyAppName',
  providers: [
{
name: 'foo',
useValue: 'foo value here',
},
],
});

Now once you instantiate your App, you can get the foo value as follows:

const app = new MyApp();
const foo = app.get('foo'); // `foo value here`

Generated values

There are times, when you don’t really know the value of your provider, and can only generate them when the App itself is instantiated. The useFactory property can be used in those scenarios:

import { createApp } from 'frint';
const MyApp = createApp({
name: 'MyAppName',
  providers: [
{
name: 'bar',
useFactory: function () {
return 'bar value here';
},
},
],
});

Now the bar provider can be obtained from your App’s instance as follows:

const app = new MyApp();
const bar = app.get('bar'); // `bar value here`

Please note that, the function that generates bar's value is only called once during the App’s instantiation. After that, it will keep returning the same cached value from the App’s internal registry of all providers.

Generated from ES6 classes

There are also cases, where you can also write your providers as ES6 classes first:

class Baz {
getValue() {
return 'baz value here';
}
}

To set ES6 classes as providers, we can use the useClass property when defining them in FrintJS Apps:

import { createApp } from 'frint';
const MyApp = createApp({
name: 'MyAppName',
  providers: [
{
name: 'baz',
useClass: Baz,
},
],
});

Now whenever your App gets instantiated, it will also instantiate the Baz class, and set the instance as baz provider’s value.

const app = new MyApp();
const baz = app.get('baz'); // instance of Baz class
console.log(baz.getValue()); // `baz value here`

Similar to useFactory, the class will be instantiated only once during your App’s instantiation and will return the same cached value every time you do app.get('baz').

Injecting providers in other providers

Now that we understand how providers can be defined in various ways in FrintJS Apps, we can go further into advanced use cases where one provider may depend on another provider.

From above examples, let’s say bar provider needs to know the value of foo. How do we inject foo into bar?

We can use the deps (short for dependencies) property:

import { createApp } from 'frint';
const MyApp = createApp({
name: 'MyAppName',
  providers: [
{
name: 'foo',
useValue: 'foo value here',
},
{
name: 'bar',
useFactory: function (deps) {
const { foo } = deps; // `foo value here`
        return foo + ', bar value here';
},
deps: ['foo'],
},
],
});

This is what we just did above:

  • Define foo provider
  • Define bar provider
  • For bar, list foo as a dependency
  • The function that generates bar value will now receive a deps object with all its dependencies keyed by their names. Since we have listed only foo as a dependency, it will receive foo only for now.
  • The generated value of bar now returns foo value here, baz value here.

You can try it yourself:

const app = new MyApp();
const foo = app.get('foo');
const bar = app.get('bar');
console.log(bar); // `foo value here, bar value here`

You can apply similar technique for useClass too. The deps object will then be given to the class as its first constructor argument:

class Baz {
constructor(deps) {
console.log(deps);
}
}

You can read further about it in the official docs for frint package here: https://frint.js.org/docs/packages/frint/.