【问题标题】:Interpolate a dynamic template with ng-select使用 ng-select 插入动态模板
【发布时间】:2020-06-30 16:17:17
【问题描述】:

我想创建一个包含ng-select 组件的组件,我将向该组件传递复杂对象的数组以及要在下拉列表中显示的字段以及所选值的定义。使用ng-select,您可以指定要显示的字段 (bindLabel) 并定义一个模板来显示所选值(您可以显示来自对象或其他 HTML 标记的一个或多个字段)。我能够传递bindLabel 的值,但不知道如何插入模板。

例如,这就是我通常使用ng-select 的方式。在这种情况下,我将显示对象的两个字段和一些 HTML(将缩写字段加粗),并在下拉列表中列出缩写字段:

子组件

  items = [
    { name: 'United States', abbreviation: 'US' }, 
    { name: 'United Kingdom', abbreviation: 'UK' }, 
    { name: 'Canada', abbreviation: 'CA' }
  ];
  displayField = 'abbreviation';

子模板

  <ng-select [items]="items" 
           bindLabel="displayField"
           [(ngModel)]="model"
           name="ngselect" 
           (change)=emitModelChanged()>
      <ng-template ng-label-tmp let-item="item">
          <b>{{item.abbreviation}}</b> - {{item.name}}
      </ng-template>
  </ng-select>

要从父组件动态配置它,我将 itemsdisplayFieldtemplate 作为输入传递:

父组件

  selectedTemplate = '<b>{{item.name}}</b> - {{item.abbreviation}}';

父模板

  <child-component [model]=model 
                   [items]=items
                   [displayField]="'abbreviation'"
                   [template]=selectedTemplate
                   (update)=updateModel($event)></child-component>

子组件

  @Input() items;
  @Input() displayField; //what field shows in dropdown options
  @Input() template; // what shows for selected value in combobox
  @Input() model;
  @Output() update: EventEmitter<any> = new EventEmitter(); //emit back to parent

子组件模板

  <ng-select [items]="items" 
           bindLabel="{{displayField}}"
           [(ngModel)]="model"
           name="ngselect" 
           (change)=modelChanged()>
      <ng-template let-item="item">
          <label [innerHTML]="template"></label>
      </ng-template>
</ng-select>

当“模板”的粗体标签被解释时,数据字段没有被插值,值显示为字面意思

{{item.name}} - {{item.abbreviation}}

它是否失去了范围,因此没有将 {{item.name}} 插值到适当的值?当我只使用 {{template}} 而不是带有 innerHTML 的标签时,也会发生同样的情况。如何防止它被呈现为字符串?

我同样有一个标准的

selectField = 'item.'+this.displayField;   // (equivalent to item.abbreviation)

<select #standardSelect [(ngModel)]="model" (change)=modelChanged() >

  <!-- Also getting interpolating error here. Below renders as a string "item.abbreviation" -->
  <option *ngFor="let item of items" [ngValue]="item">{{selectField}}</option>

  <!-- This also renders as a string -->
  <!-- <option *ngFor="let item of items" [ngValue]="item" [innerHTML]="selectField"></option> -->

  <!-- Hardcoded value below works -->
  <!-- <option *ngFor="let item of items" [ngValue]="item">{{item.abbreviation}}</option> -->
</select>

Stackblitz

【问题讨论】:

  • 你试过[bindLabel]="displayField"而不是bindLabel="{{displayField}}"吗?
  • @noamyg 是的,但结果是一样的——没有插值。
  • 有动态模板选项取决于具体的用例,这可能与您的简化代码不同。请记住,AOT 在编译时而不是运行时将模板转换为 JS。 ngTemplateOutlet w/ ngTemplateOutletContext 允许从父级传递预定义的 TemplateRefs。要从带有占位符的字符串中设置innerHTML,最简单的方法是将item 传递到使用JS 模板文字的fn 中。解析来自用户的任意 HTML(非常危险!)存在许多非 NG 库。 NG 的DomSanitizer 和类似的清理方法试图提供一些针对 XSS 和其他攻击的保护。

标签: angular angular2-template string-interpolation


【解决方案1】:

您可以将模板路径添加到您的组件中并使用ng-container 渲染它,但您需要使用ContentChild 来处理模板引用。对于几个模板,使用# 命名以将它们与ContentChild 引用匹配。你可以尝试如下example

使用 TemplateRef 引用外部模板

    import { TemplateRef } from '@angular/core';
    
    @Component({
      selector: 'ng-select-accessible[displayField]',
      templateUrl: './ng-select-accessible.component.html',
      styleUrls: [ './ng-select-accessible.component.scss' ]
    })
    export class NgSelectAccessibleComponent  {
    
      @ContentChild('labelTemplate') labelTemplate: TemplateRef<any>;;
      @ContentChild('optionTemplate') optionTemplate: TemplateRef<any>;;

    }

使用 ng-container 将外部模板放置在项目模板内

    <div class='styled-select' aria-hidden=true>
      <ng-select [items]="items"
                 [placeholder]=placeholder 
                 [(ngModel)]="model"
                 name="ngselect" 
                 (change)=modelChanged()
                 attr.aria-label={{ariaLabel}}>

          <ng-template ng-label-tmp let-item="item">
            <ng-container 
              *ngTemplateOutlet="labelTemplate; context: { $implicit: item }">
            </ng-container>
          </ng-template>
    
          <ng-template ng-option-tmp let-item="item">
            <ng-container 
              *ngTemplateOutlet="optionTemplate; context: { $implicit: item }">
            </ng-container>
          </ng-template>
      </ng-select>
    </div>

在 ng-template 中定义项目视图如下

    <ng-select-accessible 
                      [model]=model 
                      [items]=items
                      [displayField]="'abbreviation'"
                      [placeholder]="'custom placeholder'" 
                      (update)=updateModel($event)
                      [ariaLabel]="'Select a number'">
      <ng-template #labelTemplate let-item>               
        <label>
          <b>{{item.name}}</b> - {{item.abbreviation}}
        </label>
      </ng-template>
      <ng-template #optionTemplate let-item>               
        <label>
          <b>{{item.name}}</b>
        </label>
      </ng-template>

    </ng-select-accessible>

我觉得这篇文章不错about ng-template

【讨论】:

  • 谢谢!我根据 rob3c 的评论开始使用 ngTemplateOutlet 方法,现在您的回答成功了!现在我只需要弄清楚如何让另一个模板为包含
  • @EricSoyke 欢迎您。我将示例更改为模板选项和标签
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-01
  • 1970-01-01
  • 2017-01-12
  • 2020-03-18
相关资源
最近更新 更多