10

Say I have an Angular2 component <my-button> and I want to give an input for options to render in a dropdown menu shown when the button is clicked. I have the menu component as <my-menu> and it renders conditionally in the template of <my-button> if there are options passed in.

Maybe I can just absolutely position <my-menu> within <my-button> to achieve the desired positioning. But maybe I can't because I have overflow:hidden on a containing element and that would clip <my-menu>. So instead I need to render <my-menu> in <body> and position it absolutely to <my-button>.

Is there a way to render <my-menu> to <body> even though it is placed inside the template for <my-button> instead?

Thanks!

Comptonburger
  • 583
  • 3
  • 10

4 Answers4

7

You can do that, however it is complicated.

  1. Add your dynamic component to module declarations and entryComponents
  2. Get reference to root ViewContainerRef injecting it to your application component.
  3. Get reference to ComponentFactoryResolver, also using injection.
  4. Use something like this:

    private resolverFactory:ComponentFactoryResolver;
    private viewContainer:ViewContainerRef;
    
    var compFactory:ComponentFactory<Frame> = this.resolverFactory.resolveComponentFactory(Frame);
    
    var cmpRef:ComponentRef<Frame> = this.viewContainer.createComponent(compFactory, this.viewContainer.length);
    
  5. Removing component:

    cmpRef.destroy();
    
kemsky
  • 14,727
  • 3
  • 32
  • 51
1

No, there isn't.

You can bootstrap a component outside your AppComponent and communicate using a shared service instead.

Some discussion about dynamically creating components as sibling to the AppComponent are discussed in https://github.com/angular/angular/issues/9293 but it's not clear if, how, or when this might land.

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • Rendering inside of `AppComponent` would likely be fine, no need to bootstrap something extra. But could you elaborate on communicating using a shared service? Mainly, how would you render a menu and a button in radically different locations on the component tree, and then associate the two? – Comptonburger Aug 25 '16 at 05:31
  • 1
    Should all be explained in https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#bidirectional-service – Günter Zöchbauer Aug 25 '16 at 05:33
  • @GünterZöchbauer as I stated in the comment below, so many boilerplate and ubiquitous features require the ability to add arbitrary components to the ``: modals, tooltips, growl-like notifications, menus as OP describes... Any idea how any of these can be achieved in 2.0.x (GA)? – AndyPerlitch Oct 03 '16 at 20:07
1

Yes, you can. Since you didn't provide code, I can't give you an updated example. However, when I built a modal system, I used this as a base: https://github.com/shlomiassaf/angular2-modal

When we initially pulled it in, it wasn't very reusable, but there's a way to get the root node via ApplicationRef. (This was back around Angular 2 beta 8.)

It's definitely doable.

UPDATE: You can use an ApplicationRef to get the location to inject into, and a DynamicComponentLoader to inject your new component. Using the following line as your hostLocation (second parameter in DynamicComponentLoader's loadIntoLocation method) will put the element just after your app component in the html:

this.appRef['_rootComponents'][0].location

appRef is the reference to the ApplicationRef that is passed into my component's constructor.

You may be able to play with other elements or methods that came out after beta .8 to get the effect you desire.

Here's another good site with a lot of examples: https://medium.com/tixdo-labs/angular-2-dynamic-component-loader-752c3235bf9#.x913qdh4u

ps2goat
  • 8,067
  • 1
  • 35
  • 68
  • Is `DynamicComponentLoader` still available with angular2 GA? I can't find documentation on it, and it seems to be somewhat linked to ComponentResolver, which was fully deprecated in favor of `Compiler`. – AndyPerlitch Sep 30 '16 at 21:35
  • It actually appears as though the medium.com blog link you provided no longer works for angular2 – AndyPerlitch Sep 30 '16 at 21:36
  • @AndyPerlitch - that's why I labeled the example with the beta version I was using. I think I was able to still do it in RC 1, but I had to link directly to a deeper file than they exposed in the main package, which the angular team advised not to do. I haven't tried it since RC 1, but I can try it in the final version. – ps2goat Oct 03 '16 at 02:03
  • Thanks @ps2goat. I confirmed that DynamicComponentLoader and even `Compiler#compileComponent` are deprecated. There has to be a way to append components directly to the body... there are so many use-cases where that is required aside from a menu like what OP describes: modals, growl-like notifications, popovers, tooltips... – AndyPerlitch Oct 03 '16 at 20:04
1

If you want to load a component conditionally you can make use of ComponentResolverFactory.

All You need to do is to have one viewContainerRef instance & update it using compiler wherever you want.

I will recommend creating a separate module for all the components you want to load dynamically & load them asynchronously.

Perhaps below answer could help you in better way.

Load Components Dynamically

Community
  • 1
  • 1
Bhushan Gadekar
  • 13,485
  • 21
  • 82
  • 131