简体   繁体   English

为NativeScript动态生成Angular2组件

[英]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. 我需要从服务器下载内容并将该内容集成到我的Nativescript / Angular2应用程序中。 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. 我曾经使用过HtmlView和WebView,但它们有一些限制。

  1. WebView can't dynamically grow to fit the size of the content in a StackLayout for example. 例如,WebView不能动态增长以适应StackLayout中内容的大小。 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. HtmlView对HTML / CSS格式的支持非常有限,尤其是在Android上。 <font color='green'> for example doesn't work! <font color='green'>不起作用!

So I've started looking into generating Angular2 components dynamically. 因此,我开始研究动态生成Angular2组件。 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. 我一直在关注SO Answer中的讨论,并在此方面取得了一些成功。 The UI is rendered from a simple string provided at runtime, yea! UI是从运行时提供的简单字符串渲染的,是的! The sample project can be found on github, dynamic-demo . 该示例项目可以在github, dynamic-demo上找到 在此处输入图片说明

To do this I've updated the NG template project like this: 为此,我已经更新了NG模板项目,如下所示:

@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: 增强的核心是我添加的此DynamicComponent类:

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? 我是否需要担心原始StackOverflow讨论中的较大答案的问题?
  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() ? 如何设置它,以便可以将tap操作功能作为DynamicClass的输入提供,而不是像对onTap()那样将它们嵌入到类中?

Here is a solution to my question 3: 这是我的问题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. 我更新addComponent以返回动态创建的TemplateComponent类,然后像在JS中一样向对象添加一个函数。 Kinda ugly but it works. 有点丑陋,但可以。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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