Angular 将多个主机绑定组合到一个装饰器中

Angular 将多个主机绑定组合到一个装饰器中,angular,typescript,decorator,Angular,Typescript,Decorator,我有一些组件使用主机绑定装饰器来显示组件选择器,以便它们占据所有可用宽度: @HostBinding('style.width.%') @HostBinding('style.height.%') private readonly SIZE = 100; @HostBinding('style.display') private readonly DISPLAY = 'block'; @HostBinding('style.box-sizing') private readonly BOX_

我有一些组件使用主机绑定装饰器来显示组件选择器,以便它们占据所有可用宽度:

@HostBinding('style.width.%')
@HostBinding('style.height.%')
private readonly SIZE = 100;

@HostBinding('style.display')
private readonly DISPLAY = 'block';

@HostBinding('style.box-sizing')
private readonly BOX_MODEL = 'border-box';
我想做的是创建一个包含所有这些内容的装饰器(在类或属性级别,我不在乎),这样我就不必每次都重写它

现在,我通过创建一个超类并让我的其他类扩展它来实现它,但是它有很多限制和不便,因此我提出了这个问题

非常感谢任何帮助,即使是文档

EDIT我也发现了,但我的问题的不同之处在于变量有一个值,我似乎无法找到如何将这些值传递给我的装饰器

编辑2来解释我的需要:我有这个组件

@Component(...)
export class MyAngularComponent extends HostBinderComponent {...}
这个呢

export class HostBinderComponent {
  @HostBinding('style.width.%')
  @HostBinding('style.height.%')
  private readonly SIZE = 100;

  @boxComponent()
  private readonly DISPLAY;

  @HostBinding('style.box-sizing')
  private readonly BOX_MODEL = 'border-box';
}
我的最终目标是删除超类,并创建类似

@Component(...)
@BoxComponent()
export class MyAngularComponent {...}

这样我就不必再在我的角度组件中使用
extends
HostBinding

应用装饰器涉及调用
\u装饰
辅助函数。此函数可以由编译器生成,也可以从自定义tslib使用。Angular将
tslib
模块用于
\uuuu decoration
功能,我们可以从那里使用
\uuu decoration
。(我们可以复制ts生成的版本,或者编写调用decorator函数的更简单版本的
\uu decoration
,但最好使用与框架相同的调用decorator的方法)

有了这个函数(在检查TS如何调用字段的装饰器之后),我们可以轻松创建自己的复合装饰器:

import { __decorate } from 'tslib';


function BoxHostBindings() {
  return function(target) {
    __decorate([
      HostBinding('style.width.%'),
      HostBinding('style.height.%'),
    ], target.prototype, "SIZE", void 0);
    __decorate([
      HostBinding('style.display'),
    ], target.prototype, "DISPLAY", void 0);
    __decorate([
      HostBinding('style.box-sizing'),
    ], target.prototype, "BOX_MODEL", void 0);
  }
}
注意:我还没有对此进行广泛测试,但它似乎有效,我希望它能起作用

用法:

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
@BoxHostBindings()
export class AppComponent {
  title = 'testapp';

  private readonly SIZE = 100;
  private readonly DISPLAY = 'block'
  private readonly BOX_MODEL = 'border-box';
}
编辑

设置属性的值有点困难。我们无法轻松访问构造函数执行,因此无法执行

一个选项是定义
原型
上的值,因为这些值是只读的,所以应该可以正常工作:

function BoxHostBindings(size: number, display: string, box_model: string) {
  return function(target) {
    target.prototype.SIZE = size;
    target.prototype.DISPLAY = display;
    target.prototype.BOX_MODEL = box_model;
    __decorate([
      HostBinding('style.width.%'),
      HostBinding('style.height.%'),
    ], target.prototype, "SIZE", void 0);
    __decorate([
      HostBinding('style.display'),
    ], target.prototype, "DISPLAY", void 0);
    __decorate([
      HostBinding('style.box-sizing'),
    ], target.prototype, "BOX_MODEL", void 0);
  }
}
我们还可以定义属性,以允许用户修改值,并将其存储在字段(“名称”)中,但如果值未定义,则返回默认值:

function BoxHostBindings(size: number, display: string, box_model: string) {
  return function(target) {
    function propHelper(name: string, defaultValue: any) {
      Object.defineProperty(target.prototype, name, {
        get: function () {
            return this['_' + name] || defaultValue;
        },
        set: function(value: any ) {
          this['_' + name] = value
        },
        enumerable: true,
        configurable: true
      });
    }
    propHelper("SIZE", size);
    propHelper("DISPLAY", display);
    propHelper("BOX_MODEL", box_model);
    __decorate([
      HostBinding('style.width.%'),
      HostBinding('style.height.%'),
    ], target.prototype, "SIZE", void 0);
    __decorate([
      HostBinding('style.display'),
    ], target.prototype, "DISPLAY", void 0);
    __decorate([
      HostBinding('style.box-sizing'),
    ], target.prototype, "BOX_MODEL", void 0);
  }
}

经过多次尝试,我终于找到了一种非常精干、易懂的方法来完成我想要实现的目标

阅读和阅读之后,我终于明白:

  • 主机绑定装饰器需要一个值(与之相反)
  • decorator依赖于decorator提供的变量名(
    key
  • 它在
    目标
    中找到键(也由decorator提供)
  • decorator返回一个只需要该目标键的函数
最后给出了非常简单的代码:

export const boxComponent: ClassDecorator = (component) => {
  const bindings = [
    { id: 'HOST_BINDINGS_WIDTH', ngStyle: 'style.width.%', value: 100},
    { id: 'HOST_BINDINGS_HEIGHT', ngStyle: 'style.height.%', value: 100},
    { id: 'HOST_BINDINGS_DISPLAY', ngStyle: 'style.display', value: 'block'},
    { id: 'HOST_BINDINGS_BOX_MODEL', ngStyle: 'style.box-sizing', value: 'border-box'},
  ];

  bindings.forEach(binding => {
    component.prototype[binding.id] = binding.value;

    const hostBindingFn = HostBinding(binding.ngStyle);
    hostBindingFn(component.prototype, binding.id);
  });
};

作为旁注,我已将变量绑定到组件的原型(即
目标
顺便说一句),以便变量不会干扰现有组件,但它应该在不接触原型的情况下工作

您希望装饰器仅应用于这些确切属性吗?有了这些精确的值?@TitianCernicova Dragomir为了理解,是的,我只是想要一个修饰符,比如(例如)
@BoxComponent
,它将所有这4个修饰符应用于一个类(具有给定的值)@trichetrichetriche(删除最后一个代码块中的扩展),在您的解决方案中,您没有给出任何值(
100,'block','border box'
)到函数,这正常吗?该值仍将在类中定义..这只是合并了装饰器..而不是字段声明..您仍需要定义字段(否则它们将为null)。您可以将它们的默认值分配给原型…这也可以正常工作。我仍然需要编写4次此
BoxHostBindings
?@trichetriche否,它将应用于类。。但您需要声明字段。我将添加一个用法示例。抱歉,我刚刚登录到SOF(昨天我回家时断开了连接),但我自己尝试了一些方法,找到了一个干净的方法。再次感谢您的回答,那个(有点)向我解释了装饰师在做什么,让我找到了解决问题的方法。我在自己的帖子上发布了一个答案,说明我做了什么,但我会将你的答案标记为已接受,因为没有:)