将自定义元素内容用作项目模板

时间:2017-04-09 12:35:59

标签: javascript aurelia aurelia-framework

我正在为我们的内部框架编写可重用的组件,以抽象出一些猴子代码。大部分场景都是使用插槽实现的,效果很好。但是,某些场景需要在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的多个问题,使其成为将其纳入框架的不良选择:

  • 它不支持像插槽那样的默认模板,所以当你只有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了? 我希望我没有把它弄得太冗长,但我想解释一下我的功能要求是什么。如果你有任何资源可以指出我正确的方向,我将非常感激!

1 个答案:

答案 0 :(得分:8)

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

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>