在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(){
}
}
检查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
这是我使用吸气剂/装填剂的解决方案。恕我直言,这是一种更为优雅的解决方案,因为所有操作都在一个地方完成,并且该解决方案不需要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'
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
),则代码不会编译。这意味着编译时安全而不是运行时安全,这很好。令人讨厌的是,开发人员将收到的错误消息是
“
我尝试了ihor提到的装饰器方法,由于它适用于Class(因此在TS编译为原型之后)而不是实例,因此我遇到了问题;这意味着装饰器对于一个组件的所有副本仅运行一次,或者至少我找不到使它可用于多个实例的方法。my-dir
不是已知元素”,这不是非常清楚。这里是docs for the selector option。请注意,它实际上允许非常灵活的CSS样式选择器选择(甜言蜜语)。
我在Github feature request thread上找到了此建议。
constructor() {}
ngOnInit() {
if (!this.a) throw new Error();
}
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");
}
}
为什么不使用@angular/forms
库来验证您的@Input
?以下解决方案:
@input
值时]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)}`); } }