According to the official document, it seems that we can use dependency injection feature as long as we stay on object-based structure.
Here's my question. I'm using TypeScript to achieve this goal (class-based). I'm going to use Inversify as an IoC container. My initial idea was something like:
DependencyConfig.ts:
import { Container } from "inversify";
import "reflect-metadata";
import Warrior from "./interfaces/Warrior";
import { Ninja } from "./models/Warrior";
let container = new Container();
container.bind<Warrior>(Symbol("Warrior")).to(Ninja);
export default container;
App.ts:
import container from "./DependencyConfig";
@Component({
name: "App",
provide: container
})
export default class App extends Vue {
}
When I checked in my browser's dev console, I was able to see the container has been set to the _provided field. Here's Hello.ts, the child component of App.ts:
Hello.ts:
@Component({
name: "Hello",
inject: [ "container" ]
})
export default class Hello extends Vue {
created (): void {
console.log(this);
}
}
As App.ts could access to Hello.ts through vue-router, it didn't register Hello.ts as a child component. I was expecting the injected container should appear on _injected or something similar. However, I couldn't find it. I changed the inject property value from "container" to { "container": Symbol("Container") } but I couldn't still find it.
Service Locator:
It works fine to use a service locator instead of the provide/inject pair:
// App.ts
@Component({
name: "App"
})
export default class App extends Vue {
}
// Hello.ts
import container from "./DependencyConfig";
@Component({
name: "Hello"
})
export default class Hello extends Vue {
created (): void {
var ninja = container.get<Ninja>(Symbol("Warrior"));
console.log(ninja.name);
}
}
However, I want to avoid using the service locator pattern here. Did I miss something while using the provide/inject pair for dependency injection?
I found a solution. If we use vue-class-component and vue-property-decorator, we can achieve this goal – using the provide/inject pair. Here's my code snippet:
// App.vue
<script lang="ts">
import Vue from "vue";
import Component from "vue-class-component";
import SERVICE_IDENTIFIER from "./models/Identifiers";
import container from "./configs/DependencyConfigs";
@Component({
name: "App",
// Provides IoC container at the top level of VueComponent
provide: {
[SERVICE_IDENTIFIER.CONTAINER]: container
}
})
export default class App extends Vue {
}
</script>
At the top-most Vue component, App.vue, we provide the container instance so that its all child component can consume it. Here's one of its child component, Ninja.vue:
// Ninja.vue
<script lang="ts">
import Vue from "vue";
// Imports both Component and Inject decorators from vue-property-decorator,
// instead of vue-class-component
import { Component, Inject } from "vue-property-decorator";
import { Container } from "inversify";
import SERVICE_IDENTIFIER from "../models/Identifiers";
import { Ninja as _Ninja } from "../models/Warrior";
@Component({
name: "Ninja"
})
export default class Ninja extends Vue {
public warrior: string;
public weapon: string;
// IoC container provided from App.ts is injected here
@Inject(SERVICE_IDENTIFIER.CONTAINER)
private _container: Container;
private _ninja: _Ninja;
created (): void {
this._ninja = this._container.get<_Ninja>(SERVICE_IDENTIFIER.WARRIOR);
this.warrior = this._ninja.name;
this.weapon = this._ninja.weapon.name;
}
}
</script>
The child component uses the @Inject(Symbol) decorator to resolve injected container instance from App.vue.
I wrote a blog post about this in both English and Korean about this.
HTH
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With