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");
}
}
}
_
値がnull
に設定されていない場合は、ngOnChanges(changes) {...}
をチェックインすることもできます。 https://angular.io/docs/ts/latest/api/core/OnChanges-interface.html も参照してください
ゲッター/セッターを使用した私のソリューションを以下に示します。私見、これはすべてが1か所で行われ、このソリューションは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
を初めて使用する場合、エラーがスローされます。
注:ゲッター/セッターは、Angularのコンポーネント/サービスなどでうまく機能し、このように使用しても安全です。ただし、Angular以外の純粋なクラスでこのアプローチを使用する場合は注意してください。問題は、TypeScript ゲッター/セッターを変換する -クラスのprototype
プロパティに割り当てられる方法です。この場合、クラスのすべてのインスタンスで同じになるプロトタイププロパティを変更します。次のようなものを取得できることを意味します。
const instance1 = new ClassStub();
instance1.property = 'some value';
const instance2 = new ClassStub();
console.log(instance2.property); // 'some value'
公式の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が言及したデコレータアプローチを試しましたが、インスタンスではなくクラスに適用されるため(したがって、プロトタイプへのTSコンパイル後)、問題に遭遇しました。これは、デコレータがコンポーネントのすべてのコピーに対して1回だけ実行されること、または少なくとも複数のインスタンスで機能させる方法を見つけることができなかったことを意味していました。
セレクタオプションのドキュメント です。実際には、非常に柔軟なCSSスタイルのセレクター化(甘いWord)が可能です。
Github機能リクエストスレッド でこの推奨事項を見つけました。
次のようにできます:
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");
}
}