1

It is not a duplication!

So, basically, this question is very close related to this one: Angular 2 dynamic tabs with user-click chosen components and mostly to it's author also. But with the huge remark - I want to render my component not to the current ElementRef, but to custom element on the body (or simply just to the body.

I have some kind of implementation of Autocomplete directive that should render it's list component to the body like in code below (refactored to use Compiler instead of ComponentResolver after RC.5). This worked fine with RC.4 (partially with RC.5) with some trick of using internal method of ApplicationRef (or using direct DOM manipulations):

@Directive({
  selector: '[...]'
})
export class AutoCompleteDirective {
  constructor(private appRef:ApplicationRef, private compiler: Compiler, private injector:Injector) {
  }

  render () {
    this.compiler.compileComponentAsync(AutoCompleteTmplComponent)
        .then((factory:ComponentFactory<AutoCompleteTmplComponent>) => {
            let cmpRef:ComponentRef<AutoCompleteTmplComponent> = 
                  factory.create(this.injector, null, `body`);
            // here is the magic coming
            (<any>this.appRef)._loadComponent(cmpRef);
            // OR (which is more ugly): 
            // window.document.body.apeendChild(cmpRef.location.nativeElement);
  }
}

Finally, the problem - unit test are failing now since TesBed introduced and we cannot explicitly inject ApplicationRef into tests config. Also the usage of internal method of experimental class, which can be highly possible removed soon, is not looking good and scalable (also as a direct DOM manipulation).

The question - does anyone know more elegant and stable solution?

Community
  • 1
  • 1
Oleg Barinov
  • 1,635
  • 12
  • 7
  • If you add a component to an element, all it's content is purged. I'm pretty sure this is not what you want to happen with the `` element. – Günter Zöchbauer Aug 22 '16 at 13:08
  • I'm not agree. When we are using ViewContainerRef to render dynamic component, it is just adding the html to the end of component's ElementRef.nativeElement. Without purging. The same with the 'body' (or another selector, let it be '.some-class' - the same behavior), but problem is, that this implementation of 'create' method of ComponentFactory is not rendering it.. – Oleg Barinov Aug 22 '16 at 13:12
  • I agree that this is the case for `ViewContainerRef`. I haven't tried with a string selector but if this is used to create the root component (like `DynamicComponentLoader.loadAsRoot` was), then it will work the way I mentioned. Have you tried on another element than ``? – Günter Zöchbauer Aug 22 '16 at 13:15
  • For sure, I have a special div for rendering this component with custom class. I'm providing it as a 3rd param to the `create` method like this: `factory.create(this.injector, null, '.custom-class')` Looks like Angular team didn't implement it for 100%, because `this.viewContainerRef.createComponent()` has a lot of actions after calling create method of ComponentFactory.. here are the source: https://github.com/angular/angular/blob/master/modules/%40angular/core/src/linker/view_container_ref.ts – Oleg Barinov Aug 22 '16 at 13:24
  • Doesn't look like it. It is marked `stable` https://github.com/angular/angular/blob/b96869afd2bdd3ad8c5b7477da647af628e383aa/modules/%40angular/core/src/linker/component_factory.ts#L89 – Günter Zöchbauer Aug 22 '16 at 13:26
  • Yeah, that's the problem, that they are not going to change it in a nearby time ) but for custom rendered elements, like dropdowns or modals or something like this, which should be html/css agnostic (to avoid z-index or overflow or whatever headache) and added dynamically to the body, there is an issue right now.. – Oleg Barinov Aug 22 '16 at 14:05
  • You can bootstrap it as a different Angular application. You can use a shared service to communicate between different apps (just be aware that they use different zones. See also http://stackoverflow.com/questions/36566698/cant-initialize-dynamically-appended-component-in-angular-2/36566919?noredirect=1#comment60736661_36566919 for a pre-RC.5 example. – Günter Zöchbauer Aug 22 '16 at 14:07
  • No, this is insane ) in RC.5 with cannot just `bootstrap` in the air, we need to create an `NgModule`, which is for my perspective, just to clip somewhere else another template - too much... and impossible to test at all.. all that I need, just to provide another parent for this stuff.. – Oleg Barinov Aug 22 '16 at 14:29
  • You could just add **one** such additional application and use a service to communicate with this component, for example to dynamically add all kinds of components. Like some global modal component that shows all kinds of stuff any component or service from the main application requests them to show. – Günter Zöchbauer Aug 22 '16 at 15:06
  • 1
    I see.. yes, I agree, this is an another way of implementation, but not definitely 'better' than others ) Anyway - thanks for that! Will try everything and make my own conclusion ) – Oleg Barinov Aug 22 '16 at 15:27

0 Answers0