mhe*_*ens 31 rxjs angular-ngmodel rxjs5 angular
是否有简短的方法来传递RxJS Subject或BehaviorSubjectAngular 2指令以进行双向绑定?其中很长的路要做如下:
@Component({
template: `
<input type="text" [ngModel]="subject | async" (ngModelChange)="subject.next($event)" />
`
})
Run Code Online (Sandbox Code Playgroud)
我希望能够做到这样的事情:
@Component({
template: `
<input type="text" [(ngModel)]="subject" />
`
})
Run Code Online (Sandbox Code Playgroud)
我相信async管道只是单向的,所以这还不够.Angular 2是否提供了一种简单易行的方法?Angular 2也使用RxJS,所以我预计会有一些固有的兼容性.
我是否可以创建一个新ngModel的指令来实现这一目标?
act*_*cay 10
正如您在问题中所说,这是一个简单的解决方案。我认为没有什么比你已经提供的更简单了。
<input type="text"
[ngModel]="subject | async"
(ngModelChange)="subject.next($event)" />
Run Code Online (Sandbox Code Playgroud)
Die*_*ein 10
一个可能的解决方案是BehaviorSubject 的子类:
class ModelSubject<T> extends BehaviorSubject<T> {
constructor(initialValue: T) {
super(initialValue);
}
set model(value: T) {
this.next(value);
}
get model(): T {
return this.value;
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
组件类别:
name = new ModelSubject<string>('');
Run Code Online (Sandbox Code Playgroud)
组件模板:
<input [(ngModel)]="name.model">
Run Code Online (Sandbox Code Playgroud)
我已经开始研究类似的方法来将表单控件与我的库ng-app-state集成。如果您是那种喜欢编写非常通用的、类似库的代码的人,那么请继续阅读。但请注意,这很长!最后你应该能够在你的模板中使用它:
<input [subjectModel]="subject">
Run Code Online (Sandbox Code Playgroud)
我已经对该答案的前半部分进行了概念验证,我认为后半部分是正确的,但请注意,该答案中编写的实际代码均未经过测试。抱歉,但这是我现在能提供的最好的了。:)
您可以编写自己的指令,称为subjectModel将主题连接到表单组件。以下是基本部分,不包括清理等内容。它依赖于ControlValueAccessor接口,因此 Angular 包含必要的适配器来将其连接到所有标准 HTML 表单元素,并且它可以与您在野外找到的任何自定义表单控件一起使用,只要它们使用ControlValueAccessor(这是推荐的做法) )。
@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);
});
}
}
Run Code Online (Sandbox Code Playgroud)
我们可以到此为止,您将能够在模板中编写以下内容:
<input [subjectModel]="subject" [ngDefaultControl]>
Run Code Online (Sandbox Code Playgroud)
该额外的[ngDefaultControl]存在是为了手动使 Angular 为我们的指令提供所需的内容ControlValueAccessor。其他类型的输入(例如单选按钮和选择)将需要不同的额外指令。这是因为 Angular 不会自动将值访问器附加到每个表单组件,只有那些也具有ngModel、formControl或 的组件formControlName。
如果您想付出额外的努力来消除对这些额外指令的需要,您必须将它们复制到您的代码中,但修改它们的选择器以激活您的新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 {}
Run Code Online (Sandbox Code Playgroud)
我对这一点的理解归功于ngrx-forms,它采用了这种技术。
“如果山不肯来穆罕默德,那么穆罕默德就必须去山”
让我们从 RxJS 端而不是 NgModel 端来处理这个问题。
这个解决方案限制我们只能使用BehaviorSubject's 但我认为对于拥有如此简单的解决方案来说这是一个公平的交易。
将这段代码放入您的 polyfills.ts 中。这使您能够将.valuea绑定BehaviorSubject到ngModel
import { BehaviorSubject } from 'rxjs';
Object.defineProperty(BehaviorSubject.prototype, 'value', {
set: function(v) {
return this.next(v);
}
});
Run Code Online (Sandbox Code Playgroud)
就像这样使用它。
<ng5-slider [(value)]="fooBehaviorSubject.value" ...
Run Code Online (Sandbox Code Playgroud)
小智 1
我能想到的最接近的是使用 FormControl:
import { FormControl } from '@angular/forms';
@Component({
template: '<input [formControl]="control">'
})
class MyComponent {
control = new FormControl('');
constructor(){
this.control.valueChanges.subscribe(()=> console.log('tada'))
}
}
Run Code Online (Sandbox Code Playgroud)