0

I want to create a service in which an object of angular components are stored. That service basically would have a an property components which would look like {key: componentClass}.

It seems pretty simple to implement and it actually is, but I have faced one problem and I don't know how to solve it.

Lets call service ComponentStoreService. As an example it could look like this:

@Injectable()
export class ComponentStoreService {
 components: { [key: string]: any; } = {};

   register(key: string, components: any): void {
     this.components[key] = components;
   }

   getByKey(key: string): any {
     return this.components[key];
   }

   getAll(): any {
     return this.components;
   }
}

and now lets create 2 demo components and store them.:

const providers = ReflectiveInjector.resolve([ComponentStoreService]);
const injector = ReflectiveInjector.fromResolvedProviders(providers);
const componentStore= injector.get(ComponentStoreService );

@Component({
  selector: 'component-1',
  template: `<h2>component-1</h2>`
})
export class D1Component {constructor() {}}

componentStore.register('component-1', D1Component );

console.log('component-1 called', componentStore.getAll())

2nd one:

    const providers = ReflectiveInjector.resolve([ComponentStoreService]);
    const injector = ReflectiveInjector.fromResolvedProviders(providers);
    const componentStore= injector.get(ComponentStoreService );

    @Component({
      selector: 'component-2',
      template: `<h2>component-2</h2>`
    })
    export class D2Component {constructor() {}}

    componentStore.register('component-2', D2Component );

    console.log('component-2 called', componentStore.getAll())

And as a result first console.log prints object in which is first component added. Which is ok. And second console.log prints only second component. Which is incorrect.

As far as I understood in each component ComponentStoreService is separate instance. How I could make it singleton in whole app? I'm registering ComponentStoreService only in the app level. It means that the problem is elsewhere. Maybe it's because I'm calling service outside component class.

kuldarim
  • 1,096
  • 8
  • 21
  • 44
  • What you get is correct for this code: you create two injectors, so you get separate instances of what registered with them. The real question is - what is the goal you are trying to achieve by this? It doesn't seem right. In my opinion one shouldn't need such a mechanics. – Alexander Leonov Jul 21 '16 at 12:35
  • I need to implement a functionality which enables to show only those components by `componentId` which are stored in `config.json`. So I thought maybe it is possible to have an object with `{ key: componentClass }` in which all components are stored. It would enable possibility to retrieve components by key and render only those components which `Ids` are stored in `config.json`. – kuldarim Jul 21 '16 at 20:25
  • you must understand that this technology has limitations, for example this way you won't be able to manage pipes used in component's templates, and you won't be able to use those components themselves in templates. Apart from that it is possible to achieve what you want, but it will take me some time to prepare the answer for you. – Alexander Leonov Jul 22 '16 at 12:02
  • I don't know about the pipes, but about using component in template You are wrong. You can dynamically render a component using ComponentResolver. Example is here http://stackoverflow.com/questions/36325212/angular-2-dynamic-tabs-with-user-click-chosen-components/36325468#36325468 – kuldarim Jul 25 '16 at 06:27

1 Answers1

1

Your ComponentStoreService is global service.Because you use the service in multiple components.You can inject it 1 time when the application loaded.So you can use it in everywhere.

bootstrap(AppComponent, [ComponentStoreService]);

component 1:

    @Component({
      selector: 'component-1',
      template: `<h2>component-1</h2>`
    })
    export class D1Component {constructor(private _componentStoreService:ComponentStoreService) {}}

    componentStore.register('component-1', D1Component );

    console.log('component-1 called', this._componentStoreService.getAll())

component2

        @Component({
          selector: 'component-2',
          template: `<h2>component-2</h2>`
        })
        export class D2Component {constructor(private _componentStoreService:ComponentStoreService) {}}

        componentStore.register('component-2', D2Component );

        console.log('component-2 called', this._componentStoreService.getAll())

you can use providers for inject ComponentStoreService .

Yasemin çidem
  • 623
  • 8
  • 17
  • 1
    You should be aware that this way every component gets its own `ComponentStoreService`. If you want to share the service you need to provide it on a shared parent - for example `AppComponent`. – Günter Zöchbauer Jul 21 '16 at 13:30
  • Yes, just use `ComponentStoreService` in `providers` property only of root component and inject it in each component that you want to register. http://stackoverflow.com/questions/34929665/angularjs-2-multiple-instance-of-service-created?rq=1 – Oleg Barinov Jul 21 '16 at 13:41
  • I also usually tend to suggest to provide it at `bootstrap()` because it makes it more obvious that it is for the whole application, but the Angular2 style guide suggests using the providers array of the root component `AppComponent`. There isn't much difference in behavior except that for example the current router **requires** to be provided at `bootstrap()` – Günter Zöchbauer Jul 21 '16 at 13:55
  • But I need to call service outside the component class. It means that I cannot use a provider inside the component. I was trying to use `ReflectiveInjector` because I wanted to get service instance outside the component. This is needed because otherwise the service method won't be called at the compilation time and component object won't be stored in service. – kuldarim Jul 21 '16 at 20:20