Angular: Entry Components of a Lazy Loaded NgModule are not available outside the Module

Created on 6 Feb 2017  ·  122Comments  ·  Source: angular/angular

I'm submitting a ... (check one with "x")

[ ] bug report => search github for a similar issue or PR before submitting
[X] feature request
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Follow-up issue for #12275 as requested by @DzmitryShylovich

Current behavior
entryComponents of a lazy loaded NgModule cannot be rendered using ComponentFactoryResolver. Error message: No component factory found for {{entryComponent}}

Expected behavior
entryComponents should be available just like if the module is imported

Minimal reproduction of the problem with instructions
http://plnkr.co/edit/9euisYeSNbEFrfzOhhzf?p=preview

I created a simple setup, similar to what we use internally. Main component provides a method to render a Type. EntryComponent is declared as entryComponent in Page1Module. However after having loaded Page1Module, when trying to render EntryComponent via ComponentFactoryResolver, No component factory found for EntryComponent is thrown.

What is the motivation / use case for changing the behavior?
Render entryComponents of child modules at root level. Use cases for this approach are

  • Modals that shall be rendered on top of everything
  • Notifications
  • etc.

Please tell us about your environment:
We're using currently Angular 2.1.1 but this affects the latest version of Angular (2.4.6) as well (see plnkr).

  • Language: TypeScript ^2.0.0
core feature

Most helpful comment

Ok, in this case I'm changing this issue into a feature request.

IMO these use cases should be handled by Angular itself rather than by moving things along the DOM manually.

All 122 comments

After some investigation I figured out that in your in works as designed.
Entry components follow the same rules as providers in lazy loaded modules. https://angular.io/docs/ts/latest/cookbook/ngmodule-faq.html#!#q-lazy-loaded-module-provider-visibility
They are only visible inside lazy loaded module.

Ok, if this is the intended behavior, how can specific tasks like the use cases above be accomplished?

I don't think that blowing up the main module is a proper solution.

If you want to use an entry component in MainComponent then you should provide it in AppModule

So your solution indeed is to move everything into the main module?

IMO this totally breaks the module concept of Angular which should allow to group functionality blocks into one module.

This means basically that things like modals are impossible to use with a lazy load architecture.

You need to use an api similar to material's https://material.angular.io/components/component/dialog
Then it should work

Wow, so your opinion is that we should rather move DOM content, like Angular Material does. See here: https://github.com/angular/material2/blob/master/src/lib/core/portal/dom-portal-host.ts#L30-L55

If this is really the best practice to handle this in Angular, basically we can dump Angular and start using JQuery again.

Sorry, but I cannot take this approach seriously.

I don't know what is the best approach for modals and lazy loading, but anyway in your plunkr it works as intended and there's no angular bug here.
For discussion about modals it's better to use support channels such as gitter and stackoverflow

Ok, in this case I'm changing this issue into a feature request.

IMO these use cases should be handled by Angular itself rather than by moving things along the DOM manually.

Faced with same issue today - component for modal defined in lazy loaded module does not handled by app modal service (it cant find it despite entryComponents usage). Forced to move it to main bundle that breaks lazy modules structure and usage logic :confused:

Same issue there ... Impossible to use an entryComponents in a lazyLoaded module without breaking the lazy modules logic : My LazyLoaded module depends on an entryComponent declared in my AppModule :(

Ran into this same issue, I did come to a resolution though after looking through ng-bootstrap modal source code. Essentially the issue (from my understanding please correct me if I'm wrong) is that your lazy modules are not included in the initial injector when your app is bootstrapped and the initial injector cannot be modified once it is set. Which is why you get the error in the OP. When one of your lazy modules is loaded it gets its own injector with references to whatever you declare or provide in your lazy module. That being said, as long as wherever you create your dynamic component at has reference to the correct injectorand componentFactoryResolveryou can indeed reference an entryComponent outside of the lazy loaded module.

So, what I did was create a singleton service to store the injector and componentFactoryResolver of the dynamic component I want to create. This service needs to be outside of your lazy modules. Then whenever I go to dynamically create a component I can call this service to get what I need.

Code below will be wherever you are creating your dynamic component outside of your lazy module

@ViewChild('parent', {read: ViewContainerRef}) parent: ViewContainerRef;
  private componentRef: ComponentRef<any>;
...
const childComponent = this.componentFactoryResolver.resolveComponentFactory(yourComponentRef);
this.refInjector = ReflectiveInjector.resolveAndCreate([{provide: yourComponentRef, useValue: yourComponentRef}], this.injector);
this.componentRef = this.parent.createComponent(childComponent, 0, this.refInjector);

```html

Then in your parent component for your entryComponent you'll inject `injector` and `componentFactoryResolver` and set their values in the shared service:
```js
constructor(private componentFactoryResolver: ComponentFactoryResolver, private injector: Injector) {}

Let me know if this doesn't make any sense and I can elaborate further. Edit I forgot to mention I'm using Angular 2.4.9.

Thanks @jmcclanahan. Are you able to tell us a bit more about what that service looks like and how it is set up? I am struggling with a similar issue, trying ti dynamically load a component from one lazy loaded module into a component from another lazy-loaded module and even though (I think) I have added the entry components in the right place, I get told I haven't. Thanks

@gcorgnet - Sure. So my use case was, I have a common toolbar on every page of my app, but within the toolbar I wanted to add buttons and tools for additional functionality based on what page I was on. In order to achieve this I created a component to hold these buttons/tools and their logic and I wanted to keep these components inside of the module they are associated with. So, essentially nothing outside of my module knows anything about these specific toolbar functions while still being able to show them on the common toolbar. Below is my working solution which I hope will help you:

All I'm doing with the toolbar service is creating an observable, so your lazy component can pass it's componentFactoryResolver and injector references to the toolbar common component that is listening for the receiveContext Event.

toolbar.service.ts

@Injectable()
export class ToolbarService {
    contextReceivedSource = new Subject<any>();
    contextReceived$ = this.contextReceivedSource.asObservable();

    receiveContext(componentFactoryResolver: ComponentFactoryResolver, injector: Injector) {
        this.contextReceivedSource.next({ resolver: componentFactoryResolver, injector: injector });
    }
}

In your lazy loaded component you'll want to inject componentFactoryResolver and injector and fire an event in the toolbar service.

lazy loaded toolbar component

...
constructor(private componentFactoryResolver: ComponentFactoryResolver, private injector: Injector) {}
...
ngOnInit() {
  this.toolbarService.receiveContext(this.componentFactoryResolver, this.injector);
}
...

Finally, in my core toolbar component I'm subscribing to the observable in the toolbar service so that anytime an event is fired it will try to inject and create the lazy component that I need. It's also important to destroy the componentRef that you create otherwise you will end up with memory leaks.

...
@ViewChild('toolbarTarget', {read: ViewContainerRef}) toolbarTarget: ViewContainerRef;
component: Type<Component>;
refInjector: ReflectiveInjector;
resolverSub: Subscription;
refInjector: ReflectiveInjector;
componentFactoryResolver: ComponentFactoryResolver;
injector: Injector;

ngOnInit() {
  this.resolverSub = this.toolbarService.contextReceived$.subscribe(resolver => {
      this.componentFactoryResolver = resolver.resolver;
      this.injector = resolver.injector;
    });
}

updateToolbar(data: any) {
    this.clearComponent();
    this.component = data['toolbar'];
    if (this.component) {
      const childComponent = this.componentFactoryResolver.resolveComponentFactory(this.component);
      this.refInjector = ReflectiveInjector
          .resolveAndCreate([{provide: this.component, useValue: this.component}], this.injector);
      this.componentRef = this.toolbarTarget.createComponent(childComponent, 0, this.refInjector);
    }
  }

  clearComponent() {
    this.toolbarTarget.clear();
    if (this.componentRef) { this.componentRef.destroy(); };
  }

  ngOnDestroy() {
    this.resolverSub.unsubscribe();
    this.clearComponent();
  }

Your dynamically created component will then be placed wherever you put the corresponding #toolbarTarget

<div #toolbarTarget></div>

As an aside, I'm using this line this.component = data['toolbar']; in the common toolbar component to get the lazy loaded component reference from the route. If you want to see the full code for this I can post that, but it's outside the scope of this discussion.

{ path: '', component: yourComponent, data: { toolbar: yourToolbarComponentToInject } }

Let me know if you have any further questions!

@jmcclanahan I have one question (probably a stupid one) about your code, when you mention the "lazy loaded toolbar component", you put the service call code in the ngOnInit section, but how do you manage to initialize it (the component) without having to add it to the HTML code and hide it later in the DOM? Since that component is only going to be used in the topbar, I don't load it anywhere else before.

@Dunos - I realized I completely left that part out of my example above, sorry about that! You'll want to add the dynamic component as an entryComponent in the lazy loaded module it is associated with.

Then in the toolbar component itself you'll define a ViewContainerRef that will reference the location in your html template where you want the dynamic component to display. Then the updateToolbar() method will take care of creating your component, placing it in the specified location and showing the component on the toolbar.

@ViewChild('toolbarTarget', {read: ViewContainerRef}) toolbarTarget: ViewContainerRef;
...
updateToolbar(data: any) {
    this.clearComponent();
    this.component = data['toolbar'];
    if (this.component) {
      const childComponent = this.componentFactoryResolver.resolveComponentFactory(this.component);
      this.refInjector = ReflectiveInjector
          .resolveAndCreate([{provide: this.component, useValue: this.component}], this.injector);
      this.componentRef = this.toolbarTarget.createComponent(childComponent, 0, this.refInjector);
    }
  }

Your dynamically created component will then be placed wherever you put the corresponding #toolbarTarget

<div #toolbarTarget></div>

The clearComponent() method gets called whenever ngOnDestroy() is run, which takes care of hiding the component when you leave the page.

clearComponent() {
    this.toolbarTarget.clear();
    if (this.componentRef) { this.componentRef.destroy(); };
  }

  ngOnDestroy() {
    this.resolverSub.unsubscribe();
    this.clearComponent();
  }

I've also updated my original post above. I've been getting a lot of questions about this, so I'll add a working example using this dynamic toolbar as soon as I can and update you guys with a link.

@jmcclanahan that I get (I think), but I still don't understand how the call to the service is make from the dynamic component (the this.toolbarService.receiveContext call part) since the ngOnInit never gets called (or I don't see how to do so). I added to the entryComponents but need to initialize it somewhat to get the onInit to work.

@Dunos - Maybe an example will be best. Say I'm navigating from the index page of my app to Page A. You will have a corresponding route for Page A, that will define Page A's module/component. Page A will also have a separate component defined that I want to display in the global toolbar. So, inside Page A's main component you will inject ComponentFactoryResolver and Injector and then fire a receiveContext event. This bit of code:

constructor(private componentFactoryResolver: ComponentFactoryResolver, private injector: Injector) {}
...
ngOnInit() {
  this.toolbarService.receiveContext(this.componentFactoryResolver, this.injector);
}

Sorry, I could see how that could be confusing. You don't put that code in the component you want to display in the toolbar, but rather the component associated to the page you are on.

@jmcclanahan Ah perfect, many thanks!!! Got it working now :)

@jmcclanahan I just solved the same issue before finding this post. One thing I noticed is if you want to save a few characters in your function declarations, you can get a reference to the ComponentFactoryResolver using the Injector that you pass. you can just do:
injector.get(ComponentFactoryResolver);

And that should return the reference you need. But in the end it gives the same result as passing the ComponentFactoryResolver in as a parameter in the function as well.

This solves my issue as well.

In sum, all you need to know is that when you want to dynamically create a component that's declared in a lazy module, you need to actually use ComponentFactoryResolver from a component that's declared in that lazy module. The root injector (which contains the root ComponentFactoryResolver) will not know about any entryComponents declared in a loaded lazyModule.

Looks kinda weird, but this is how Angular is designed (which makes sense) but this actually creates extra complexity when working with the framework, imho.

How Angular team could leverage this: Whenever a route is activated, the root injector should append the entryComponents listed on that lazyModule automatically and whenever the route is changed, the previously appended entryComponents should be automatically destroyed, this would ensure less complexity and avoid over flooding the root injector with extra component factories.

I'll submit a feature request for this.

Thank you!

Many thanks @jmcclanahan

@jmcclanahan thanks a lot!! @renatoaraujoc have you submitted this feature to Angular team?

