[解決済み] Angular 2 カスタムフォーム入力
質問
ネイティブのコンポーネントと同じように動作するカスタムコンポーネントを作成するには、どうすればよいでしょうか。
<input>
タグのように動作するカスタムコンポーネントを作成するにはどうしたらよいでしょうか。私はカスタムフォーム制御をngControl、ngForm、[(ngModel)]をサポートできるようにしたいです。
私の理解では、独自のフォームコントロールがネイティブのものと同じように動作するように、いくつかのインターフェイスを実装する必要があります。
また、ngFormディレクティブがバインドするのは
<input>
タグにのみバインドされるようですが、これは正しいのでしょうか?どのように私はそれに対処することができますか?
なぜこれが必要なのかを説明します。私は、いくつかの入力要素をラップして、1つの入力として一緒に動作できるようにしたいのです。これを扱う他の方法はありますか? もう一回。このコントロールは、ネイティブのものと同じようにしたいのです。バリデーション、ngForm、ngModelの双方向バインディング、その他。
ps: Typescriptを使っています。
どのように解決するのですか?
実際には、2つのことを実施する必要があります。
-
フォームコンポーネントのロジックを提供するコンポーネントです。これは、入力は
ngModel
自体によって提供されるため、入力は必要ありません。 -
カスタム
ControlValueAccessor
との間のブリッジを実装します。ngModel
/ngControl
サンプルを見てみましょう。ある会社のタグのリストを管理するコンポーネントを実装したいと思います。このコンポーネントでは、タグの追加と削除を行うことができます。タグのリストが空でないことを確認するために、バリデーションを追加したいと思います。以下のようにコンポーネントに定義します。
(...)
import {TagsComponent} from './app.tags.ngform';
import {TagsValueAccessor} from './app.tags.ngform.accessor';
function notEmpty(control) {
if(control.value == null || control.value.length===0) {
return {
notEmpty: true
}
}
return null;
}
@Component({
selector: 'company-details',
directives: [ FormFieldComponent, TagsComponent, TagsValueAccessor ],
template: `
<form [ngFormModel]="companyForm">
Name: <input [(ngModel)]="company.name"
[ngFormControl]="companyForm.controls.name"/>
Tags: <tags [(ngModel)]="company.tags"
[ngFormControl]="companyForm.controls.tags"></tags>
</form>
`
})
export class DetailsComponent implements OnInit {
constructor(_builder:FormBuilder) {
this.company = new Company('companyid',
'some name', [ 'tag1', 'tag2' ]);
this.companyForm = _builder.group({
name: ['', Validators.required],
tags: ['', notEmpty]
});
}
}
は
TagsComponent
コンポーネントで、要素を追加したり削除したりするロジックを定義しています。
tags
リストに要素を追加したり削除したりするロジックを定義します。
@Component({
selector: 'tags',
template: `
<div *ngIf="tags">
<span *ngFor="#tag of tags" style="font-size:14px"
class="label label-default" (click)="removeTag(tag)">
{{label}} <span class="glyphicon glyphicon-remove"
aria- hidden="true"></span>
</span>
<span> | </span>
<span style="display:inline-block;">
<input [(ngModel)]="tagToAdd"
style="width: 50px; font-size: 14px;" class="custom"/>
<em class="glyphicon glyphicon-ok" aria-hidden="true"
(click)="addTag(tagToAdd)"></em>
</span>
</div>
`
})
export class TagsComponent {
@Output()
tagsChange: EventEmitter;
constructor() {
this.tagsChange = new EventEmitter();
}
setValue(value) {
this.tags = value;
}
removeLabel(tag:string) {
var index = this.tags.indexOf(tag, 0);
if (index !== -1) {
this.tags.splice(index, 1);
this.tagsChange.emit(this.tags);
}
}
addLabel(label:string) {
this.tags.push(this.tagToAdd);
this.tagsChange.emit(this.tags);
this.tagToAdd = '';
}
}
見ての通り、このコンポーネントには入力はなく、代わりに
setValue
があります (名前はここでは重要ではありません)。これは後で
ngModel
からコンポーネントに値を提供するために使用します。このコンポーネントは、コンポーネントの状態(タグリスト)が更新されたときに通知するイベントを定義しています。
では、このコンポーネントと
ngModel
/
ngControl
. を実装したディレクティブに相当します。
ControlValueAccessor
インタフェースを実装したディレクティブに対応します。この値へのアクセスのために、プロバイダを定義しなければなりません。
NG_VALUE_ACCESSOR
トークンに対応するプロバイダを定義する必要があります (忘れずに
forwardRef
ディレクティブの後に定義されているので)。
このディレクティブは、イベントリスナーを
tagsChange
イベントのイベントリスナーを付けます (すなわち、ディレクティブが付けられているコンポーネント、すなわち
TagsComponent
). そのため
onChange
メソッドは、イベントが発生したときに呼び出されます。このメソッドはAngular2が登録したものに相当します。こうすることで、変更を認識し、それに応じて関連するフォームコントロールを更新します。
このメソッドは
writeValue
で束縛された値が呼び出されます。
ngForm
に束縛された値が更新されたときに呼び出されます。にアタッチされたコンポーネント(つまりTagsComponent)を注入した後、この値を渡すためにそれを呼び出すことができるようになります(前の
setValue
メソッドを参照)。
を提供することを忘れないでください。
CUSTOM_VALUE_ACCESSOR
をディレクティブのバインディングで指定することを忘れないでください。
以下は、カスタムの完全なコードです。
ControlValueAccessor
:
import {TagsComponent} from './app.tags.ngform';
const CUSTOM_VALUE_ACCESSOR = CONST_EXPR(new Provider(
NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => TagsValueAccessor), multi: true}));
@Directive({
selector: 'tags',
host: {'(tagsChange)': 'onChange($event)'},
providers: [CUSTOM_VALUE_ACCESSOR]
})
export class TagsValueAccessor implements ControlValueAccessor {
onChange = (_) => {};
onTouched = () => {};
constructor(private host: TagsComponent) { }
writeValue(value: any): void {
this.host.setValue(value);
}
registerOnChange(fn: (_: any) => void): void { this.onChange = fn; }
registerOnTouched(fn: () => void): void { this.onTouched = fn; }
}
この方法では、すべての
tags
を削除すると
valid
の属性は
companyForm.controls.tags
コントロールは
false
に自動的に変更されます。
詳しくはこちらの記事("NgModel対応コンポーネント"項)をご覧ください。
関連
-
[解決済み] ngModel' は 'input' の既知のプロパティではないため、バインドできません。
-
[解決済み] formGroup' は 'form' の既知のプロパティではないため、バインドできません。
-
[解決済み] Angular HTMLバインディング
-
[解決済み] Angularで@Input()の値が変更されたときに検出する方法は?
-
[解決済み] Angular 2でアプリ起動時にサービスを実行する方法
-
[解決済み] 文字列ユニオンから文字列配列へ
-
[解決済み] Visual Studio Code - インポート引用符の設定を調整する
-
[解決済み] Typescript で Enum を制限付きキータイプとして使用する
-
[解決済み] Typescriptで<T>は何を意味するのですか?
-
[解決済み] TypeScriptで単一のプロパティをオプションにする
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] ts ES5/ES3の非同期関数やメソッドには、「Promise」コンストラクタが必要です。
-
[解決済み] Angular 2でアプリ起動時にサービスを実行する方法
-
[解決済み] TypeScriptでObject.keysがkeyof型を返さないのはなぜですか?
-
[解決済み] モジュールと名前空間 - Import vs Require Typescript
-
[解決済み] Visual Studio コード自動インポート
-
[解決済み] typescriptでmoment.jsをインポートするには?
-
[解決済み] Promiseの型はどのようにアンラップするのですか?
-
[解決済み] Typescript で Enum を制限付きキータイプとして使用する
-
[解決済み] エラー Typescript Feature 1.5。現在の言語レベルは1.4です。
-
[解決済み] TSにおける文字列列列挙型と文字列リテラル型の相違点