如何双向绑定我自己的RxJS主题为[(ngModel)]?

时间:2016-07-29 15:13:03

标签: angular rxjs angular-ngmodel rxjs5

是否有一种简短的方法可以将RxJS SubjectBehaviorSubject传递给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 - 类指令来实现这一目标?

6 个答案:

答案 0 :(得分:2)

我能想到的最接近的是使用FormControl:

import { FormControl } from '@angular/forms';

@Component({
    template: '<input [formControl]="control">'
})
class MyComponent {
    control = new FormControl('');
    constructor(){
        this.control.valueChanges.subscribe(()=> console.log('tada'))
    }
}

答案 1 :(得分:2)

我开始研究这样的事情,将表单控件与我的库ng-app-state集成。如果您喜欢制作非常通用的类库代码,那么请继续阅读。但要注意,这很长!最后,您应该可以在模板中使用它:

<input [subjectModel]="subject">

我已经为这个答案的前半部分做了一个概念验证,而我认为下半部分是正确的,但是请注意,本答案中写的实际代码都没有经过测试。对不起,但这是我现在提供的最好的。 :)

您可以编写自己的名为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);
        });
    }
}

我们可以在这里停下来,你可以在模板中写下这个:

<input [subjectModel]="subject" [ngDefaultControl]>

存在额外的[ngDefaultControl]以手动导致angular为我们的指令提供所需的ControlValueAccessor。其他类型的输入(如单选按钮和选择)需要不同的额外指令。这是因为Angular不会自动将值访问器附加到每个表单组件,只有那些也具有ngModelformControlformControlName的组件。

如果您想加倍努力以消除对这些额外指令的需求,您必须将它们复制到您的代码中,但修改其选择器以激活您的新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,它采用了这种技术。

答案 2 :(得分:2)

我尝试了这个,它奏效了

$this->db->select('a.tgl_kejadian,b.nama_bencana,a.alamat,c.nama_kelurahan,d.nama_kecamatan,a.kerugian,a.keterangan')
          ->from('data_kejadian a , data_bencana b, kelurahan c, kecamatan d')
          ->where('a.id_bencana = b.id_bencana')
          ->where('a.id_daerah = c.id_kelurahan')
          ->where('c.id_kecamatan = d.id_kecamatan')
          ->where_between('a.tgl_kejadian', ['$tanggal_awal', '$tanggal_akhir']);

enter image description here

我不确定这是否违反任何规则,是否有错误,但似乎对我有用。我希望Angular像构建表单一样拥有一个内置的主题指令。

希望这会有所帮助

答案 3 :(得分:2)

正如您在问题中所说,这是一个简单的解决方案。 (没有比您已经提供的更简单的了)

<input type="text" [ngModel]="subject | async" (ngModelChange)="subject.next($event)" />

答案 4 :(得分:2)

可能的解决方案是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;
    }
}

用法:

组件类:

name = new ModelSubject<string>('');

组件模板:

<input [(ngModel)]="firstName.model">

答案 5 :(得分:0)

  

'如果山不来穆罕默德,那么穆罕默德必须去山上'

让我们从RxJS端而不是NgModule端进行处理。

此解决方案将我们限制为仅使用users 813-123-4567 | 1 users2 813-123-4567 | 1 813-123-4567 | 0 ,但我认为采用这种简单解决方案是一项公平的交易。

将这段代码拍入您的polyfills.ts中。这使您可以将BehaviorSubject的{​​{1}}绑定到.value

BehaviorSubject

然后像这样使用它。

ngModule

ps:我正准备在RxJS的github存储库中打开一个与此有关的请求,但结果发现有人刚刚提出了the exact same request just 3 hours ago.如果您希望实现此功能,请认可该请求。