web-dev-qa-db-ja.com

Angular 2ディレクティブで@Inputを要求する

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(){

  }
}
32
Simon Trewhella

属性に値があるかどうかを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 も参照してください

44

ゲッター/セッターを使用した私のソリューションを以下に示します。私見、これはすべてが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'
29
Ihor

公式の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機能リクエストスレッド でこの推奨事項を見つけました。

14
Ryan Miglavs

次のようにできます:

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

私にとっては、次のようにしなければなりませんでした。

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");
    }
}
3
Ulfius