3

I want to dynamically insert a <mat-checkbox> element into a component. and I inserted two <mat-checkbox> elements. one inserted statically (i.e into the template file directly).

and the other one inserted dynamically, and contains a <mat-checkbox> and another normal checkbox <input type="checkbox" />

the first one (the statically inserted one) rendered without any problem.

also the normal input inserted dynamically without any problem.

but the <mat-checkbox> that dynamically inserted didn't render as expected.

component.html:

<mat-checkbox> checkbox (static) </mat-checkbox>

<div [innerHTML] = "test2">

component.ts:

 this.test= ` 
     <mat-checkbox>mat-checkbox</mat-checkbox><br />
     <input type="checkbox" /> normal checkbox   
  `

  this.test2 = this.sanitizer.bypassSecurityTrustHtml(this.test);

I created a Reproduction on stackblitz:

https://stackblitz.com/edit/angular-hsjevo

also I need to create checkboxes from categories, and append some extra spaces before each sub-category

i.e:

  • category
    • sub category
      • sub of sub category
  • category2

each category or sub-category is <mat-checkbox value="$id"> $title

Sh eldeeb
  • 1,589
  • 3
  • 18
  • 41
  • 2
    That's not how you should us Angular to show dynamic content on your component. Use `*ngIf` / `*ngFor` for example to decide if something should be rendered depending on a component property. – Thomas Schneiter Feb 17 '20 at 15:06
  • 1
    Kindly take a look, this solved my issue Ref: https://stackoverflow.com/questions/47384092/angular-mat-checkbox-dynamically-with-ngmodel – Vivek Singh Feb 17 '20 at 15:07
  • I don't want to conditionally load a component. I want to dynamically add some html input to the existing component @ThomasSchneiter – Sh eldeeb Feb 18 '20 at 14:08

1 Answers1

4

innerHTML property of div tag can only parse and render regular HTML elements like input, etc. To dynamically create Angular components like mat-checkbox, you need to inform Angular about those components. Let's see how this can be done below:

We need a placeholder in the AppComponent template where we can add the mat-checkbox component dynamically. We can use <ng-template> tag as a placeholder in this case.

app.component.html

<mat-checkbox>mat-checkbox (static)</mat-checkbox> <hr />


dynamic angular component:<br/>
<ng-template #placeholder></ng-template>

In AppComponent typescript file, we need to refer this placeholder and append the mat-checkbox component. Find the inline comments for explanation.

app.component.ts

import {
  Component,
  ComponentFactoryResolver,
  OnInit,
  Renderer2,
  ViewChild,
  ViewContainerRef
} from '@angular/core';
import {MatCheckbox} from '@angular/material';

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnInit {
  // Get reference to the ng-template placeholder tag
  @ViewChild('placeholder', {read: ViewContainerRef, static: true}) placeholder: ViewContainerRef;

  constructor(
    private resolver: ComponentFactoryResolver,
    private renderer: Renderer2) {}

  ngOnInit() {
    this.createComponent();
  }

  createComponent() {
    // creating the mat-checkbox tag body content
    let content = this.renderer.createText(' mat-checkbox (dynamic)');
    // clear any previously appended element or component
    this.placeholder.clear();
    // resolve the MatCheckbox component and get the factory class to create the component dynamically
    let factory = this.resolver.resolveComponentFactory(MatCheckbox);
    // create the component and append to the placeholder in the template
    let ref = this.placeholder.createComponent(factory);
    // set the mat-checkbox body content
    (ref.location.nativeElement as HTMLElement).appendChild(content);
  }
}

In order for the above code to work, we need to inform Angular that we would be expecting to resolve the MatCheckbox component at runtime. Make the following changes in app.module.ts.

app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { MatCheckboxModule, MatCheckbox } from '@angular/material';

import { AppComponent } from './app.component';
import { HelloComponent } from './hello.component';


@NgModule({
  imports:      [ BrowserModule, FormsModule, MatCheckboxModule ],
  declarations: [ AppComponent, HelloComponent ],
  // Inform Angular about those components which will be created dynamically by including them in entryComponents field
  entryComponents: [ MatCheckbox ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }

Now you should be able to see the mat-checkbox created dynamically at runtime. View the complete example here (https://stackblitz.com/edit/angular-e7vhue)

For more information, refer this article.

Hope this helped your query.

Shravan
  • 1,182
  • 8
  • 21
  • Ok, I want to fetch an array of inputs that will be converted into , each entry contains {title, id}. indeed I want to form the checkboxes to append some extra spaces before every child category, So I think of fetching the inputs as string – Sh eldeeb Feb 18 '20 at 14:45
  • @Sheldeeb Interesting. You can start appending multiple mat-checkbox component to the `ng-template` placeholder using the `createComponent` method of the `ViewContainerRef` reference. I have updated the app in the stackblitz for this usecase. – Shravan Feb 18 '20 at 16:26
  • I just gave you vote +1 & accepted your answer @sharavan – Sh eldeeb Feb 18 '20 at 16:46
  • How to add additional directives to he dynamically loaded components ex: `..` – Sh eldeeb Feb 22 '20 at 14:40
  • @Sheldeeb As far as I know, there is no way to add/remove directives programatically. Instead you can create a wrapper component (say, ) and control the directive value through the wrapper component inputs. Have a look at the solution [here](https://stackoverflow.com/questions/60284184/how-to-dynamically-apply-a-tooltip-to-part-of-an-elements-dynamic-text-content/60287692#60287692) which does the same. – Shravan Feb 23 '20 at 12:39