dongping4273
dongping4273
2018-02-27 09:01
浏览 29
已采纳

Angular:带有角度分量动态的JSON html标记

EDIT 1:

Thank you for @Narm for the solution. I got it working!

In let myTemplate = '<div class="test" (tap)="test();">Test</div>';, I have a tap function.

When I click on it to invoke the function, it does not work and gives an error:

ERROR TypeError: _co.another_test is not a function

Here is what I have so far:

ngOnInit(){ 
   ...
    let myTemplate = `<div class="test" (tap)="test();">Test</div>`;
   ...
}

test(){
  console.log("Test");
}

Any thoughts?


Original Question Below

From php using REST, I am getting html markup with Angular components:

From php:

function send_html(){
    $html = '<div class="test" *ngIf="data">This is an example</div>';
    return $html;
};

Then in my angular project, I am trying to add this html dynamically using componentFactoryResolver: (I understand that it only accepts Angular component)

Here is my though process:

  1. In main.ts (shown below): call the getDynamicREST() and get the $html from php.
  2. When the data is fetched, then send this to my_component.ts to make this as an Angular component.
  3. Once the html markup becomes a part of Angular component, then use createComponent to create the component.

Of course, it doesn't work...

This is what I have so far: Please feel free to tear it apart.

main.html

<div class="top">
   <ng-template #main></ng-template>
</div>

main.ts

import { Component, ViewChild, ComponentFactoryResolver, ViewContainerRef  } from '@angular/core';
import { my_component } from './my_component';

@Component({
    selector: 'page-main_page',
    templateUrl: 'main_page.html'
})
export class main_page {        
    @ViewChild('main', { read: ViewContainerRef }) entry: ViewContainerRef;
    data: any;

constructor(public resolver: ComponentFactoryResolver,
            public mine: my_component ){    

};      

    ngOnInit(){ 
        this.getDynamicREST().then((res)=>{
            this.mine.data = res;

            const factory = this.resolver.resolveComponentFactory(my_component);
            this.entry.createComponent(factory);

        })
    };

}

my_component.ts

import { Component } from '@angular/core';


@Component({
    selector: 'my_component ',
    template: '<div class="my_component">{{data}}</div>'
})

export class my_component {
   data: any;
}

How would I achieve this so that I can fetch angular components dynamically and display them?

Any help will be much appreciated.

Thank you.

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

1条回答 默认 最新

  • douhao8456
    douhao8456 2018-03-01 19:20
    已采纳

    You're on the right path. I ran into this very same scenario just a few months ago, here is the solution to accomplish your goals.

    Here I attached a StackBlitz if you want to see the code running live and have a chance to play with it. Dynamic components can be a challenge to understand at first.

    Live Demo

    app.component.ts

    This component acts as the container to the dynamic component and is where it will be created.

    import {
      Component, ViewChild, OnInit, OnDestroy,
      AfterViewInit, ComponentFactoryResolver,
      Input, Compiler, ViewContainerRef, NgModule,
      NgModuleRef, Injector
    } from '@angular/core';
    
    
    import { CommonModule } from '@angular/common';
    import { BrowserModule } from '@angular/platform-browser';
    
    
    @Component({
      selector: 'app',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    })
    export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
      @ViewChild('vc', { read: ViewContainerRef }) _container: ViewContainerRef;
      private cmpRef;
    
      constructor(
        private componentFactoryResolver: ComponentFactoryResolver,
        private compiler: Compiler,
        private _injector: Injector,
        private _m: NgModuleRef<any>) { }
    
      ngOnInit() { }
    
      ngAfterViewInit() {
        let myTemplate = `<h2> Generated on the fly</h2>
                          <p>You can dynamically create your component template!</p>`
    
        @Component({ 
          template: myTemplate 
          })
          class dynamicComponent {
            constructor(){}
        }
        @NgModule({
          imports: [
            BrowserModule
          ],
          declarations: [dynamicComponent]
        })class dynamicModule {};
        const mod = this.compiler.compileModuleAndAllComponentsSync(dynamicModule);
        const factory = mod.componentFactories.find((comp) =>
          comp.componentType === dynamicComponent
        );
    
        this.cmpRef = this._container.createComponent(factory);
      }
    
      ngOnDestroy() {
        if (this.cmpRef) {
          this.cmpRef.destroy();
        }
      }
    }
    

    Then in the template for your app component

    app.component.html

    <ng-template #vc></ng-template>

    Then in your app.module you need to import the compiler and declare it as a provider:

    app.module.ts

    import {Compiler, COMPILER_OPTIONS, CompilerFactory} from '@angular/core';
    import {JitCompilerFactory} from '@angular/platform-browser-dynamic';
    export function createCompiler(compilerFactory: CompilerFactory) {
      return compilerFactory.createCompiler();
    }
    
    
    
    @NgModule({
      declarations: [
        AppComponent,
      ],
      imports: [
        BrowserModule,
        RouterModule,
        FormsModule,
        ReactiveFormsModule,
        HttpClientModule,
        routing
      ],
      providers: [
        {provide: COMPILER_OPTIONS, useValue: {}, multi: true},
        {provide: CompilerFactory, useClass: JitCompilerFactory, deps: [COMPILER_OPTIONS]},
        {provide: Compiler, useFactory: createCompiler, deps: [CompilerFactory]}
      ],
      bootstrap: [AppComponent]
    })
    export class AppModule {}
    

    *** Edit for 'tap' question

    What you want to do is bind to the (click) event, I am not aware of any (tap) events and there are none listed in the Events reference by MDN Events Reference MDN

    In your dynamic component add the following:

    let myTemplate = `<h2> Generated on the fly</h2>
                          <p>You can dynamically create your component template!</p>
                          <div class="test" (click)="test();">Test</div>`
    
        @Component({ 
          template: myTemplate 
          })
          class dynamicComponent {
            constructor(){}
    
            public test(){
              alert("I've been clicked!");
            }
        }
    

    Note I updated the StackBlitz if you want to see it in action!

    点赞 评论

相关推荐