@rraya I have submitted a feature request regarding this "issue" (it's not really an issue if you think about it), please see #17168.

Just to add to this. My use case is that I have created a ModalManagerService in my AppModule which has an open method that all other parts of the app call to open a modal. It gets passed a component which is then opened as a modal. I also have a closeAll method which is used when the modals need to be closed by some action other than the user closing them e.g. route change due to session timeout.

@jmcclanahan but I still not understanding yours,many thanks if there are some suggestions.it seems some error occured at 'this.componentFactoryResolver.resolveComponentFactory'

@Dunos can u give me a hand?I suffered the same issure for weeks,and its really a headache.

@werts,

All you need to understand is whenever you navigate to a lazy loaded route, the activated route will spawn a child injector which contains the entryComponents for that lazyModule. If you have a global modalService defined in the AppModule (which is the application's root injector), the componentFactoryResolver will not know any new entryComponents from any lazyModule. So, one of the ways to solve this issue is to get the instance of complementFactoryResolver from a lazyModule's component and somehow tell the modalService "hey, use this injector/componentFactoryResolver to render the wanted entryComponent".

Seems a bit hard to understand but it's not.

@renatoaraujoc thanks for your explain,in my use case, I want to load component from a link,thats to say,i click the menu from the left sidebar,and i will retrieve module from a lazyloaded module,and then ,i will create a dynamic component,because i want to build a tab-like app,so, in the right main content area,there are some component content, if i click the tab trigger,the content will show.or is there any solution building tab-like app via angular2/4?

anyway,the lazyload component can never be init ,how can i tell the sevice its componentFactoryResolver?

for my app, there is a AppModule,and it contains CoreModule, so, my ContentTabModule(used to load tabset)belongs to CoreModule,,all creating component handled by ContentTabModule,it has service,component,directive,pipe,when click a menu link, the ContentTabService will create a dynamic component from menu params.i have seen some trunk(i use webpack)loaded at network panel.and then i want to create component that demonstated at the lazyload module.

@renatoaraujoc how can i get the instance of complementFactoryResolver from a lazyModule's component?i think its much more difficult, because the lazymodule's component never be init(or i'm wrong)

@werts check the conversation I had with @jmcclanahan above, we talked about something similar with a toolbar.

@Dunos i cant understand the example from @jmcclanahan , i think i suffer the same.the lazyload component never init.
this.toolbarService.receiveContext() called from lazyload module?

@werts you have to load the main component of the lazy module with the routes, and that component is the one that calls the aux service with its Injector and ComponentFactoryResolver.

@Dunos thanks, can u give me a hand if its convenient for you, some other use case,i want to lazyload a module from link click,and then create dynamic component.is it possible?

@werts ... yes, it is possible. Look at https://github.com/Toxicable/module-loading for the starting point.

ok,try it now,thx. @mlc-mlapis

@werts et al: Can I recommend you take this support thread to another channel? StackOverflow or Gitter might be more suitable. As it is, I am subscribed to this issue (as I am sure are many others) and it is generating a lot of noise right now which is getting a little off-topic.

On the plus side: great to see such a helpful community!

I can't believe that this issue is also another road block I have to face with lazy loaded modules. I've been desperately looking for a solution to keep my lazy module's children anonymous from it's parent (app.module) so that I can easily remove/export the lazy module if needed. For something as simple as an app wide <router-outlet name="app-popup"> in app.component.html (which I've given up on in hopes that ComponentFactory can replace it's job) just to load lazy children components has been very daunting. Only to now come across the entry-components issue with the lazy-modules entry-children. It's just not possible to keep my modules isolated properly if I need the root module to register lazy children components. I'm just hoping the Angular team realizes this as an issue soon and resolves it. I'm still waiting for them to respond to my issue with an app-wide router-outlet for lazy-modules. https://github.com/angular/angular/issues/19520

In the beginning, there was nothing,
'Let's create a view! ', says jQuery team,
'Let's have a list! ', says Angular team.

@jmcclanahan we got ngComponentOutlet now, it makes things much simpler.

<ng-container  *ngComponentOutlet="yourComponentClass;injector: yourInjector;"></ng-container>

@crysislinux ngComponentOutlet is great but it does not allow us to pass data to the component yet so it's not a solution.

Any news about this feature? =/

I just came across this issue. Tried a couple of the suggestions, without success. I got it working, though. It actually turned out that my lazily loaded module was missing an import of another dependant module -- in this case the NGX modal module -- but the error accused my component of not being resolvable.

I'm curious: Is this problem solved by the use of ui-router? Or maybe even angular's own router multiple outlets capability? Do they do this logic behind the scenes? This is a very common case for me in angular 1.x and I am trying to transition that to angular 2+

In the toolbar example, the ui-view (probably a named view, or router outlet in case of angular router), would be setup in a higher level module. If a lazy loaded module state replaces the toolbar ui-view with it's own content, it would have to just "work" or ui-router would not function in a lazy loaded environment where higher level views outside of the current module need to be replaced. That would be extremely limiting compared to angular 1.x.

The above example seems like it is solving the same thing that ui-router/angular router are designed to solve. Am I missing something?

... we are probably lost what is the problem at all. As we know and use each day there is no problem to use components from lazy loaded modules ... even via router or programmatically by our own code.

When using @jmcclanahan example above it appears to work well for my use case.

I had to add this.componentRef.changeDetectorRef.markForCheck(); after this.componentRef = this.toolbarTarget.createComponent(childComponent, 0, this.refInjector);.

This fixed an issue I had with HostBinding properties not triggering.

I figured I would take a swing at a potential solution that has worked for me. This is a fork of the original plunkr.
http://plnkr.co/edit/x3fdwqrFDQr2og0p6gwr?p=preview

To work around this you need to resolve the component factory from a service (ResolverService) provided in the module that has the entryComponent (Page1Module). This is then passed to a second service (ChangerService) which uses the resolved factory and adds the component to the view on the MainComponent.

What I seriously don't understand is this god damn router. Check this plunker (forked from @patkoeller 's example)
http://plnkr.co/edit/keMuCdU9BwBDNcaMUAEU?p=preview @ src/page1.ts

What I basically want to do is inject some component into the toplevel app and use [routerLink] directive with commands relative to its defining lazy loaded module like: [routerLink]="['./', 'sub', 'component'] to generate /page-1/sub/component. If I pass an injector to createComponent with some ActivatedRoute and it works for my case it screws up every other routerLink, but if I reassign router.routerState.snapshot to the state from the resolve and pass the injector with this router instance it seams to work as intended... very confusing!

Should I file a bug?

I am facing the same issue with entry components for my dialog singleton service. Feature related modal components should remain in their respective module without the need of @jmcclanahan's workaround.

Any new ETA on this? Or at least a response from Angular team? Not having this functionality is a real downer since module encapsulation just got thrown out the window. All the dialogs for @angular/material need to be imported in root module.

@xban1x I don't think we have any progress with that. We have to do the walkaround of it :(

@jmcclanahan is it possible to get a reference of injected component using your approach? I have successfully injected a component, it is a tree btw, so now I need to subscribe on tree click events in component, that injected the tree. Is there a way to accomplish this?

In the issue #23819 I asked about making the entry components behave in a similar way as the services with providesIn: 'root'. Closed that issue because it seems both issues are intended to solve the same thing. I hope this issue will be solved soon because this is an important feature :)

Update

From what I see in the non-minified code, when providesIn is defined in the service, it register the factory so that it can already be used (without the need of importing it in a module), if I understood correctly:

var Injectable = makeDecorator('Injectable', undefined, undefined, undefined, function (injectableType, options) {
    if (options && options.providedIn !== undefined &&
        injectableType.ngInjectableDef === undefined) {
        injectableType.ngInjectableDef = defineInjectable({
            providedIn: options.providedIn,
            factory: convertInjectableProviderToFactory(injectableType, options)
        });
    }
});

I assume there is a way to apply a similar logic in a component with some option like @Component({ ... entryComponent: true }) so that when the chunk that contains it is loaded, the factory is defined in the resolver (there should be a module that includes the component in entryComponents with the component dependencies, otherwise it would cause a compile time error).

@lucasbasquerotto ... we use the following logic to load a module and use its component types (with our own mapping between string keys <-> component types stored directly in that module):

this._loader.load(url)
    .then((_ngModuleFactory: NgModuleFactory<any>) => {
        const _ngModuleRef: NgModuleRef<any> = _ngModuleFactory.create(this._injector);
        const _cmpType: Type<any> = _ngModuleRef.instance.cmps.get('dynamic');
        const _cmpFactory: ComponentFactory<any> = _ngModuleRef
            .componentFactoryResolver
            .resolveComponentFactory(_cmpType);
        this._cmpRef = this.vc.createComponent(_cmpFactory, 0, this._injector, []);
    });

Hello guys, for now I'm also using the workaround of having a singleton class that maintains a reference to the several lazy-loaded module resolvers.
Is there any better way to handle this with the new version of Angular ?
Thanks

Hey everyone,

Just facing the same problem and I use the workaround as a bootstrap for my solution.

A way to go further on this and reduce the need to have a global service, would be to expose the loadedConfig private variable of RouteConfigLoadEnd event. This private variable of type LoadedRouterConfig contains the loaded module, its injector and its componentFactoryResolver.

Should I open a new issue to propose this as a feature request?
--------- After edit

Nevermind my question, I found this feature request https://github.com/angular/angular/issues/24069

I think I have the same issue, but my extra module is not lazy loaded. I have to move my entryModules from SharedFeatureModule (which is loaded only if is needed) to CoreModule.
Now it works. But I moved feature related component to core which is not ok in my opinion.

This issue is still not resolved?

@jmcclanahan 's solution

{ path: '', component: yourComponent, data: { toolbar: yourToolbarComponentToInject } }

Adds an extra blank route to the end. When I try to create a dynamic breadcrumb, this is becoming a problem.

Without adding this blank route defaults to the component, I prefer to have something similar to bootstrap in lazy child modules.

Any updates on this?

Our custom solution involves constructor-injecting the ComponentFactoryResolver and Injector into each component that could open our global sidebar. The calling component then passes those references (its own) to a service along with the desired Component. The service uses those instances to create the component. Over-complicated and over-verbose.

Just put your entry components into their own submodule and import that into the app and nowhere else.

@broweratcognitecdotcom I think the point of this issue is for entryComponents in lazy loaded modules. Including all submodules of every component you want to use as an entry component in AppModule will force all such components be loaded eagerly, which could cause performance issues at the startup.

Not to say that those components should be agnostic to the components that use them (the consumers), in such a way that their modules shouldn't know if they are being used as entry components or normal components (declared in the html), only its consumer should know (so, putting them in AppModule would make it hard to know why they are there, you would need to search in other places to know).

@lucasbasquerotto ... load such lazy modules programmatically ... you have full control then, and it just works.

I stumbled upon this thread after encountering a weird issue where I could not get the component resolved from the root resolver by using a map of string keys and corresponding components. I can literally see the component in the _factories Map but somehow it doesn't compare correctly and returns undefined.

@jmcclanahan @Dunos Don't you think instead of the feature module handing over its injector reference (registering its context with the main app module) it will be better if it exposes a service say FactoryResolver that will actually return the correctly resolved factory for its components as it will have correct reference to its own module's injector. This way it will be like exposing a public api for your components.

I am trying to do something similar, but my module is not lazy loaded and I am bumping into the issue I described above. In theory, it should work, unless I am missing something obvious here?

@h-arora I noticed the same issue, seeing the comp. in _factories, but the issue was that the module had a build error actually, which didn't show up

Coping over from #17168 (closed as a duplicate of this one)

Current behavior
If you have a singleton service that uses a ComponentFactoryResolver provided by the App's RootInjector to dynamically create components and you wish to create an entryComponent declared in a lazy module, this will fail because the root injector does not know about it.

Expected behavior
This is actually the expect behavior considering that the root injector will not know about entryComponents known by the child injector.

What is the motivation / use case for changing the behavior?
The motivation is to leverage the complexity to create dynamic components though the modular design of Angular is correct.

Angular version: 2.4.9

Proposed solution: Even though this goes against the modular design adopted by Angular itself, I think that the complexity provided to create dynamic components could be softened by simply appending the LazyModule's entryComponents to the RootInjector's entryComponents and to avoid flooding the RootInjector, whenever you navigate out (which destroys the LazyModule), the previously injected entryComponents would be erased from the array.

Simply talking:

Page is loaded -> RootInjector is created.
Issues a navigation which activates a LazyModule -> ChildInjector is created.
RootInjector.entryComponents.append(childInjector.entryComponents)
Issues a navigation to somewhere else -> LazyModule is no longer necessary, thus destroyed.
RootInjector.entryComponents.destroyLastAppendedEntryComponents()
Hope I could suggest something good.

I spent a couple of days breaking apart my companies project and implementing lazy loading. It wasn't until the end that I realized our modals weren't working due to being entries in their respective modules. We've reverted back and are waiting on a fix...

Is this something that's scheduled or should we explore a workaround. As people are doing above.

@ravtakhar you probably want to look at the workaround as this is in the Backlog and doesn't have a priority assigned at this time.

I spent a couple of days breaking apart my companies project and implementing lazy loading. It wasn't until the end that I realized our modals weren't working due to being entries in their respective modules. We've reverted back and are waiting on a fix...

Is this something that's scheduled or should we explore a workaround. As people are doing above.

Same problem HERE

@tmirun ... if you load such lazy modules programmatically ... you have the full control then, ... it means that you can get a module reference finally and call moduleRef.componentFactoryResolver.resolveComponentFactory(entryComponentType)

Had the same issue as many above, and solved by creating a standalone module declaring and exporting all entryComponents, including that in the appModule and exporting it as well, so that every single lazyLoaded module will load these components.

The only other feasible solution I've found was also spawning dialogs by providing a custom ComponentFactoryResolver according to the module hosting the entryComponent.

BEWARE: remember that singleton modules provided in root will only see the main module entry components, that's why I've decided to come up with the first solution, otherwise you may think about making a dialog spawner for each submodule of your application and, in that submodule, eventually include shared modules that have other entryComponents, by providing to the service spawning the modals the ComponentFactoryResolver of said modules.

... the general supported solution should be available (some new APIs + support in Ivy) ... due to Jason's presentation http://www.youtube.com/watch?v=2wMQTxtpvoY&t=131m24s ... in Angular 7.2 ... but I am not sure if the version 7.2 was mentioned correctly.

I have the same issue with CDK Overlay, I have a gallery plugin that is built on top of the CDK overlay, but because of this issue I have to import it in the root module.

BTW, is there any workaround for my case?

@MurhafSousli ... lol, I have to say again that it's by design. The thing was explained a hundred times ... there is one root injector and any lazy loaded module is one level deep so you can't suppose that you are able to touch its components from root level. But as it was said many times you can load that module programmatically ... so you will get that module reference ... and you can reach its components then.

Add me to the list too 😄. I have modal component where i load my components dynamically and i don't want to load all of them in app that would be bad design as they are not part of app component. But that's what I have to do i guess after reading all this.

@msarica pointed out to me today that if you create a service that is lazily loaded and have it extend your global service that opens popups, then use the lazily loaded service to open the popup, it will work as expected:

Global PopupService

@Injectable({ providedIn: 'root' })
export class PopupService {
  constructor(
    private injector: Injector,
    private overlay: Overlay,
  ) {}

  open<T = any>(component: ComponentType<T>) { ... }
}

Specific Service only lazily loaded in MyFeatureModule

@Injectable()
export class MyFeaturePopupService extends PopupService {}

Usage in a component that is part of the lazily loaded MyFeatureModule

@Component({ ... })
export class MyFeatureComponent {
  constructor(
    private myFeaturePopupService: MyFeaturePopupService,
  ) {}

  openPopup() {
    this.myFeaturePopupService.open(MyFeaturePopupComponent);
  }
}

Example definition of lazily loaded module

@NgModule({
  imports: [MyFeatureRoutingModule],
  declarations: [
    MyFeatureComponent,
    MyFeaturePopupComponent,
  ],
  entryComponents: [MyFeaturePopupComponent],
  providers: [MyFeaturePopupService],
})
export class MyFeatureModule {}

Tested in Angular 7.

Note: Our code leverages the Angular CDK's Overlay, but the concept is the same (according to other parts of this thread, it's the Injector that is of concern).

... the general supported solution should be available (some new APIs + support in Ivy) ... in Angular 7.2

... @mlc-mlapis could you expand on what the general supported solution would be ?

@cedvdb ... I just mentioned the important moment in that presentation which actually looks like a solution ... it means the supported ability to programmatically load a lazy loaded module and then use any part of it, like a component, a directive, ...

That way is available even today ... but with Angular CLI you always need to define necessary routes ... even if you never use them via the router.

Wow. This was very unexpected.

I implemented my own dialog system based on @angular/cdk portals and overlays: https://stackblitz.com/edit/cdk-dialog-example-p1. Got this issue as well.

The workaround mentioned by @CWSpear works: I had to extend the service in my feature module and that worked for me. How weird!

Is there a more elegant solution for this?

Is there a more elegant solution for this?

Something I have been experimenting with is to re-provide a launcher wrapper at the lazy module level. Have this wrapper take an injector in the constructor, and pass that along to the real launcher (which gets injected from the root). Its a one line fix. I'm not sure how stable it is, though,

@NgModule({
  declarations: [LazyRootComponent, LazyEntryComponent],
  entryComponents:[LazyEntryComponent],
  providers:[LauncherWrapper],  //<---- reprovision here 
  imports: [
    CommonModule,
    LazyModRoutingModule
  ]
})
export class LazyModModule { }
@Injectable()
export class LauncherWrapper {
    constructor(private injector: Injector,  private launcher:ActualLaucherClass) {
    }

My issue was caused because I used @Injectable({providedIn: 'root'}). Changing it to @Injectable({providedIn: MyModule}) solved it. 😄

This makes sense, because the service needs access to the lazy-loaded modules entry components. When provided in the root, it cannot access them because the module is temporarily disconnected from the root injector.

Many thanks to @jmcclanahan and @dirkluijk , I was scratching my head for hours and your description helped to find a solution for my case.

I just wondering what is the right way to do it.
It is needed @angular/cdk ? Why ?

I don't think any workaround is viable if it requires the code you're integrating with to know about it, because there's plenty of 3rd party code that just uses ComponentFactoryResolver without knowing about your special alternative registry.

With that in mind, here's my "solution": CoalescingComponentFactoryResolver. This is a service that should be provided by the app module and initialised in its constructor, like so:

@NgModule({
 providers: [CoalescingComponentFactoryResolver]
})
class AppModule {
  constructor(coalescingResolver: CoalescingComponentFactoryResolver) {
    coalescingResolver.init();
  }
}

Then, lazy-loaded modules should inject it and register their own ComponentFactoryResolver instances with it. Like so:

@NgModule({})
export class LazyModule {
  constructor(
    coalescingResolver: CoalescingComponentFactoryResolver,
    localResolver: ComponentFactoryResolver
  ) {
    coalescingResolver.registerResolver(localResolver);
  }
}

When this is done, entry components in the lazy-loaded module should be available from non-lazy loaded services.

How it works: When initialised, it injects the root app's ComponentFactoryResolver and monkey patches the resolveComponentFactory method to call its own implementation. This implementation first tries resolving the component factory on all the registered lazy-module resolvers, then falls back to the root app resolver (there's a bit of extra logic to avoid cyclic calls, but that's the gist).

So, yeah, a pretty gross hack. But it works, right now, in Angular 7. Maybe it will be of use to someone.

Building on @CWSpear' s solution, I got this working by simply adding my existing PopupService to the providers array of the component. So I didn't need to create a separate "feature-specific" PopupService.

So like so:

@Component({
  ...
  providers: [PopupService]  //<---- generic PopupService
})
export class MedicationPortletComponent implements OnInit, OnDestroy {
...

Alternatively, you can also add the PopupService to the providers array of the lazy-loaded module's NgModule. Eg:

@NgModule({
  declarations: [
    ...
  ],
  imports: [
    ...
  ],
  providers: [PopupService] //<--- here's the PopupService!
})
export class SharedFeaturePatientModule {}

@jonrimmer best solution

@jonrimmer This is minimal intrusive and works perfectly for our use case. A giant thanks from me and my team :)

I am just curious how it works smoothly in case of lazy router but it throws error when I removed the lazy route and load the modules manually using ngModuleFactoryLoader.

//Ignore the syntax
CompA {
  openDialog() {//invoked on button click of this comp
    matDialog.open(FilterComponent);//works fine in case of lazy route but throws error when manually loaded
  }
}

ModuleA {//lazy module
  imports: [MatDialog, FilterModule],
  declaration: [CompA]
}

FilterModule {
  declaration: [FilterComponent],
  entryComponent: [FilterComponent]
}

FilterComponent { ...
}

@jmcclanahan @MartinJHItInstituttet @splincode

async showBar() {
    const module = await import("../bar/bar.module");
    const compiled = this._compiler.compileModuleAndAllComponentsSync(
      module.BarModule
    );
    compiled.ngModuleFactory.create(this._injector);

    let factory: any = compiled.componentFactories.find(
      componentFactory => componentFactory.selector === "app-dialog"
    );
    this.modal.show(factory.componentType);
  }

@Jimmysh This is for JIT mode. You need to load import("../bar/bar.module.ngfactory") for AOT mode (where compileModuleAndAllComponentsSync is not available).

@mlc-mlapis
heavily inspired by https://github.com/angular/angular/blob/master/aio/src/app/custom-elements/elements-loader.ts

https://github.com/Jimmysh/ng-lazy-component-load

It can use AOT JIT. success for me.

import { InjectionToken, Type } from '@angular/core';
import { LoadChildrenCallback } from '@angular/router';

export const ELEMENT_MODULE_LOAD_CALLBACKS_AS_ROUTES = [
  {
    path: 'aio-foo',
    loadChildren: () => import('../foo/foo.module').then(mod => mod.FooModule)
  }
];

export interface WithCustomElementComponent {
  customElementComponent: Type<any>;
}

export const ELEMENT_MODULE_LOAD_CALLBACKS_TOKEN = new InjectionToken<Map<string, LoadChildrenCallback>>(
  'aio/elements-map'
);

export const ELEMENT_MODULE_LOAD_CALLBACKS = new Map<string, LoadChildrenCallback>();
ELEMENT_MODULE_LOAD_CALLBACKS_AS_ROUTES.forEach(route => {
  ELEMENT_MODULE_LOAD_CALLBACKS.set(route.path, route.loadChildren);
});

```typescript
import { Compiler, Inject, Injectable, NgModuleFactory, NgModuleRef, Type, ComponentFactory } from '@angular/core';
import { LoadChildrenCallback } from '@angular/router';

import { ELEMENT_MODULE_LOAD_CALLBACKS_TOKEN } from './lazy-component-registry';

@Injectable({
providedIn: 'root'
})
export class LazyComponentLoader {
private modulesToLoad: Map;
private modulesLoading = new Map>();

constructor(
private moduleRef: NgModuleRef,
@Inject(ELEMENT_MODULE_LOAD_CALLBACKS_TOKEN) elementModulePaths: Map,
private compiler: Compiler
) {
this.modulesToLoad = new Map(elementModulePaths);
}

load(path: string, selector?: string): Promise {
if (this.modulesLoading.has(path)) {
return this.modulesLoading.get(path);
}

if (this.modulesToLoad.has(path)) {
  const modulePathLoader = this.modulesToLoad.get(path);
  const loadedAndRegistered = (modulePathLoader() as Promise<NgModuleFactory<any> | Type<any>>)
    .then(elementModuleOrFactory => {
      if (elementModuleOrFactory instanceof NgModuleFactory) {
        return elementModuleOrFactory;
      } else {
        return this.compiler.compileModuleAsync(elementModuleOrFactory);
      }
    })
    .then(elementModuleFactory => {
      const elementModuleRef = elementModuleFactory.create(this.moduleRef.injector);
      const factories: Map<any, ComponentFactory<any>> = (elementModuleRef.componentFactoryResolver as any)
        ._factories;
      if (selector) {
        const find = Array.from(factories.keys()).find(type => {
          const factory = factories.get(type);
          return factory.selector === selector;
        });
        if (find) {
          return find;
        } else {
          return Promise.reject(new Error(`not found selector:${selector}`));
        }
      }
      this.modulesToLoad.delete(path);
      return;
    })
    .catch(err => {
      this.modulesLoading.delete(path);
      return Promise.reject(err);
    });
  this.modulesLoading.set(path, loadedAndRegistered);
  return loadedAndRegistered;
}
return Promise.resolve();

}
}

``` typescript
  async showFoo() {
    await this.lazyComponentLoader.load('aio-foo');
    this.modal.show(FooDialogComponent);
  }
  async showFoo2() {
      const aaa = await this.lazyComponentLoader.load('aio-foo', 'app-dialog');
    this.modal.show(aaa);
  }

@Jimmysh ... then correct. It's using the compiler only in JIT mode.

constructor(private compiler: Compiler) {}
...
if (elementModuleOrFactory instanceof NgModuleFactory) {
   return elementModuleOrFactory;
} else {
   return this.compiler.compileModuleAsync(elementModuleOrFactory);
}

@mlc-mlapis AOT will be NgModuleFactory. You can test https://github.com/Jimmysh/ng-lazy-component-load

Hey guys, with the help of the comments form this thread I have created a package that is meant to be a workaround for trying to get entry components loaded outside the module through routing. Please have a look at it and hope that it helps.

Repo Link: https://github.com/Jonathan002/route-master-example
Demo Url: https://jonathan002.github.io/route-master-example/

FWIW, in a similar vein to @CWSpear and @dirkluijk, I removed the the providedIn: 'root' from my service (defined in a lazy loaded module and responsible for opening an entryComponent) and instead specified the service in the providers of my lazy module definition. I believe the biggest/only drawback of this is the service is no longer tree-shakeable.

Using providedIn: MyLazyModule led to circular dependencies warnings.

@jonrimmer I couldn't leave the thread without thanking you!! you saved my day.
For everyone reading my comment it may help you in order not to waste your time if you look at this wonderful answer.

Confirmed this is not resolved in Angular 8.2.0 yet. Since the OP had been created 2.5 years ago, the landscape of Angular had changed a lot, and Angular Material 2 had become Angular Components.

As indicated by the other commentators, Angular Material uses DOM so application developers do not have to use DOM when loading an application component in MatDialog, even if the application component is declared in the entryComponents of the sub module lazily loaded.

However, doing the same DOM things in a business application is looking odd, against Angular's application programming guidelines.

It will be nice that the feature request of the OP could be implemented so ComponentFactoryResolver could "see" an entry component declared in a sub module lazily loaded, one way or the other. Declaring an entry component of a lazy module in app.module is working, but ugly.

In addition to the dirty working solutions as mentioned above: 1. DOM, 2. Declaring the component in app.module, I would provide another solution, less dirty, below.

the 3rd solution, less dirty.

I have a shared services used by multiple modules lazily loaded.

export interface DataComponent {
    data: any;
}

/**
 * Display an NG component in a dialog, and this dialog has not need to answer but close.
 */
@Injectable()
export class DataComponentDialogService {
    modalRef: MatDialogRef<DataComponentDialog>;

    constructor(private dialog: MatDialog) { }

    open(title: string, externalComponentType: Type<DataComponent>, componentData: any, autofocus = true): Observable<any> {
        const isSmallScreen = window.innerWidth < 640 || window.innerHeight < 640;
        this.modalRef = this.dialog.open(DataComponentDialog,
            {
                disableClose: true,
                minWidth: isSmallScreen ? '98vw' : undefined,
                autoFocus: autofocus,
                data: {
                    title: title,
                    externalComponentType: externalComponentType,
                    componentData: componentData,
                    isSmallScreen: isSmallScreen
                }
            });

        return this.modalRef.afterClosed();

    }

}

This service can see entry components of modules not lazily loaded. I have some entry components implemented in some modules lazily loaded. So I have derived class of this service to be provided in each lazy module:

import { DataComponentDialogService } from '../_ui_services/dataComponentDialog.component';

@Injectable()
export class LazyComponentDialogService extends DataComponentDialogService {
    constructor(dialog: MatDialog) {
        super(dialog);
    }
}

Since LazyComponentDialogService is provided in the same lazy module with the lazy entry component, so it can see the component.

Depending on your app architecture, you may not need the derived class, but just provide DataComponentDialogService or alike in each module.

It looks like that the issue does not exist in Angular 9 any more due to the ivy implementation (tested with 9.0.0-next.7). You can dynamically create a component of a lazy loaded module from outside of the lazy module. It appears to be the case that the entry component declaration is not needed any more for dynamic components in Ivy. Ivy does not need a separated component factory which used to be generated by the compiler. The component factory info is embedded in the component definition with Ivy(?). Once you have a component type, you should be able to get its component factory and then create an instance of it. That is just my impression. Take my word with a grain of salt

All you need to do is create a separate widgets module that declares all your dynamic components and also includes them as entryComponents. Then import this module from AppModule. This will ensure that your entry components are registered with the root injector.

This solution is clean and doesn't break Lazy encapsulation.

@NgModule({
  declarations: [
    ModalComponent,
    ... more dynamic components ...
  ],
  entryComponents: [
     ModalComponent,
    ... more dynamic components ...
  ]
})
export class DynamicModule {

}

AppModule:

@NgModule({
   declarations: [
      AppComponent
   ],
   imports: [
      BrowserModule,
      AppRoutingModule,
      BrowserAnimationsModule,
      DynamicModule
   ],
   providers: [],
   bootstrap: [
      AppComponent
   ]
})
export class AppModule { }

@pixelbits-mk That would make all the dynamic components (like modal and the like) eager loaded, but the point here is make them being able to be lazy loaded to not slow down the initial load of the app (especially as the project grows), just like the components that call them.

Let's say that a lazy loaded ComponentA calls dynamically a lazy loaded dynamic ComponentB. If ComponentA knows that it will call ComponentB you can just import ComponentBModule in ComponentAModule. This is not the ideal case, but I can live with it.

The main problem arises (in my opinion) when ComponentA doesn't know that ComponentB will be loaded. For example, if ComponentB is a modal and ComponentA calls a method showModal() in a service, and the service loads ComponentB dynamically behind the scenes, ComponentA wouldn't know that.

What I do now is including the ComponentB reference as a parameter in the method, like showModal(ComponentB), in such a way that ComponentA now knows that the ComponentB will be loaded, but that's bad in my opinion (each call to the method must pass the component reference).

If Entry Components were available without having to import their modules explicitly (what I think this issue is about), that would solve it (maybe Angular could have a way to detect that ComponentA calls a service that imports ComponentB and make ComponentAModule import implicitly ComponentBModule, and although it could have the undesirable effect of also importing modules of components whose references are imported but the reason of the import is not to create them dynamically, I would see this more as an edge case, and would be okay with it).

I haven't tested (yet) @jonrimmer suggestion, but that seems promising.

It would be awesome if ComponentA would automatically lazy load module with ComponentB only if ComponentA is displayed:
ModuleA has component ComponentA1, ComponentA2.
ComponentA1 reference in html ComponentB (in module ModuleB), but ComponentA2 doesn't.
ModuleB would be automatically lazy load only if ComponentA1 is displayed, but not if ComponentA2 is displayed.

This would perfectly fit scenario of dashboards on home page. Dahsboards comes from many different modules, but if dashboard is not included (let's say through user settings), there is no need to download that module.

@jonrimmer Great idea to wrap the resolveComponentFactory function of the main ComponentFactoryResolver and search the registered lazy load modules before.

It works with entry components which inject services that are provided in root. But I can not inject a service that is only provided in the lazy load module. In this case I got a StaticInjectorError. Can you please check it?

I wanted to confirm that ivy seems to take care of this issue. I just stumbled on this issue today when I rewrote my modules to be lazy loaded which contained entry components and a shared services to instantiate them only to find a new error stating the component factory couldn't be found. I was actually shocked to read this was an issue in the first place.

At any rate, I just enabled ivy in my existing angular 8.2 project and all seems to be working as expected.

My solution for usage the Overlay in lazy loaded modules:
You have to pass factory resolver to ComponentPortal's contructor like this

@Injectable()
export class OverlayService {

  constructor(
    private overlay: Overlay,
    private componentFactoryResolver: ComponentFactoryResolver
  ) {
  }

  open<T>(component: ComponentType<T>) {
    const overlayRef = this.overlay.create();

    const filePreviewPortal = new ComponentPortal(component, null, null, this.componentFactoryResolver);

    overlayRef.attach(filePreviewPortal);
  }
}

but you also have to proide OverlayService in lazy loaded module. It's need for OverlayService to inject ComponentFactoryResolver of module in which OverlayService is Provided

@NgModule({
  declarations: [
     SomeComponent,
  ],
  entryComponents: [
    SomeComponent,  // <-------- will be opened via this.overlayService.open(SomeComponent)
  ],
  providers: [
    OverlayService, // <--------
  ]
})
export class LazyLoadedModule {}

@mixrich This approach will work [in my app, I am doing like that and it works fine until I need to close all the modal dialogs on some event like logout] but keep in mind that this will instantiate the OverlayService every time when other module imports this module i.e. the app will not have a single instance like services used to be.

Because there will be no signalton of overlay service it will NOT be easy to know how many modal dialogs are opened.

Of course, it is app's requirements but just to be cautious of this approach.

@mixrich This approach will work [in my app, I am doing like that and it works fine until I need to close all the modal dialogs on some event like logout] but keep in mind that this will instantiate the OverlayService every time when other module imports this module i.e. the app will not have a single instance like services used to be.

Because there will be no signalton of overlay service it will NOT be easy to know how many modal dialogs are opened.

Of course, it is app's requirements but just to be cautious of this approach.

For example, in my case I have ModalDialogModule with provided OverlayService and when I call OverlayService.open(SomeComponent) it also create for me the modal window tempate, inserts SomeComponent inside it and returns back some data structure with helpful observables (for close and success events), component instance and manual close method.
So, when I need to use modals in my LazyModule I just need to import the ModalDialogModule to it for getting the ability to use OverlayService. I found this approach convenient, bacause you always know that to use modal you have to import the ModalDialogModule, like you always know that to use reactive forms you have to import ReactiveFormModule

I got this working in Angular 8.3.6. I have a library module with entry components (mat dialogs) that won't load if I add them to the library modules entry components. It says can't find component factory for MyCustomDialog in the console log when I tried to open it.

Solution is to create a static method in the library module's NgModule class that returns an array of all the module's entry components. Then call this method from the app modules NgModule.

@NgModule({
    declarations: [
        MyCustomDialogComponent
    ],
    imports: [
        CommonModule
    ],
    exports: [
        MyCustomDialogComponent
    ]
    // no need to add the dialog component to entryComponents here.
})
export class MyCustomModule {
    static getEntryComponents() {
        const entryComponents = [];
        entryComponents.push(MyCustomDialogComponent);
        return entryComponents;
    }
}

import {MyCustomModule} from 'my-custom-library'; // import library from node_modules

@NgModule({
    declarations: [
        AppComponent
    ],
    imports: [
        BrowserModule,
        AppRoutingModule,
        BrowserAnimationsModule,
        MyCustomModule
    ],
    providers: [],
    bootstrap: [AppComponent],
    entryComponents: [].concat(MyCustomModule.getEntryComponents())
})
export class AppModule {
}

I know this still doesn't fix the entryComponents in the library not working but at least with the static method the app module doesn't have to know about the entry components in the custom library. It just calls that method and gets whatever is listed in the library. There's no responsibility on the app to know what components in the library are entry components.

@asmith2306 why do you not use spread operator? it looks ugly

@asmith2306 why do you not use spread operator? it looks ugly

Because it doesn't work with it. There's a bug about it somewhere.

looks like it doesnt work with --prod mode

Even if it did work, it does not address the REAL problem. the problem is if I add a component to the entryComponents of an NgModule, and that module is imported into a lazily loaded module in my app, then the entryComponents should automatically get registered with the apps entry components. There should be no further work required. I would further say that any component in the entryComponents of any NgModule should automatically wind up in the entryComponents of the app module. This is currently not the case. If it were then we wouldn't be here.

@broweratcognitecdotcom What about conflicts and priorities from different injection levels?

@mlc-mlapis order should be on a first-come, first-served basis in my opinion.

@mlc-mlapis Do you mean if different components have the same name? First match or an error if there are multiple matches. I could live with a requirement that entry component names must be unique.

@broweratcognitecdotcom Hi, yes. Because all variants have to be covered if we are talking about general principles.

@mlc-mlapis I think that the limitation in angular that many would like to overcome is that entry components can only be instantiated and placed in the dom by the app. The app does not need to know about a dialog box that a module that I import into a lazy loaded module uses. The current concept of entry components breaks the modular pattern of angular.

AFAIK with Ivy entryComponents will no longer be necessary, right? Since components will have locality so just dynamically importing them will always work?

@Airblader You are right, we know. It was just a small reminiscence back to history. 😄

For those who still struggling with this issues here is the solution that actually works: https://github.com/angular/angular/issues/14324#issuecomment-481898762

I publish a package for this issues. some code copy from jonrimmer.
It looks like that the issue does not exist in Angular 9. If you need fix this now. you can use @aiao/lazy-component
sample code here
https://github.com/aiao-io/aiao/tree/master/integration/lazy-component

I believe that this is fixed in Angular v9 running ivy. If anyone still faces similar problems, please open a new issue with a minimal reproduce scenario. Thnx!

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

_This action has been performed automatically by a bot._

Was this page helpful?
0 / 5 - 0 ratings