所以,这就是我的用例:我从rest API获取一个observable,然后通过在模板中使用async来获取数据。单击时,我将获得特定用户的ID,然后使用该用户的数据填写表单。为了实现后者,我重用现有的观察值并过滤数据,然后订阅它。我想知道我的方法是否正确,因为我认为当我点击“编辑”来填写表单时,应用程序太慢了,所以我猜我在这里创建了太多的订阅或诸如此类的东西?
另外,为了便于讨论,我创建了两个可观察对象(user和users),其中一个(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
发布于 2017-11-06 05:42:29
假设您想要使用可观察性来完成此任务。虽然在Leon的答案中有更直接的方法,但为了学习可观察性,让我们这样做:-)
您正在使用异步管道在模板中订阅user observable,但也在编辑方法中订阅。edit方法中的订阅永远不会取消订阅,因此存在内存泄漏。
此外,每次单击用户并让异步管道输入模板以重新订阅时,您都会重新定义this.user observable。这会导致另一个内存泄漏,因为异步管道没有关于应该取消订阅的重写可观察对象的线索。
这并不是你应该如何组成你的可观察到的流。首先,您应该定义可观察的数据转换。
当我查看您的应用程序时,您有两个数据/事件来源:
服务
用户只能从服务中观察到。Clicks应该是真正的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,我将editClick中的users、observable和id组合为object,稍后我将使用它来过滤被单击的用户。
现在如何在onEdit方法中触发editClick?只需在每次单击时发出userId,如下所示:
edit(id) {
this.editClick.next(id);
}注意我是如何在ngOnInit中定义数据流的。一开始只有一次。没有娱乐之类的。我有两个观察点要订阅:
用于显示用户的
users用于显示详细信息的listuser -这将在每次触发editClick时发出
此外,模板中只有订阅,因此没有内存泄漏,因为async管道将为您管理unsubscribe。
发布于 2017-11-06 00:42:08
您可以通过传递整个用户而不仅仅是id来极大地简化edit方法。
<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()会导致多个订阅和多个后台调用。
如果您想多次调用它,请使用.share() rxjs操作符。这将在可观察对象周围创建一个包装器主题,该主题将在每次调用时返回相同的值。
https://stackoverflow.com/questions/47122758
复制相似问题