【问题标题】:Extending Angular 2 ngModel directive to use observables扩展 Angular 2 ngModel 指令以使用 observables
【发布时间】:2016-08-09 07:41:37
【问题描述】:

Angular 2 ngModel 指令适用于变量和函数,例如

<input [ngModel]="myVar" (ngModelChange)="myFunc($event)" />

我想用 BehaviorSubjects 代替变量和函数

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

有没有一种安全的方法来扩展 ngModel 或使用某种宏来减少模板中的重复?

<input [myNewNgModel]="mySubject" />

【问题讨论】:

  • 听起来您正在寻找类似github.com/angular/angular/issues/4062 的东西。我敢肯定这会出现在 Angular2 中,但只是在发布之后。
  • @GünterZöchbauer 很高兴知道还有其他人试图接收所有内容。主要区别在于,我尝试使用 observables 扩展/重用 ngModel,而提案侧重于将事件绑定到 observables。
  • 这是最终解决的问题吗?我很高兴写下回复,但目前我很难理解您是否真的想将这些输入用作表单的一部分?如果是这样,你有没有想过听可观察的形式?如果没有,您能否提供一些关于您如何使用 BehaviorSubject 的背景信息?当然有它的用例,但是当我们刚接触 RxJS 时,我们倾向于稍微过度使用 Subjects。
  • 这里有 2 个目标:1. 减少在处理 myObservable.subscribe((myVar) =&gt; this.myVar = myVar)myObserable | async 等可观察对象时使用的样板代码 2. 学习如何扩展 Angular 对象。我不明白装饰器如何工作以及如何扩展装饰对象。我选择 ngModel 来说明我要解决的问题,但它根本与 ngModel 无关。
  • 管道会是这里的理想解决方案吗?您将在更新属性和发出值方面保持关注点分开。否则,它将导致您必须订阅事件才能将值设置回原始属性... 工作实验:尝试创建一个简单的组件,它可以为您提供两全其美的功能,而无需比您连接更多'd 使用 ngModel 连接起来。

标签: angular rxjs observable


【解决方案1】:

我不知道您为什么不只使用反应式表单,但这是一个有趣的谜题。我创建了一个指令,将模型值更改为BehaviorSubject 的值。任何更改都会为您调用BehaviorSubject 上的.next

用法如下所示

<input type="text" [ngModel]="ngModelValue" appRxModel> 

这里是stackblitz,享受吧

【讨论】:

  • 不链接外部资源;而是在这里解释如何解决问题
  • 操作的问题已经有了解释。 “外部”解决方案是将代码转换为指令。
【解决方案2】:

我想出了一个与@Adbel 类似的方法。不确定这个的内在含义,但有一些反馈会很棒。 Stackbliz code

Your.component.ts

export class AppComponent  {
  email = new BehaviorSubject("UnwrappedMe ?");

  emailHandler(input) {
    this.email.next(input);
  }
}

Your.component.html

 <form class="mx-3">
     <input [ngModel]="email | async" 
            (ngModelChange)="emailHandler($event)" 
            name="email" type="email" 
            id="email" placeholder="Enter email">
 </form>

 <p class="mx-3"> {{ email | async }} </p>

如果您需要获取输入值的引用,请稍作改动 并且您不想进行第二次订阅(使用模板变量)。

Your.component.html

 <form class="mx-3">
     <input [ngModel]="email | async" #emailref
            (ngModelChange)="emailHandler($event)" 
            name="email" type="email" 
            id="email" placeholder="Enter email">
 </form>

 <p class="mx-3"> {{ emailref.value }} </p>

【讨论】:

  • 这会很好用!唯一的问题是它不能重复使用。想象一下,您还有 5 个输入,这意味着您需要将 5 个行为主题添加到 AppComponent 和 5 个其他方法来处理它们的更改。您可能会改进此方法以提高效率,但我的方法将所有这些方法封装在一个指令中。
【解决方案3】:

您真的想为表单中的每个输入字段创建一个可观察对象吗?我使用的模式是为整个表单的模型创建一个 observable,将其克隆为一个视图变量,然后您可以绑定到该变量,然后让表单的提交处理程序将新模型推送回服务。

user$ = this.userService.user$;

save(user: User) {
  this.userService.save(user);
}

在视图中

<form *ngIf="user$ | async | clone as user" #userForm="ngForm" (submit)="userForm.form.valid && save(user)">
  <label>
    Firstname
    <input name="firstname" [(ngModel)]="user.firstname" required>
  </label>
  <label>
    Lastname
    <input name="lastname" [(ngModel)]="user.lastname" required>
  </label>
  <button>Save</button>
</form>

克隆管道如下所示

export const clone = (obj: any) =>
  Array.isArray(obj)
    ? obj.map(item => clone(item))
    : obj instanceof Date
    ? new Date(obj.getTime())
    : obj && typeof obj === 'object'
    ? Object.getOwnPropertyNames(obj).reduce((o, prop) => {
        o[prop] = clone(obj[prop]);
        return o;
      }, {})
    : obj;

import { Pipe, PipeTransform } from '@angular/core';

import { clone } from './clone';

@Pipe({
  name: 'clone'
})
export class ClonePipe implements PipeTransform {

  transform(value: any): any {
    return clone(value);
  }
}

我在这里用我的状态管理库写了一篇关于这种模式的文章。 https://medium.com/@adrianbrand/angular-state-management-with-rxcache-468a865fc3fb

【讨论】:

  • 当值在其他地方(例如在组件打字稿中)发生变化时,这是否会更新视图值?挣扎着不更新视图。
  • 克隆未被异步管道扭曲的值的重点是,我们不会改变 observable 的内容。如果我们点击取消,那么唯一的变化就是一次性视图变量。当我们点击 save 时,由 save 方法来保存变异的对象。如果您希望在编辑时将变异值反映在您的视图中,那么您应该使用视图变量。您可以使用更改事件将值传递给 TypeScript,例如 (ngModelChange)="yourCompomentFunction(user)"
猜你喜欢
  • 2017-06-27
  • 2019-06-23
  • 2016-10-02
  • 2018-10-06
  • 2016-10-31
  • 2016-03-22
  • 2016-06-23
  • 1970-01-01
相关资源
最近更新 更多