Aurelia:对父和子自定义元素

时间:2017-06-21 15:52:44

标签: aurelia aurelia-binding

我有一个类似下面的代码,其中Project有一个时间轴。

我创建了一个名为 TimelineProperties 的类,它作为我想要从项目传递到子时间轴的属性的共享规范。

我计划在Project自定义元素中使用 TimelineProperties 类,如下所示:

import {TimelineProperties} from "./timeline";

@customElement('project')
export class Project {
   timeline: TimelineProperties = new TimelineProperties();
}
<template>
   ...
   <timeline list.bind="timeline.list" loading.bind="timeline.loading" error.bind="timeline.error" />
   ...
</template>

还在时间轴自定义元素内(共享代码):

通过这样的继承:

export class Timeline extends TimelineProperties {}

或者通过这样的构图:

export class Timeline {
  // TimelineProperties class has @bindable properties defined
  timeline: TimelineProperties = new TimelineProperties();
}

// and then use bindings from project.html like this:
<timeline timeline.list.bind="timeline.list" timeline.loading.bind="timeline.loading" timeline.error.bind="timeline.error" />

问题是我不能通过继承或组合在时间轴自定义元素中使用共享规范类 TimelineProperties

  1. 继承问题 - https://github.com/aurelia/templating/pull/507
  2. 组合问题 - 运行时异常:“错误:当已经存在具有相同名称的元素时,尝试注册元素。名称:project”
  3. 所以现在我已将 TimelineProperties 字段复制到时间轴自定义元素类中(请参阅下面的 timeline.ts 代码中的3个@bindable属性),以使其正常工作。但我想避免代码重复。

    我的问题是,是否有某种方法可以在时间轴自定义元素中使用 TimelineProperties 类将项目元素中的数据直接绑定到时间轴的时间轴属性

    以下是我的完整代码,它通过代码复制而不是使用共享的 TimelineProperties 类:

    1. project.ts - 我有一个父类自定义元素,如下所示:
    2. import {TimelineProperties} from "./timeline";
      
      @customElement('project')
      export class Project {
         timeline: TimelineProperties = new TimelineProperties();
      }
      <template>
         ...
         <timeline list.bind="timeline.list" loading.bind="timeline.loading" error.bind="timeline.error" />
         ...
      </template>

      1. timeline.ts - 和儿童自定义元素一样:
      2. import {DataLoading} from "./api";
        
        export class TimelineProperties extends DataLoading {
            @bindable list: Task[] = [];
        }
        
        @customElement('timeline')
        export class Timeline {
            @bindable list: Task[] = [];
            @bindable loading: boolean = false;
            @bindable error: any;
            ...
        }
        <template>
        ...
        </template>

        1. api.ts
        2. export class DataLoading {
              @bindable loading: boolean = false;
              @bindable error: any;
          }

          更新 - 对此解决方案感到满意

          基于Ashley Grant建议使用装饰器来初始化绑定,我已根据输入修改了我的代码,现在我很高兴。这是我的代码现在使用“extends”和装饰器来初始化子类中的绑定:

          import {DataLoading} from "./api";
          
          export class TimelineProperties extends DataLoading {
              list: Task[] = [];
          }
          
          // To ensure a compile time errors if property names are changed in TimelineProperties
          function propertyName<T>(name: keyof T){
              return name;
          }
          // Now define all bindings in a decorator instead of inside the classes
          function timelineBindings() {
            return function(target) {
              bindable(propertyName<TimelineProperties>("loading"))(target);
              bindable(propertyName<TimelineProperties>("error"))(target);
              bindable(propertyName<TimelineProperties>("list"))(target);
            }
          }
          
          @customElement('timeline')
          @timelineBindings()
          export class Timeline extends TimelineProperties {
              ...
          }
          

1 个答案:

答案 0 :(得分:0)

我不确定你为什么要为此创建如此深层的类层次结构。这些类有一个或两个属性,所以我没有看到继承树的好处是什么。你正在编写TypeScript,所以通过使用这些东西的接口可能会更好。

您的代码库中使用了TimelineProperties多少个其他地方?如果这是唯一的地方,那么实际上并没有真正的代码重复。代码重复是由于您坚持使用一堆类来不必要地使设计复杂化而引起的。

如果我从你的示例代码中误读了这个,我很抱歉,但我非常经常看到开发人员对继承感到疯狂,这样的想法会帮助他们不再重复,但最终DRY的好处远远超过了它们产生的解决方案复杂性的增加。

所以无论如何,假设实际上需要在多个地方重用这些类,我建议使用装饰器来实现这一点。

例如:https://gist.run/?id=6ad573e051f53c8e163d36dc31dc36b6

时间轴的props.js

import {bindable} from 'aurelia-framework';

export function timelineProps() {
  return function(target) {
    bindable('list')(target);
  }
}

timeline.js

import {timelineProps} from 'timeline-props';

@timelineProps()
export class Timeline {

}

timeline.html

<template>
  <ul>
    <li repeat.for="item of list">
      ${item}
    </li>
  </ul>
</template>

app.html

<template>
  <require from="./timeline"></require>

  <timeline list.bind="items"></timeline>
</template>

这是创建装饰器以处理的确切用例类型。