68

What I want to do is simply to append some html on an element. I checked some links and found different confusing, non-working, non-recommended solutions.

Using JavaScript, I'll do something like this:

var d1 = document.getElementsByClassName('one');
d1.insertAdjacentHTML('beforeend', '<div class="two">two</div>');

How do I achieve the same result using typescript/angular2, RC5?

EDIT

The element with class .one is generated by an external js, and I can't modify it.

Srdjan Pazin
  • 103
  • 2
  • 5
Gerald Hughes
  • 5,771
  • 20
  • 73
  • 131

5 Answers5

117

1.

<div class="one" [innerHtml]="htmlToAdd"></div>
this.htmlToAdd = '<div class="two">two</div>';

See also In RC.1 some styles can't be added using binding syntax

  1. Alternatively
<div class="one" #one></div>
@ViewChild('one') d1:ElementRef;

ngAfterViewInit() {
  d1.nativeElement.insertAdjacentHTML('beforeend', '<div class="two">two</div>');
}

or to prevent direct DOM access:

constructor(private renderer:Renderer) {}

@ViewChild('one') d1:ElementRef;

ngAfterViewInit() {
  this.renderer.invokeElementMethod(this.d1.nativeElement', 'insertAdjacentHTML' ['beforeend', '<div class="two">two</div>']);
}
    3.
constructor(private elementRef:ElementRef) {}

ngAfterViewInit() {
  var d1 = this.elementRef.nativeElement.querySelector('.one');
  d1.insertAdjacentHTML('beforeend', '<div class="two">two</div>');
}
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • 1
    `var d1 = this.elementRef.nativeElement.querySelector('.one');` returns null :) I think it executes before it renders the html – Gerald Hughes Aug 24 '16 at 14:47
  • 1
    This might be related to how you create the `.one` element. I don't have any information about that. – Günter Zöchbauer Aug 24 '16 at 17:09
  • 1
    for some reason, ngAfterviewinit have to be like this: setTimeout(() => { var d1 = this.elementRef.nativeElement.querySelector('.one'); d1.insertAdjacentHTML('beforeend', '
    two
    '); }, 1000);
    – jcdsr Jan 28 '19 at 16:57
  • @jcdsr you could try `ngAfterContentInit`. I don't remember which one executes first. – Günter Zöchbauer Jan 28 '19 at 17:02
  • Does the 3rd solution too prevent Direct Dom Access? – Rohan Agarwal Jan 09 '20 at 18:25
  • No, it uses `nativeElement` but I think this is now considered Ok in contrary to back then when I posted the answer. Only if you want to use server-side rendering or webworker then you need to avoid it. – Günter Zöchbauer Jan 09 '20 at 18:40
  • what if the div I want to manipulate is added at runtime? that is, I am adding a span to the dom manually using insertAdjacentHTML and then I am trying to change its content but nothing is happening – M Fuat Oct 12 '21 at 13:46
  • @Gunter, Can I add some angular component as below this.htmlToAdd = ' – ashish kumar May 18 '23 at 07:23
  • @ashishkumar No, just plain HTML. There is information out there abour what you want like https://angular.io/guide/dynamic-component-loader, https://stackoverflow.com/questions/46576727/angular-compile-and-create-components-at-runtime – Günter Zöchbauer May 18 '23 at 10:29
30

With the new angular class Renderer2

constructor(private renderer:Renderer2) {}

  @ViewChild('one', { static: false }) d1: ElementRef;

  ngAfterViewInit() {
    const d2 = this.renderer.createElement('div');
    const text = this.renderer.createText('two');
    this.renderer.appendChild(d2, text);
    this.renderer.appendChild(this.d1.nativeElement, d2);
  }
Abhijeet Abnave
  • 801
  • 6
  • 16
J.J
  • 881
  • 16
  • 29
7

You could do something like this:

htmlComponent.ts

htmlVariable: string = "<b>Some html.</b>"; //this is html in TypeScript code that you need to display

htmlComponent.html

<div [innerHtml]="htmlVariable"></div> //this is how you display html code from TypeScript in your html

Stefan Svrkota
  • 48,787
  • 9
  • 98
  • 87
5

There is a better solution to this answer that is more Angular based.

  1. Save your string in a variable in the .ts file

    MyStrings = ["one","two","three"]

  2. In the html file use *ngFor.

    <div class="one" *ngFor="let string of MyStrings; let i = index"> <div class="two">{{string}}</div> </div>

  3. if you want to dynamically insert the div element, just push more strings into the MyStrings array

    myFunction(nextString){ this.MyString.push(nextString) }

this way every time you click the button containing the myFunction(nextString) you effectively add another class="two" div which acts the same way as inserting it into the DOM with pure javascript.

  • eg. MyStrings = ["
    This is starting", "some dynamic text

    ", "...this is ending
    "; When we loop through it in the html, angular is auto closing the first
    and then the last
    is kind of broken. Another example is if we have some table and td tags in the array opening and closing so we can create the table structure dynamically and add the dynamic text. Output:
    This is starting
    some dynamic text this is ending Expected output:
    some dynamic text ...this is ending
    – Rafi Ali Khan Oct 31 '17 at 17:24
2

When working with Angular the recent update to Angular 8 introduced that a static property inside @ViewChild() is required as stated here and here. Then your code would require this small change:

@ViewChild('one') d1:ElementRef;

into

// query results available in ngOnInit
@ViewChild('one', {static: true}) foo: ElementRef;

OR

// query results available in ngAfterViewInit
@ViewChild('one', {static: false}) foo: ElementRef;
Daniel Danielecki
  • 8,508
  • 6
  • 68
  • 94