简体   繁体   中英

Dynamically generating Angular2 Components for NativeScript

I've have a requirement to download content from a server and integrate the content into my Nativescript/Angular2 app. The content provider would like to have the ability to format the text. I've worked with both HtmlView and WebView and they have some limitations.

  1. WebView can't dynamically grow to fit the size of the content in a StackLayout for example. And it creates a scrolled area which doesn't fit the user experience we want to provide.

  2. HtmlView has very limited support for HTML/CSS formatting particularly on Android. <font color='green'> for example doesn't work!

So I've started looking into generating Angular2 components dynamically. That is download the template from the server. I've been following the discussion in the SO Answer and have had some success with it. The UI is rendered from a simple string provided at runtime, yea! The sample project can be found on github, dynamic-demo . 在此处输入图片说明

To do this I've updated the NG template project like this:

@Component({
    selector: "my-app",
    templateUrl: "app.component.html",
})
export class AppComponent implements AfterViewInit { 
    @ViewChild('container', { read: ViewContainerRef }) container: ViewContainerRef;

    constructor(
         @Inject('DynamicComponent') private _dynamicComponent: DynamicComponent
    ){};

    ngAfterViewInit() {
        this._dynamicComponent.addComponent(
            this.container,
            ` <Label text="Hello, World!" class="h1 text-center"> </Label>
              <Button text="Tap Again" (tap)="onTap()" class="btn btn-primary btn-active"> </Button>  
            `
        )
    }

    public counter: number = 16;
      public get message(): string {
        if (this.counter > 0) {
            return this.counter + " taps left";
        } else {
            return "Hoorraaay! \nYou are ready to start building!";
        }
    }

    public onTap() {
        this.counter--;
    }
}

The heart of the enhancement is this DynamicComponent class I added:

import { Injectable, Compiler, Component, NgModule, ViewContainerRef} from '@angular/core'

@Injectable()
export class DynamicComponent {
    constructor(private compiler: Compiler) {}

    public addComponent(container: ViewContainerRef, template: string) {
        @Component({template: template})
        class TemplateComponent {

            onTap(args) {
                console.log('onTap()');
            }
        }

        @NgModule({declarations: [TemplateComponent]})
        class TemplateModule {}

        const mod = this.compiler.compileModuleAndAllComponentsSync(TemplateModule);
        const factory = mod.componentFactories.filter((comp) =>
        comp.componentType === TemplateComponent
        );
        const component = container.createComponent(factory[0]);
    }
}

I would like to get general feedback on my approach.

  1. Will this work?
  2. Do I need to worry about the issues of the larger answer in the original StackOverflow discussions?
  3. How can I set it up so I can provide that tap action functions as inputs to the DynamicClass and not imbedded them in the class like I've done with with onTap() ?

Here is a solution to my question 3:

ngAfterViewInit() {
    var component = <any> 
        this._dynamicComponent.addComponent(
            this.container, `
            <Label text="Hello, World!" class="h1 text-center"> </Label>
            <Button text="Tap Again" (tap)="onTap()" class="btn btn-primary btn-active"> </Button>  
         ` );

    component.prototype.onTap = () => {
        this.counter++;
    } 
}

I update addComponent to return the dynamically created TemplateComponent class then I add a function to the object as I would in JS. Kinda ugly but it works.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM