在运行时分配 Angular 结构指令 [英] Assigning Angular structural directives during runtime

查看:39
本文介绍了在运行时分配 Angular 结构指令的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图在运行时将 *ngIf 指令从 angular 代码分配给模板.无法想出一种方法来做到这一点.view/templateref 是一种选择,还是有一种不同的方式和更简单的方式.一开始有可能吗?

更新:

代码有点乱,所以避免了.但这是 DOM 代码的大致外观以及为什么我需要动态添加内置结构指令.

<input type="text" [value]="userProvidedValue"><textarea [value]="someDynamicDOMCodefromWYSIWYG"><!-- 用户提供的代码或动态代码--></textarea>

<div><select *ngIf="fetchArraywithHttpFromuserProvidedValue"><option *ngFor="let val of fetchArraywithHttpFrom-userProvidedValue" value=""></option></选择>

<div><ng-模板><!-- 某些用户提供的代码或动态代码可能需要使用 *ngIf 或 *ngFor --><!-- *ngIf OR *ngFor 将根据操纵器函数动态添加,该操纵器函数由 fetchArraywithHttpFromuserProvidedValue 的值决定 --></ng-模板>

更新

我正在根据 userProvidedValue 值执行获取请求,获取请求的结果决定了 fetchArraywithHttpFromuserProvidedValue 数组.其次,基于从获取请求派生的 fetchArraywithHttpFromuserProvidedValue 的值,决定是在 switch 选项中显示用户提供的模板还是一组预先确定的模板.(只有用户提供的模板的一部分需要 *ngIf 指令.用户模板在 JS 中解析以获得所需的部分).用例类似于创建从用户获取结构的 HTML 设计/页面的工具.这完全是针对类似的工具,只是我不是在制作创建用户定义的 HTML 页面的工具.

请帮我解决这个问题.如果这是不可能的,那么请推荐一个替代方案,让我能够以类似的方式分配功能或在这种情况下为我提供解决方法.

更新 2

就像在下面的一个答案中指出的那样,以下所有模板都无法通过使用 elementref 或使用 ViewContainerRef + TemplateRef 进行适当的解析/编译来添加:

<input value="{{someVariableFromClass}}"/><div *ngFor="let item of items">{{item}}</div>

但是,如果模板在构建和加载应用程序之前位于 DOM 中(不是动态添加),则以下内容有效:

<!-- 使用类中的 ViewContainerRef 将模板添加到 #vcr --><div *ngFor="let item of items">{{item}}</div></ng-模板><div #vcr><!-- 使用类中的 ViewContainerRef 添加来自 #tpl 的模板 -->

目前,我正在 Angular 中尝试编译器 API,并检查 compileModuleAndAllComponentsAsync(moduleType: Type): Promise> 是否可以帮助我用例.问题似乎是我将通过将 html 作为模板分配给组件来创建一个全新的组件,然后创建一个动态模块,然后在插入到视图之前编译整个模块(我目前正在尝试的逻辑 - 不工作然而).在此之后(如果我成功了),我将尝试添加带有指令的组件模板,看看是否编译正确.欢迎任何帮助.如果我没有成功,我似乎最终可能会向手动占位符添加静态指令并添加 [innerHTML]=/safeHTML/Sanitize API.虽然不理想.如果可以,请帮助提供替代方案.

我正在使用这个示例,尽管它目前无法正常工作.

我该怎么做使用/创建动态模板以使用 Angular 2.0 编译动态组件?

http://plnkr.co/edit/wh4VJG?p=preview

解决方案

不要在 *ngIf 中调用 fetch 方法.*ngIf="..." 中的 ... 每次 angular 决定进行更改检测时都会执行,这可能是每秒数十次.您不想为自己的后端部署 DDOS.

这就是为什么你应该在那里放置一个像 isUserProvidedValueValid 这样的字段并在你的 HttpClient 调用的订阅中更新该字段:

