需要在指令上输入Angular 2 make @Input

问题描述 投票:34回答:6

在Angular 1中,我们可以将指令属性设为必需。我们如何使用@Input在Angular 2中做到这一点?文档没有提及它。

例如

Component({
    selector: 'my-dir',
    template: '<div></div>'
})
export class MyComponent {
  @Input() a:number; // Make this a required attribute. Throw an exception if it doesnt exist
  @Input() b:number;

  constructor(){

  }
}
angular angular2-directives
6个回答
44
投票

检查ngOnInit()(执行构造函数时尚未设置输入)该属性是否具有值。

Component({
    selector: 'my-dir',
    template: '<div></div>'
})
export class MyComponent implements OnInit, OnChanges {
    @Input() a:number; // Make this a required attribute. Throw an exception if it doesnt exist
    @Input() b:number;

    constructor(){
    }

    ngOnInit() {
       this.checkRequiredFields(this.a);
    }

    ngOnChanges(changes) {
       this.checkRequiredFields(this.a);
    }

    checkRequiredFields(input) {
       if(input === null) {
          throw new Error("Attribute 'a' is required");
       }
    }
}

如果没有将值设置为ngOnChanges(changes) {...},您也可以签入null。另请参见https://angular.io/docs/ts/latest/api/core/OnChanges-interface.html


32
投票

这是我使用吸气剂/装填剂的解决方案。恕我直言,这是一种更为优雅的解决方案,因为所有操作都在一个地方完成,并且该解决方案不需要OnInit依赖项。

解决方案1

Component({
  selector: 'my-dir',
  template: '<div></div>'
})
export class MyComponent {
  @Input()
  get a () { throw new Error('Attribute "a" is required'); }
  set a (value: number) { Object.defineProperty(this, 'a', { value, writable: true, configurable: true }); }
}

解决方案2

可以通过装饰器完成[[甚至更容易”。因此,您可以在应用中定义一次这样的装饰器:

function Required(target: object, propertyKey: string) { Object.defineProperty(target, propertyKey, { get () { throw new Error(`Attribute ${propertyKey} is required`); }, set (value) { Object.defineProperty(target, propertyKey, { value, writable: true, configurable: true }); }, }); }
然后在您的课程中,您只需要按如下所示标记您的媒体资源:

Component({ selector: 'my-dir', template: '<div></div>' }) export class MyComponent { @Input() @Required a: number; }

说明

如果定义了属性a-属性a的设置器将覆盖其自身,并且将使用传递给属性的值。否则-在组件初始化之后-首次要在类或模板中使用属性a时,将引发错误。

注:

getters / setters在Angular的组件/服务等中运行良好,可以安全地使用它们。但是在将这种方法与Angular之外的纯类一起使用时要小心。问题是打字稿converts getters/setters-它们如何分配给类的prototype属性。在这种情况下,我们对原型属性进行了突变,这对于所有类实例都是相同的。意味着我们可以得到这样的东西:const instance1 = new ClassStub(); instance1.property = 'some value'; const instance2 = new ClassStub(); console.log(instance2.property); // 'some value'

14
投票
官方的Angular方法是在组件的选择器中包括所需的属性。因此,类似:

Component({ selector: 'my-dir[a]', // <-- Check it template: '<div></div>' }) export class MyComponent { @Input() a:number; // This property is required by virtue of the selector above @Input() b:number; // This property is still optional, but could be added to the selector to require it constructor(){ } ngOnInit() { } }

这样做的好处是,如果开发人员在模板中引用组件时不包括属性(a),则代码不会编译。这意味着编译时安全而不是运行时安全,这很好。

令人讨厌的是,开发人员将收到的错误消息是

my-dir不是已知元素”,这不是非常清楚。

我尝试了ihor提到的装饰器方法,由于它适用于Class(因此在TS编译为原型之后)而不是实例,因此我遇到了问题;这意味着装饰器对于一个组件的所有副本仅运行一次,或者至少我找不到使它可用于多个实例的方法。

这里是docs for the selector option。请注意,它实际上允许非常灵活的CSS样式选择器选择(甜言蜜语)。

我在Github feature request thread上找到了此建议。


6
投票
您可以这样操作:

constructor() {} ngOnInit() { if (!this.a) throw new Error(); }


3
投票
对我来说,我必须这样做:

ngOnInit() { if(!this.hasOwnProperty('a') throw new Error("Attribute 'a' is required"); }

仅供参考,如果您需要@Output指令,请尝试以下操作:

export class MyComponent { @Output() myEvent = new EventEmitter(); // This a required event ngOnInit() { if(this.myEvent.observers.length === 0) throw new Error("Event 'myEvent' is required"); } }


0
投票

为什么不使用@angular/forms库来验证您的@Input?以下解决方案:

    快速失败(不仅是组件首次访问@input值时]
  • 允许重复使用已用于Angular表单的规则

  • 用法:

    export class MyComponent { @Input() propOne: string; @Input() propTwo: string; ngOnInit() { validateProps<MyComponent>(this, { propOne: [Validators.required, Validators.pattern('[a-zA-Z ]*')], propTwo: [Validators.required, Validators.minLength(5), myCustomRule()] }) } }

    实用功能:

    import { FormArray, FormBuilder, ValidatorFn, FormControl } from '@angular/forms'; export function validateProps<T>(cmp: T, ruleset: {[key in keyof T]?: ValidatorFn[]} ) { const toGroup = {}; Object.keys(ruleset) .forEach(key => toGroup[key] = new FormControl(cmp[key], ruleset[key])); const formGroup = new FormBuilder().group(toGroup); formGroup.updateValueAndValidity(); const validationResult = {}; Object.keys(formGroup.controls) .filter(key => formGroup.controls[key].errors) .forEach(key => validationResult[key] = formGroup.controls[key].errors); if (Object.keys(validationResult).length) { throw new Error(`Input validation failed:\n ${JSON.stringify(validationResult, null, 2)}`); } }

  • © www.soinside.com 2019 - 2024. All rights reserved.