{"id":22847592,"url":"https://github.com/vue-ioc/vue-ioc","last_synced_at":"2025-08-02T12:39:35.643Z","repository":{"id":16799781,"uuid":"38900236","full_name":"vue-ioc/vue-ioc","owner":"vue-ioc","description":"IoC and DI for Vue powered by InversifyJS and inspired by Angular Module syntactic sugar.","archived":false,"fork":false,"pushed_at":"2023-01-04T18:30:08.000Z","size":3070,"stargazers_count":45,"open_issues_count":44,"forks_count":3,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-07-27T11:15:17.320Z","etag":null,"topics":["dependency-injection","inversifyjs","inversion-of-control","ioc","typescript","vue"],"latest_commit_sha":null,"homepage":"https://codesandbox.io/s/k28n6rp36v","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vue-ioc.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-07-10T20:16:20.000Z","updated_at":"2025-06-14T17:22:51.000Z","dependencies_parsed_at":"2023-01-13T19:01:52.935Z","dependency_job_id":null,"html_url":"https://github.com/vue-ioc/vue-ioc","commit_stats":null,"previous_names":["gejgalis/vue-ioc"],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/vue-ioc/vue-ioc","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vue-ioc%2Fvue-ioc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vue-ioc%2Fvue-ioc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vue-ioc%2Fvue-ioc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vue-ioc%2Fvue-ioc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vue-ioc","download_url":"https://codeload.github.com/vue-ioc/vue-ioc/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vue-ioc%2Fvue-ioc/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268391588,"owners_count":24243295,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-02T02:00:12.353Z","response_time":74,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dependency-injection","inversifyjs","inversion-of-control","ioc","typescript","vue"],"created_at":"2024-12-13T04:01:24.941Z","updated_at":"2025-08-02T12:39:35.581Z","avatar_url":"https://github.com/vue-ioc.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"# vue-ioc\nIoC and DI for [Vue](https://vuejs.org/) powered by [InversifyJS](http://inversify.io/)\nand inspired by [Angular @Module](https://angular.io/guide/ngmodules) syntactic sugar.\n\n## Required Opinionated Stack\n \n * [Vue](https://vuejs.org) 2.x\n * [vue-class-component](https://github.com/vuejs/vue-class-component)\n * [TypeScript](https://www.typescriptlang.org/) with config flags: `experimentalDecorators: true` and `emitDecoratorMetadata: true`\n\n## Features\n\n 1. **Hierarchical IoC Container** defined in `@Module` by `providers` (using [InversifyJS](http://inversify.io/) under the hood). The hierarchy is bound to Vue components tree.\n 2. **Autostart** - instantiating top level services when container has been started (for background tasks similar to `@Effects`  from `ngrx`).\n 3. **@InjectReactive()** - makes injected dependency 'deeply reactive' in Vue template.\n 4. **Instance Handlers** - `@PostConstruct` and `@BeforeDestroy` - decorators for methods called when instance is created or destroyed by container.\n 5. **Custom Instance Handlers** ie. `@OnEvent('submitForm')`\n 6. **providedIn 'root' or 'self'** - select where will be bound @Injectable (friendly for tree shakeable singletons and lazy loaded @Modules) \n\n## Planned features (not ready yet)\n 7. **State Injectors** for [Vuex](https://vuex.vuejs.org/) and [MobX](https://mobx.js.org/).\n 8. **vue-cli** integration\n\n## Caveats / Limitations\n\n * `@Module` can be bound only to Vue component.\n * You can't use `@Inject()` for Vue component constructor arguments (because you can't define own constructor \n   using `vue-class-component`). Only Vue component class fields are supported.\n \n## Installation\n\n`vue-ioc` uses following `peerDependencies`:\n\n * `inversify`\n * `reflect-metadata`\n * `vue-class-component`\n * `vue`\n\n```bash\n\n# NPM\nnpm install @vue-ioc/core inversify reflect-metadata vue-class-component vue --save\n\n# Yarn\nyarn add @vue-ioc/core inversify reflect-metadata vue-class-component vue\n```\n\nYou must explicitly install `vue-ioc` via `Vue.use()` in your app main entrypoint:\n\n```typescript\n// main.ts\nimport Vue from 'vue'\nimport {VueIocPlugin} from '@vue-ioc/core' \n\nVue.use(VueIocPlugin)\n``` \n\n## Quick Start\n\n[![Edit vue-ioc basic example](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/k28n6rp36v?fontsize=14)\n\nCreate simple injectable service `HttpService`:\n\n```typescript\n// ./services/HttpService.ts\nimport {Injectable} from '@vue-ioc/core';\n\n@Injectable()\nexport class HttpService {\n    public get(url: string): Promise\u003cany\u003e {\n        return fetch(url).then(rs =\u003e rs.json());\n    }\n}\n```  \n\nAdd root container using `@Module` decorator at your top level App component and setup providers:\n\n```vue\n// App.vue\n\u003ctemplate\u003e\n    \u003cdiv\u003e\n        \u003cHelloWorld/\u003e\n    \u003c/div\u003e\n\u003c/template\u003e\n\u003cscript lang=\"ts\"\u003e\n    import Vue from 'vue'\n    import Component from 'vue-class-component'\n    import {Module} from '@vue-ioc/core';\n    import {HttpService} from './services/HttpService';\n    import HelloWorld from './components/HelloWorld.vue';\n    \n    @Module({\n        providers: [\n            HttpService\n        ]\n    })\n    @Component({\n      components: { HelloWorld }\n    })\n    export default class App extends Vue {\n    }\n\u003c/script\u003e\n```\nInject `HttpService` to `\u003cHelloWorld\u003e` component:\n```vue\n// ./components/HelloWorld.vue\n\u003ctemplate\u003e\n    \u003cdiv\u003e\n        \u003ch2 v-if=\"user\"\u003eHello {{user.firstName}} {{user.lastName}}!\u003c/h2\u003e\n    \u003c/div\u003e\n\u003c/template\u003e\n\u003cscript lang=\"ts\"\u003e\n    import Vue from 'vue'\n    import Component from 'vue-class-component'\n    import {Inject} from '@vue-ioc/core';\n    import {HttpService} from '../services/HttpService';\n\n    @Component()\n    export default class HelloWorld extends Vue {\n\n        @Inject()\n        public httpService: HttpService;\n        \n        public user = null;\n\n        public async created() {\n            this.user =  await this.httpService.get('./hello.json');\n        }\n    }\n\u003c/script\u003e\n``` \n\n## Providers\n\n```typescript\n@Module({\n    providers: [\n        // useClass\n        { provide: HttpService, useClass: HttpService },\n        \n        // useClass shortcut \n        HttpService,\n        \n        // useValue\n        { provide: HttpService, useValue: httpService },\n        \n        // useFactory\n        { provide: HttpService, useFactory: (injector) =\u003e /* ...injector.get(FooService)... */ }\n    ] \n})\n``` \n\n## providedIn\n\nDefault value: `'self'` Available values: `'root', 'self'`\n\n`@Injectable( { providedIn: 'root' } )` will bind service in root (App) container always as singleton.\n\nYou may also override this setting at `@Module` providers level in both directions:\n\n```typescript\n@Module({\n    providers: [\n        { provide: HttpService, useClass: HttpService, providedIn: 'root' },   // overrides @Injectable()\n        { provide: OtherService, useClass: OtherService, providedIn: 'self' }, // overrides @Injectable( {providedIn: 'root'} )\n    ] \n})\n```\n\n## Custom Instance Handlers\n\n`@PostConstruct()` and `@BeforeDestroy()` are two built in instance listeners. You may create custom instance handlers\nlike `@OnEvent('submitForm')` by creating a decorator using `createInstanceHandlerDecorator`\n\n1. Prepare the most basic EventBus implementation:\n```typescript\n// bus/EventBus.ts\nimport Vue from 'vue';\nimport {Injectable} from '@vue-ioc/core';\n\n@Injectable()\nexport class EventBus {\n\n    private bus: Vue = new Vue();\n\n    dispatch(name: string, data: any) {\n        this.bus.$emit(name, data);\n    }\n\n    addListener(name: string, listener: (data: any) =\u003e void) {\n        this.bus.$on(name, listener)\n    }\n\n    removeListener(name: string, listener: (data: any) =\u003e void) {\n        this.bus.$off(name, listener)\n    }\n}\n\n```\n\n2. Create `@OnEvent(name:string)` decorator\n\n```typescript\n// bus/OnEvent.ts\nimport {createInstanceHandlerDecorator} from '@vue-ioc/core';\nimport {EventBus} from './EventBus';\n\nexport function OnEvent(name: string) {\n    return createInstanceHandlerDecorator(({injector, instance, method}) =\u003e {\n        // attach handler - a place where listeners should be attached\n        const bus: EventBus = injector.get(EventBus); // you have access to injector where all services can be retrieved\n        const boundMethod = instance[method].bind(instance); // bound method to `this` of instance\n        bus.addListener(name, boundMethod);\n        return () =\u003e { \n            // detach handler - a place where all listeners should be detached\n            bus.removeListener(name, boundMethod);\n        };\n    });\n}\n```\n\n3. Dispatch event from view:\n\n```typescript\n// view/Form.vue\n\u003ctemplate\u003e\n    \u003cdiv\u003e\n        \u003cbutton @click=\"submitForm\"\u003eSubmit\u003c/button\u003e\n    \u003c/div\u003e\n\u003c/template\u003e\n\u003cscript lang=\"ts\"\u003e\n    import Vue from 'vue'\n    import Component from 'vue-class-component'\n    import {Inject} from '@vue-ioc/core';\n    import {EventBus} from '../bus/EventBus';\n\n    @Component()\n    export default class SomeForm extends Vue {\n\n        @Inject()\n        public bus!: EventBus;\n\n        public submitForm() {\n            this.bus.dispatch('submitForm', { firstName: 'John', lastName: 'Doe'})\n        }\n    }\n\u003c/script\u003e\n```\n4. Handle event in external action:\n\n```typescript\n// actions/SubmitForm.ts\nimport {OnEvent} from '../bus/OnEvent'\nimport {Injectable} from '@vue-ioc/core';\n\n@Injectable()\nexport class SubmitForm {\n  \n  @OnEvent('submitForm')\n  perform (data) {\n     // do something with data\n  }\n}\n```\n\n## Inversify Container Options\n\nvue-ioc uses following default options for creating Inversify containers: \n\n    {\n        autoBindInjectable: false,\n        defaultScope: 'Singleton',\n        skipBaseClassChecks: true,\n    }\n    \nTo override or add other options, please use `containerOptions` of plugin options:\n\n```typescript\nVue.use(VueIocPlugin, {\n  containerOptions: {\n    // options of Inversify container:\n    // https://github.com/inversify/InversifyJS/blob/master/wiki/container_api.md#container-options\n  }\n})\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvue-ioc%2Fvue-ioc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvue-ioc%2Fvue-ioc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvue-ioc%2Fvue-ioc/lists"}