Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compile dynamic HTML in Angular 4/5- something similar to $compile in Angular JS

I wanted to receive an HTML data via service call to server(this is for sure. I cannot keep templates in local) and manipulate them internally on how to show it(either as a modal or full page). This HTML with Angular tags should be looped to a component and work together. At most kind of $compile in Angular JS.

I am developing the solution in Angular 5 and should be compatible with AOT compiler. I had referred several solutions and landed to confusion on the deprecated and updated solutions. Please help me. I believe your updated answer would help many other people as well.. Thank you so much in advance!

like image 899
Srini Avatar asked Dec 29 '17 21:12

Srini


People also ask

What is $compile in Angular?

The $compile service is the service to use for compilation. Invoking $compile against markup will produce a function you can use to bind the markup against a particular scope (what Angular calls a linking function). After linking, you'll have DOM elements you can place into the browser.

What is compile and link in AngularJS?

Compile function: It is used for template DOM Manipulation and collect all of the directives. Link function: It is used for registering DOM listeners as well as instance DOM manipulation. It is executed once the template has been cloned.

Is Angular JS compiled?

Overview. AngularJS's HTML compiler allows the developer to teach the browser new HTML syntax. The compiler allows you to attach behavior to any HTML element or attribute and even create new HTML elements or attributes with custom behavior. AngularJS calls these behavior extensions directives.


1 Answers

For rendering HTML on the fly, you need DomSanitizer. E.g. something like this:

<!-- template -->
<div [innerHTML]="htmlData"></div>

// component
import { Component } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  htmlData: any;
  constructor(private sanitizer: DomSanitizer) {}

  ngOnInit() {
    this.htmlData= this.sanitizer.bypassSecurityTrustHtml('<div style="border: 1px solid red;"><h2>Safe Html</h2><span class="user-content">Server prepared this html block.</span></div>');
  }
}

Now, that's the gist of it. You obviously also need a loading mechanic. You might also want to include some data into this block - if it's simple data, it can be on the fly:

this.htmlData = this.sanitizer.bypassSecurityTrustHtml(`<div>${this.someValue}</div>`);

For more complex scenarios you might need to create a dynamic component.

Edit: an example of a component resolved dynamically. With this, you create a component on-the-fly from server-sent html.

@Component({
  selector: 'my-component',
  template: `<h2>Stuff bellow will get dynamically created and injected<h2>
          <div #vc></div>`
})
export class TaggedDescComponent {
  @ViewChild('vc', {read: ViewContainerRef}) vc: ViewContainerRef;

  private cmpRef: ComponentRef<any>;

  constructor(private compiler: Compiler,
              private injector: Injector,
              private moduleRef: NgModuleRef<any>,
              private backendService: backendService,
              ) {}

  ngAfterViewInit() {
    // Here, get your HTML from backend.
    this.backendService.getHTMLFromServer()
        .subscribe(rawHTML => this.createComponentFromRaw(rawHTML));
  }

  // Here we create the component.
  private createComponentFromRaw(template: string) {
    // Let's say your template looks like `<h2><some-component [data]="data"></some-component>`
    // As you see, it has an (existing) angular component `some-component` and it injects it [data]

    // Now we create a new component. It has that template, and we can even give it data.
    const tmpCmp = Component({ template, styles })(class {
      // the class is anonymous. But it's a quite regular angular class. You could add @Inputs,
      // @Outputs, inject stuff etc.
      data: { some: 'data'};
      ngOnInit() { /* do stuff here in the dynamic component */}
    });

    // Now, also create a dynamic module.
    const tmpModule = NgModule({
      imports: [RouterModule],
      declarations: [tmpCmp],
      // providers: [] - e.g. if your dynamic component needs any service, provide it here.
    })(class {});

    // Now compile this module and component, and inject it into that #vc in your current component template.
    this.compiler.compileModuleAndAllComponentsAsync(tmpModule)
      .then((factories) => {
        const f = factories.componentFactories[0];
        this.cmpRef = f.create(this.injector, [], null, this.moduleRef);
        this.cmpRef.instance.name = 'my-dynamic-component';
        this.vc.insert(this.cmpRef.hostView);
      });
  }

  // Cleanup properly. You can add more cleanup-related stuff here.
  ngOnDestroy() {
    if(this.cmpRef) {
      this.cmpRef.destroy();
    }
  }
}
like image 175
Zlatko Avatar answered Oct 05 '22 23:10

Zlatko