【问题标题】:Using custom element content as item template使用自定义元素内容作为项目模板
【发布时间】:2017-04-09 12:35:59
【问题描述】:

我正在为我们的内部框架编写可重用组件,以抽象出一些猴子代码。大多数场景都是用插槽实现的,效果很好。但是,有些场景需要在 for 循环中渲染模板,不幸的是,那里不支持插槽。

我想出了以下(工作)代码:

<template>
  <div class="form-group">
    <label for.bind="titleSafe" class="control-label">{title}</label>
    <select id.bind="titleSafe" value.bind="value" class="form-control">
      <option repeat.for="item of itemsSource" >
        <template replaceable part="item-template" containerless>${item}</template>
      </option>
    </select>
  </div>
</template>

IMO,此代码存在多个问题,使其不适合将其包含在框架中:

  • 它不支持像 slot 那样的默认模板,所以当你只有 1 个可替换部分时,语法是不必要的冗长
  • 在我的项目中必须使用 2 个不同的模板系统(插槽 + 替换部分)似乎非常违反直觉,并且肯定会在我的开发团队中造成混乱/错误
  • 当您在我上面提供的示例中使用模板部件时,您需要知道我在 for 循环中将“item”声明为迭代器,以便正确构造您的模板

因此我去寻找替代品。经过一番研究,我想出了这样的事情:

<template>
  <div class="form-group">
    <label for.bind="titleSafe" class="control-label">{title}</label>
    <select id.bind="titleSafe" value.bind="value" class="form-control">
      <option repeat.for="item of itemsSource" >
        <!-- I want to insert my custom element here -->
      </option>
    </select>
  </div>
  <slot></slot>
</template>

以上是我的select-item 自定义元素。然后我还会为可重复项的模板创建另一个自定义元素,例如select-item-template,然后我会像这样将两者一起使用:

<select-item title="myTitle" items-source="myItems">
  <select-item-template><span>${myItemsProperty}</span></select-item-template>
</select-item>

这种方法的优势在于您可以使用一个默认插槽创建复杂的“根”自定义元素。在这个插槽中,您可以定义多个自定义“子”元素,根元素在初始化时可以搜索这些元素(我知道您可以使用 @child 和 @children 装饰器来执行此操作,以便覆盖该部分)。我对如何在我的根自定义元素中使用这些自定义子元素的内容有点迷茫。我将如何在上面的示例中使用我的span 元素并准备它的内容以在转发器中呈现?是否可以将重复的item 设置为模板的数据源,这样我就不必在模板中指定item? 我希望我没有把它写得太冗长,但我想解释一下我的功能需求是什么。如果您有任何资源可以为我指明正确的方向,我将不胜感激!

【问题讨论】:

  • 是的,中继器和其他模板控制器内部无法支持插槽是一个真正的 PITA……不幸的是,标准的编写方式需要它,这就是我们有 @ 987654329@ 的东西是一种解决 slot 失败的方法。

标签: javascript aurelia


【解决方案1】:

使用processContent 属性将元素内容转换为部件替换。组件仍将在内部使用 replace-part,但组件的使用者不会接触到此实现细节。

https://gist.run?id=2686e551dc3b93c494fa9cc8a2aace09

picker.html

<template>
  <label repeat.for="item of itemsSource" style="display: block">
    <input type="radio" value.bind="item" checked.bind="value">
    <template replaceable part="item-template">${item}</template>
  </label>
</template>

picker.js

import {bindable, processContent} from 'aurelia-templating';
import {bindingMode} from 'aurelia-binding';
import {FEATURE} from 'aurelia-pal';

@processContent(makePartReplacementFromContent)
export class Picker {
  @bindable itemsSource = null;
  @bindable({ defaultBindingMode: bindingMode.twoWay }) value = null;
}


function makePartReplacementFromContent(viewCompiler, viewResources, element, behaviorInstruction) {
  const content = element.firstElementChild;
  if (content) {
    // create the <template>
    const template = document.createElement('template');

    // support browsers that do not have a real <template> element implementation (IE)
    FEATURE.ensureHTMLTemplateElement(template);

    // indicate the part this <template> replaces.
    template.setAttribute('replace-part', 'item-template');

    // replace the element's content with the <template>
    element.insertBefore(template, content);
    element.removeChild(content);
    template.content.appendChild(content);

    return true;
  }
}

用法

<template>
  <require from="picker"></require>

  <h1>Default Item Template</h1>

  <picker items-source.bind="colors" value.bind="color"></picker>

  <h1>Custom Item Template</h1>

  <picker items-source.bind="colors" value.bind="color">
    <em css="color: ${item}">
      ${item}
    </em>
  </picker>
</template>

【讨论】:

  • 这让我朝着正确的方向前进,感谢您提供的详尽示例!现在我使用 processcontent 函数来扫描自定义子元素并附加替换部分位,只要它在内部使用我不在乎。此示例中唯一缺少的是“item”位,我通过在模板中使用 with.bind="item" 绑定解决了这个问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多