userProvidedValue:任何;isUserProvidedValueValid: boolean = false;构造函数(私有 httpClient:HttpClient){}doFetch() {//例如通过单击按钮调用this.isUserProvidedValueValid = false;this.httpClient.get(SOME_URL).subscribe(res => {if (res) {//你可能在这里有一个复杂的检查,而不仅仅是非未定义this.isUserProvidedValueValid = true;}//你可能会考虑把它放在 if 子句和 *ngIf 中,只检查 userProvidedValue 是否不为 nullthis.userProvidedValue = res;});}

现在对于您的用户提供的代码:首先,您需要对其进行清理.您可以使用指令中的管道来完成(您不需要使用 ng-template,您可以使用普通标签的 innerHtml),就像在这个例子中一样:https://stackoverflow.com/a/39858064/4125622

 模板:`<div [innerHtml]="html | safeHtml"></div>`,

或者在上面代码中的.subscribe()之前,你可以做

//domSanitizer 也需要注入到构造函数中.map(res => this.domSanitizer.bypassSecurityTrustHtml(res));

如果您需要转换此代码,您可以添加另一个 .map()-RXJS-mapper 或另一个自定义管道 - 这取决于您喜欢哪种转换器.在转换器中,您可以使用 VanillaJS 来操作用户代码.也许 HTML-parser-plugin 或 JSON-toHTML-parser-plugin 或类似的东西可能对您有用 - 取决于您的用户提供的数据类型.

I am trying to assign *ngIf directive from angular code to the template during runtime. Not been able to figure out a way to do it. Is view/templateref an option to do it or is there a different way and an easier one. Is it possible in the first place?

Update:

The code is a little messy and jumbled, so avoided it. But here is the DOM code how it approximately looks and why I need to add inbuilt structural directives dynamically.

<div>
  <input type="text" [value]="userProvidedValue">
  <textarea [value]="someDynamicDOMCodefromWYSIWYG">
    <!-- user provided provided code or dynamic code -->
  </textarea>
</div>
<div>
  <select *ngIf="fetchArraywithHttpFromuserProvidedValue">
    <option *ngFor="let val of fetchArraywithHttpFrom-userProvidedValue" value=""></option>
  </select>
</div>
<div>
  <ng-template>
    <!-- Some User provided code or dynamic code which might need to use *ngIf OR *ngFor -->
    <!-- The *ngIf OR *ngFor will be added dynamically based on a manipulator function which is decided from the value of fetchArraywithHttpFromuserProvidedValue -->
  </ng-template>
</div>

Update

I am doing a fetch request based on userProvidedValue value and the result of the fetch request decides the fetchArraywithHttpFromuserProvidedValue array. Second, based on the value of fetchArraywithHttpFromuserProvidedValue derived from fetch request the decision is made whether to show the user provided template or a predecided set of templates in switch option. (only part of user provided template needs the *ngIf directive. The user template is parsed in JS to get the needed part). The use case is similar to a tool that creates a HTML design/page which fetches structure from a user. This is exactly for a similar tool, just that I am not making a tool that creates a user defined HTML page.

Please help me out with this. If this is not possible, then please recommend an alternative that will allow me to assign functionality similarly or get me a workaround in this situation.

Update 2

Like pointed out in one of the answers below, all of the following templates failed to be added with proper parsing/compilation with elementref or using ViewContainerRef + TemplateRef:

<input [value]="someVariableFromClass"/>

<input value="{{someVariableFromClass}}"/>

<div *ngFor="let item of items">{{item}}</div>

The following works though, if the template is in the DOM before the application is being built and loaded (not dynamic addition):

<ng-template #tpl>
  <!-- Add the template to #vcr using ViewContainerRef from the class -->
    <div *ngFor="let item of items">{{item}}</div>
</ng-template>
<div #vcr>
    <!-- Add the template from #tpl using ViewContainerRef from the class -->
</div>

Currently, I am trying out the compiler API in Angular and checking if compileModuleAndAllComponentsAsync<T>(moduleType: Type<T>): Promise<ModuleWithComponentFactories<T>> can help me in this use case. The issue seems like I will have a create a completely new component by assigning the html as a template to the component, then create a dynamic module, and then compile the whole before inserting into the view (Logic I am trying out currently - not working yet). After this (if I succeed), I will try adding the component template with a directive and see if that compiles right. Any help is welcome. It seems like I might end up by adding static directives to manual placeholders and adding [innerHTML]= / safeHTML / Sanitize API, if I dont succeed. Not ideal though. Please help with alternatives if you can.

I am using this example, though it's plunkr currently not working.

How can I use/create dynamic template to compile dynamic Component with Angular 2.0?

http://plnkr.co/edit/wh4VJG?p=preview

解决方案

You don't call a fetch method inside an *ngIf. The ... inside *ngIf="..." gets executed every time angular decides to do change-detection and that might be dozens of times per second. You don't want to deploy a DDOS for your own backend.

This is why you should put a field like isUserProvidedValueValid there and update that field in the subscription of your HttpClient-call:

userProvidedValue: any;
isUserProvidedValueValid: boolean = false;

constructor(private httpClient: HttpClient) {}

doFetch() { // called by a button-click for example
    this.isUserProvidedValueValid = false;
    this.httpClient
      .get<any>(SOME_URL)
      .subscribe(res => {
          if (res) { // you might have a complex check here, not just not-undefined
               this.isUserProvidedValueValid = true;
          }
          // you might consider putting this in the if-clause and in the *ngIf only check for userProvidedValue being not null
          this.userProvidedValue = res;
      });
}

Now for the code that your user provides: first of all, you need to sanitize it. You can do it with a pipe inside a directive (you don't need to use ng-template, you use innerHtml of a normal tag for it) like in this example: https://stackoverflow.com/a/39858064/4125622

  template: `<div [innerHtml]="html | safeHtml"></div>`,

Or before the .subscribe() in the code above, you can do

// domSanitizer needs to be injected in constructor as well
.map(res => this.domSanitizer.bypassSecurityTrustHtml(res)); 

If you need to transform this code, you can add another .map()-RXJS-mapper or another custom pipe - it's up to you which kind of transformer you prefer. In the transformer you can use VanillaJS for manipulation of the user-code. Perhaps an HTML-parser-plugin or a JSON-toHTML-parser-plugin or something similar might be useful to you - depends on the data type your user provides.

这篇关于在运行时分配 Angular 结构指令的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
前端开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