RxJS Subject
または BehaviorSubject
をan Angularに渡すための短くて簡単な方法はありますか? =双方向バインディングの2ディレクティブ?それを行う長い方法は次のようになります:
@Component({
template: `
<input type="text" [ngModel]="subject | async" (ngModelChange)="subject.next($event)" />
`
})
私はこのようなことができるようになりたいです:
@Component({
template: `
<input type="text" [(ngModel)]="subject" />
`
})
async
パイプは一方向に過ぎないので、それだけでは不十分です。 Angular 2はこれを行うための短くて簡単な方法を提供しますか?Angular 2もRxJSを使用するため、固有の互換性があると予想しました。
これを可能にするために、新しいngModel
のようなディレクティブを作成できますか?
私は、フォームコントロールをライブラリ ng-app-state と統合するために、このようなものを検討し始めました。非常に一般的なライブラリのようなコードを作成するのが好きなタイプの場合は、次に進んでください。しかし、注意してください、これは長いです!最後に、これをテンプレートで使用できるようにする必要があります。
<input [subjectModel]="subject">
私はこの回答の前半について概念実証を行い、後半は正しいと信じていますが、この回答で書かれた実際のコードはどれもテストされていないことに注意してください。申し訳ありませんが、それは私が今提供しなければならない最高のものです。 :)
subjectModel
という独自のディレクティブを記述して、サブジェクトをフォームコンポーネントに接続できます。以下は、クリーンアップなどを除いた重要な部分です。 ControlValueAccessor
インターフェースに依存しているため、Angularには、これをすべての標準HTMLフォーム要素にフックするために必要なアダプターが含まれていますandControlValueAccessor
を使用している限り、実際に使用されているカスタムフォームコントロールで動作します(推奨される方法です)。
@Directive({ selector: '[subjectModel]' })
export class SubjectModelDirective {
private valueAccesor: ControlValueAccessor;
constructor(
@Self() @Inject(NG_VALUE_ACCESSOR)
valueAccessors: ControlValueAccessor[],
) {
this.valueAccessor = valueAccessors[0]; // <- this can be fancier
}
@Input() set subjectModel(subject: Subject) {
// <-- cleanup here if this was already set before
subject.subscribe((newValue) => {
// <-- skip if this is already the value
this.valueAccessor.writeValue(newValue);
});
this.valueAccessor.registerOnChange((newValue) => {
subject.next(newValue);
});
}
}
ここで止めると、テンプレートにこれを書き込むことができます。
<input [subjectModel]="subject" [ngDefaultControl]>
その余分 [ngDefaultControl]
は手動でangularに必要なControlValueAccessor
をディレクティブに提供するために存在します。他の種類の入力(ラジオボタンや選択など)は、別の追加のディレクティブが必要になります。これは、Angularがすべてのフォームコンポーネントに自動的に値アクセサをアタッチするのではなく、ngModel
、formControl
、またはformControlName
。
これらの余分なディレクティブの必要性を排除するためにさらに1マイル進む場合は、基本的にそれらをコードにコピーする必要がありますが、新しいsubjectModel
でアクティブになるようにセレクターを変更する必要があります。これは完全にテストされていない部分ですが、私はあなたがこれを行うことができると信じています:
// This is copy-paste-tweaked from
// https://angular.io/api/forms/DefaultValueAccessor
@Directive({
selector: 'input:not([type=checkbox])[subjectModel],textarea[subjectModel]',
Host: {
'(input)': '_handleInput($event.target.value)',
'(blur)': 'onTouched()',
'(compositionstart)': '_compositionStart()',
'(compositionend)': '_compositionEnd($event.target.value)'
},
providers: [DEFAULT_VALUE_ACCESSOR]
})
export class DefaultSubjectModelValueAccessor extends DefaultValueAccessor {}
これについての私の理解の功績は、この手法を採用する ngrx-forms に行きます。
私が考えることができる最も近いものは、FormControlを使用することです。
import { FormControl } from '@angular/forms';
@Component({
template: '<input [formControl]="control">'
})
class MyComponent {
control = new FormControl('');
constructor(){
this.control.valueChanges.subscribe(()=> console.log('tada'))
}
}
「もし山がムハンマドに来ないなら、ムハンマドは山に行かなければならない」
NgModule側ではなくRxJS側からこれに取り組みましょう。
この解決策はBehaviorSubject
のみを使用するように制限しますが、これはそのような簡単な解決策を講じることの公正な取引だと思います。
このコードをpolyfills.tsにスラップします。これにより、.value
のBehaviorSubject
からngModule
import { BehaviorSubject } from 'rxjs';
Object.defineProperty(BehaviorSubject.prototype, 'value', {
set: function(v) {
return this.next(v);
}
});
そして、このように使用してください。
<ng5-slider [(value)]="fooBehaviorSubject.value" ...
ps:RxJSのgithubリポジトリでこれについてのリクエストを開こうとしたところ、誰かがちょうど作成したことが判明しました ちょうど同じリクエストがちょうど3時間前に。 この機能を実装したい場合は、リクエストを承認してください。
あなたの質問で言ったように、これは簡単な解決策です。 (あなたがすでに提供したものより簡単なものはありません)
<input type="text" [ngModel]="subject | async" (ngModelChange)="subject.next($event)" />