11

I was able to successfully create a button that on click loads another instance of my component into the DOM, but it loads it outside the component I want it to be inside which results with the CSS being off. When I add "editorComponents" to my page it currently looks like this:

enter image description here

But I want it to look like this:

enter image description here

You can see from the inspection that in the second example all of the tags are manually moved to where I want them to be.

Right now my code is as follows:

home.component.html

<div class="row backgroundContainer">

  <div class="col-md-7 componentContainer">
    <app-editor></app-editor>
    <button (click)="addEditor()" type="button">Add Editor</button>
  </div>

  <div class="col-md-5 componentContainer">
    <app-bible></app-bible>
  </div>

  <div id="footerBox">
    <app-footer></app-footer>
  </div>
  
</div>

home.component.ts

import { Component, OnInit, ViewContainerRef, ComponentFactoryResolver } from '@angular/core';
import { EditorComponent } from '../editor/editor.component';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {

  constructor(private componentFactoryResolver: ComponentFactoryResolver,
    private viewContainerRef: ViewContainerRef) {}

  ngOnInit() {
  }

  addEditor() {
    const factory = this.componentFactoryResolver.resolveComponentFactory(EditorComponent);
    const ref = this.viewContainerRef.createComponent(factory);
    ref.changeDetectorRef.detectChanges();
  }

}

So how do I make it so when I dynamically add an EditorComponent to my page it is added directly below the current "app-editor" in my html?

1 Answer 1

19

You need to use your own ViewContainerRef. Currently you injecting root one and as you can see all your components appended to <app-root>.

import { Component, OnInit, ViewContainerRef, ComponentFactoryResolver } from '@angular/core';
import { EditorComponent } from '../editor/editor.component';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
  // Setup custom viewChild here
  @ViewChild('container', { read: ViewContainerRef }) viewContainerRef: ViewContainerRef;    

  constructor(private componentFactoryResolver: ComponentFactoryResolver) {}

  ngOnInit() {
  }

  addEditor() {
    const factory = this.componentFactoryResolver.resolveComponentFactory(EditorComponent);
    const ref = this.viewContainerRef.createComponent(factory);
    ref.changeDetectorRef.detectChanges();
  }

}

Now inside your home.component.html put this where you want your editors to be instantiated

<div #container></div>

You can use any html tag here.

Now all your editors will be inside this block.

Sign up to request clarification or add additional context in comments.

4 Comments

If you don't want to introduce a div, use <ng-template #container></ng-template>.
Add ViewChild to import from angular/core
You don't need the detectChanges() call do you?
createComponent with factory input is marked as deprecated: "Angular no longer requires component factories to dynamically create components. Use different signature of the createComponent method, which allows passing Component class directly."

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.