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