Home > Software design >  How to dynamically create elements with *ngFor from an array of tag names?
How to dynamically create elements with *ngFor from an array of tag names?

Time:01-08

I am creating a website builder and need to dynamically render html element where the tags and content are supplied from an array. Is that possible with *ngFor?

@Component({
  selector: 'app-root',
  template: `
    <ng-container *ngFor="let element of elements">
      <{{element.tag}}>
        {{ element.text }}
      </{{element.tag}}>
    </ng-container>
  `
})
export class AppComponent {
  elements = [
    { tag: 'div', text: 'foo' },
    { tag: 'p', text: 'bar' },
  ]
}

To keep things simple, this example is flat but my actual use case is recursive with children

export interface ElementDescriptor {
  tag: string
  text?: string
  children?: ElementDescriptor[]
}

CodePudding user response:

As pointed by @MarkoEskola in his answer, you can use Angular Renderer2. From your model, you create the text content with createText(), then create the element with createElement() and then add to its parent with appendChild(). Repeat the process for descendants.

This is untested (coded here on SO):

@Component({
  selector: 'app-root',
  template: `
    <div #root></div>
  `
})
export class AppComponent implements OnInit {
  elements: ElementDescriptor = [
    { tag: 'div', text: 'foo' },
    { tag: 'p', text: 'bar' },
  ]

  @ViewChild('root', { static: false }) private root: ElementRef;

  constructor(private renderer: Renderer2) {}

  ngOnInit(): void {
    this.addChildren(this.root, this.elements);
  }

  private addChildren(parent: HTMLElement, children: ElementDescriptor[]): void {
    for (var child of children) {
      this.addChild(parent, child.tag, child.text);
      if (child.children) {
        this.addChildren(child, children);
      }
    }
  } 

  private addChild(parent: HTMLElement, childTag: string, childText: string): void {
    const el = this.renderer.createElement(childTag);
    const text = this.renderer.createText(childText);

    this.renderer.appendChild(el, text);
    this.renderer.appendChild(parent, el);
  }
}

You might also need to do some checks to ensure adding a text to an element is valid, or adding a specific element as child to another does not invalidate your component's HTML markup.

CodePudding user response:

I think you have to add the elements to the DOM in the code by using Angular Renderer2 and its methods. I don't think this is possible only using ngFor and template. https://www.concretepage.com/angular-2/angular-4-renderer2-example

  •  Tags:  
  • Related