Angular - 在组件中重用observable?

时间:2017-11-05 14:35:39

标签: javascript angular rxjs observable

所以,这是我的用例:我从rest API中获取了一个observable,然后在模板中使用async来预测数据。点击后,我获得特定用户的ID,然后用该用户的数据填写表单。为了实现后者,我重用现有的observable并过滤数据,然后订阅它。我想知道我的方法是否正确,因为当我点击“编辑”表单填充时,我认为应用程序太慢了,所以我猜这里我创建了太多的订阅或诸如此类的东西?

另外,为了论证,我创建了两个可观察对象(userusers),一个(user)获得另一个(users的“引用” ),然后它显示在模板中async,而我也订阅它并将其设置为“常规”变量。这是一个问题吗?要显示带有async的观察者并订阅它吗?

这是代码,因为我的问题可能有点令人困惑:

//our root app component
import {Component, NgModule, VERSION, OnInit, OnDestroy} from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'
import { HttpClientModule } from '@angular/common/http';
import { ReactiveFormsModule } from '@angular/forms';
import { Observable }     from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';

import 'rxjs/add/operator/map';
import 'rxjs/add/operator/find';
import 'rxjs/add/operator/takeUntil';

import { FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms';

import { UserService } from './service';

@Component({
  selector: 'my-app',
  template: `
    <div>
      <h2>Hello {{name}}</h2>
      <div>
        {{user | async | json}}
        {{selected | json}}
      </div>
      <div *ngFor="let u of users | async">
        {{u.first_name}} {{u.last_name}}
        <br />
        <img [src]="u.avatar" />
        <br />
        <br />
        <a (click)="edit(u.id)">Edit</a>
        <br />
        <br />
      </div>
      <div>
        <form [formGroup]="userForm" (ngSubmit)="onSubmit()" novalidate>
            <input type="text" formControlName="first_name" />
            <input type="text" formControlName="last_name" />
        </form>
        <p>userForm value: {{ userForm.value | json}}</p>
        <p>validity: {{ userForm.valid }}</p>
      </div>
    </div>
  `,
  styles: ['a { cursor: pointer; }']
})
export class App implements OnInit, OnDestroy {
  users: Observable<any>;
  user: Observable<any>;
  destroy$: Subject<boolean> = new Subject<boolean>();

  name:string;
  userForm: FormGroup;

  constructor(private fb: FormBuilder, private service: UserService) {
    this.name = `Angular! v${VERSION.full}`;
    this.createForm();
  }

  createForm() {
    this.userForm = this.fb.group({
      first_name: ['', {updateOn: 'blur', validators: [Validators.required, Validators.minLength(2), Validators.maxLength(10)]}],
      last_name: ['', [Validators.required, Validators.minLength(2), Validators.maxLength(20)]]
    })
  }

  edit(id) {
    this.user =  this.users.map(x => x.find(x => x.id === +id));

    this.user.takeUntil(this.destroy$).subscribe(u => {
      console.log(u);
      this.selected = u;

      this.userForm.patchValue({
        first_name: u.first_name,
        last_name: u.last_name
      });

    });
  }

  ngOnInit() {
    console.clear();
    console.log('hello world');

    this.users = this.service.all();
  }

  ngOnDestroy() {
    this.destroy$.next(true);
    this.destroy$.unsubscribe();
  }
}

@NgModule({
  imports: [ BrowserModule, HttpClientModule, ReactiveFormsModule ],
  providers: [UserService],
  declarations: [ App ],
  bootstrap: [ App ]
})
export class AppModule {}

这是链接:http://plnkr.co/edit/F0dheIdgoNNw53hNVjZm?p=preview

2 个答案:

答案 0 :(得分:0)

您可以通过传递整个用户而不仅仅是id来大大简化编辑方法。

ISO

至于多次调用<a (click)="edit(u)">Edit</a> edit(user) { this.selected = user; this.userForm.patchValue({ first_name: user.first_name, last_name: user.last_name }); } 会导致多次订阅和多次调用后端。

如果要多次调用它,请使用service.all() rxjs运算符。 这将在observable周围创建一个包装器主题,每次调用它时都会返回相同的值。

答案 1 :(得分:0)

假设你想使用observable来完成这项任务。虽然Leon的答案中有更直接的方法,但为了学习Observables,我们可以这样做: - )

您正在使用异步管道在模板中订阅用户可观察对象,但也在编辑方法中订阅。编辑方法中的订阅永远不会取消订阅,因此存在内存泄漏。

每次单击用户并让模板中的异步管道重新订阅时,您也会重新定义this.user observable。这导致另一个内存泄漏,因为异步管道没有关于应该取消订阅的覆盖可观察量的线索。

这不是你应该如何组成你的可观察流。首先,您应该定义可观察的数据转换。

当我查看您的应用程序时,您有两个数据/事件源:

  1. 来自服务的用户
  2. 点击编辑按钮
  3. 用户只能从服务中观察到。点击次数应该是Subject。因为在编辑时,您应该发出被单击(可观察)的userId,并通过显示用户详细信息(观察者行为)对此单击做出反应。

    editClick: Subject<number> = new Subject<number>;
    

    那么让我们来定义user observable的样子(ngOnInit中的所有代码):

    this.users = this.service.all().shareReplay();
    this.user = this.editClick
        .switchMapTo(this.users, (userId, users) => ({ userId, users}))
        .map(x => x.users.find(user => user.id === +x.userId))
        .do((u) => {
            console.log(u);
            this.selected = u;
    
            this.userForm.patchValue({
                first_name: u.first_name,
                last_name: u.last_name
          });
       });
    

    这里我基于editClick定义用户可观察性 - 这就是我将从后面获取id的地方。使用switchMapTo我将来自users的{​​{1}} observable和id合并到我稍后用来过滤掉点击用户的对象中。

    现在如何在editClick方法中触发editClick?只需在每次点击时发出onEdit,如下所示:

    userId

    注意我如何在edit(id) { this.editClick.next(id); } 中定义数据流。刚刚开始一次。没有娱乐或其他什么。我有两个可观察的订阅者:

    1. ngOnInit显示用户列表
    2. users显示详细信息 - 每次触发user
    3. 时都会发出此信息

      此外,模板中只有订阅,因此没有内存泄漏,因为editClick管道将为您管理async