Angular 2 组件未使用 *ngFor 和 Reactive Provider 进行更新
Angular 2 Component not updating using *ngFor with Reactive Provider
我正在尝试创建一个 Angular 2 Provider,它使用反应式方法执行所有 CRUD 操作。但是,当我第一次调用 loadAll()
时,我的 Ionic App 添加到我的列表中,但之后我调用 this.children.next(children)
时,它不会更新列表。
另外,我调用this.childrenService.createChild(child)
,没有child也更新了。
我的环境:
运行 Windows 10,运行 ionic --version 给我 2.0.0-beta .35
我的一部分package.json:
...
"@angular/common": "2.0.0-rc.4",
"@angular/compiler": "2.0.0-rc.4",
"@angular/core": "2.0.0-rc.4",
"@angular/forms": "0.2.0",
"@angular/http": "2.0.0-rc.4",
"@angular/platform-browser": "2.0.0-rc.4",
"@angular/platform-browser-dynamic": "2.0.0-rc.4",
"ionic-angular": "2.0.0-beta.11",
"ionic-native": "1.3.10",
"ionicons": "3.0.0"
...
供应商Class:
import {Injectable, EventEmitter} from '@angular/core';
import { Http } from '@angular/http';
import {Child} from "../../models/child.model";
import {Constants} from "../../models/Constants";
import 'rxjs/Rx';
import {Observable, Subject, ReplaySubject} from "rxjs";
import {IChildOperation} from "../../models/IChildOperation";
@Injectable()
export class ChildrenService {
public children: Subject<Child[]> = new Subject<Child[]>();
private updates: Subject<IChildOperation> = new Subject<IChildOperation>();
private createStream: Subject<Child> = new Subject<Child>();
constructor(private http: Http) {
this.updates
.scan((children : Child[], operation: IChildOperation) => {
return operation(children);
}, [])
.subscribe(this.children);
this.createStream
.map((newChild: Child) => {
//"Do what is in this returned method for each child given to this create object (via this.createStream.next(newChild))"
return (curChildren: Child[]) => {
console.log("Creating a new child via stream.");
return curChildren.concat(newChild);
}
})
.subscribe(this.updates);
this.loadAll();
}
createChild(newChild: Child) {
console.log("Creating child...");
this.http.post(`${Constants.CHILDREN_API}`, newChild)
.map(res=>res.json())
.subscribe((newChild: Child) => {
console.log("Child Created!");
console.log(newChild);
this.createStream.next(newChild);
});
}
loadAll() {
this.http.get(`${Constants.CHILDREN_API}`)
.map(res => res.json())
.subscribe(
(children: Child[]) => { // on success
console.log(children);
this.children.next(children);
},
(err: any) => { // on error
console.log(err);
},
() => { // on completion
}
);
}
}
主页组件
import {Component, OnInit, ChangeDetectionStrategy} from '@angular/core';
import {NavController, ModalController} from 'ionic-angular';
import {AddChildPage} from "../add-child/add-child";
import {ChildrenService} from "../../providers/children/ChildrenService";
import {Child} from "../../models/child.model";
import {AsyncPipe} from "@angular/common";
@Component({
templateUrl: 'build/pages/home/home.html',
providers: [ ChildrenService ],
pipes: [AsyncPipe],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class HomePage implements OnInit {
constructor(
private nav: NavController,
private childrenService: ChildrenService,
private modalCtrl: ModalController) {}
}
goToAddChild() {
this.nav.push(AddChildPage);
}
}
主页模板
...
<ion-list>
<ion-item *ngFor="let child of childrenService.children | async">
{{ child.name }}
</ion-item>
</ion-list>
<button secondary fab fab-fixed fab-bottom fab-right margin
(click)="goToAddChild()"><ion-icon name="person-add"></ion-icon></button>
...
添加子组件
import {Component, Output} from '@angular/core';
import {NavController, NavParams, ViewController} from 'ionic-angular';
import {ChildrenService} from "../../providers/children/ChildrenService";
import {Child} from "../../models/child.model";
@Component({
templateUrl: 'build/pages/add-child/add-child.html',
providers: [ ChildrenService ]
})
export class AddChildPage {
newChild: Child;
constructor(private nav: NavController, private childrenService: ChildrenService, public viewCtrl: ViewController) {
this.newChild = new Child({ name: "" });
}
addChild() {
this.childrenService.createChild(this.newChild);
this.dismiss();
}
dismiss() {
this.viewCtrl.dismiss();
}
}
更新: 我只用 Angular 2(没有 Ionic 2)测试了我的反应式提供者,如果我在我的提供者中改变这两件事:
export class ChildrenService {
//Changed child from Subject to ReplaySubject
public children: ReplaySubject<Child[]> = new ReplaySubject<Child[]>();
...
createChild(newChild: Child) {
console.log("Creating child...");
this.http.post(`${Constants.CHILDREN_API}`, newChild)
.map(res=>res.json())
.subscribe((newChild: Child) => {
console.log("Child Created!");
console.log(newChild);
this.createStream.next(newChild);
this.loadAll(); //This is the NEW line in createChild
});
}
当我将 children 更改为 ReplaySubject 而不是 Subject,以及在为新 child 调用的函数中调用 this.loadAll()
时,children 在 Angular 中更新,但在我的 Ionic 应用程序中没有更新。
我注意到 curChildren(在 createStream
的函数中找到)总是空的。我假设 curChildren 将是当前显示的那些。
它打印出所有预期的日志,说它创建了 child,它正确地执行了 post 请求,在我收到请求后,它说它创建了 child 通过流但没有更新。
我认为这可能是 Ionic 没有通过流正确更新或者我没有正确使用 rxjs。可能是什么?
谢谢
解决此问题需要进行三个主要更改:
- 删除 AddChildComponent - 直接在主页中添加子项
- 将
ChildrenService
中的 children
属性 更改为 Observable
而不是 Subject/ReplaySubject
- 而不是:
this.updates.scan(...).subscribe(this.children)
,将其更改为 this.children = this.updates.scan(...)
ChildrenService.ts
...
export class ChildrenService {
public children: Observable<Child[]> = new Observable<Child[]>();
...
constructor(private http: Http) {
//AsyncPipe will take care of subscribing, so just set it to this.updates
this.children = this.updates
.scan((children : Child[], operation: IChildOperation) => {
return operation(children);
}, []);
...
}
}
要点: AsyncPipe 非常适合解决这个问题。但是,我仍然不确定为什么必须删除 AddChild 组件,因为所有创建逻辑都在 ChildrenService 中。
我正在尝试创建一个 Angular 2 Provider,它使用反应式方法执行所有 CRUD 操作。但是,当我第一次调用 loadAll()
时,我的 Ionic App 添加到我的列表中,但之后我调用 this.children.next(children)
时,它不会更新列表。
另外,我调用this.childrenService.createChild(child)
,没有child也更新了。
我的环境:
运行 Windows 10,运行 ionic --version 给我 2.0.0-beta .35
我的一部分package.json:
...
"@angular/common": "2.0.0-rc.4",
"@angular/compiler": "2.0.0-rc.4",
"@angular/core": "2.0.0-rc.4",
"@angular/forms": "0.2.0",
"@angular/http": "2.0.0-rc.4",
"@angular/platform-browser": "2.0.0-rc.4",
"@angular/platform-browser-dynamic": "2.0.0-rc.4",
"ionic-angular": "2.0.0-beta.11",
"ionic-native": "1.3.10",
"ionicons": "3.0.0"
...
供应商Class:
import {Injectable, EventEmitter} from '@angular/core';
import { Http } from '@angular/http';
import {Child} from "../../models/child.model";
import {Constants} from "../../models/Constants";
import 'rxjs/Rx';
import {Observable, Subject, ReplaySubject} from "rxjs";
import {IChildOperation} from "../../models/IChildOperation";
@Injectable()
export class ChildrenService {
public children: Subject<Child[]> = new Subject<Child[]>();
private updates: Subject<IChildOperation> = new Subject<IChildOperation>();
private createStream: Subject<Child> = new Subject<Child>();
constructor(private http: Http) {
this.updates
.scan((children : Child[], operation: IChildOperation) => {
return operation(children);
}, [])
.subscribe(this.children);
this.createStream
.map((newChild: Child) => {
//"Do what is in this returned method for each child given to this create object (via this.createStream.next(newChild))"
return (curChildren: Child[]) => {
console.log("Creating a new child via stream.");
return curChildren.concat(newChild);
}
})
.subscribe(this.updates);
this.loadAll();
}
createChild(newChild: Child) {
console.log("Creating child...");
this.http.post(`${Constants.CHILDREN_API}`, newChild)
.map(res=>res.json())
.subscribe((newChild: Child) => {
console.log("Child Created!");
console.log(newChild);
this.createStream.next(newChild);
});
}
loadAll() {
this.http.get(`${Constants.CHILDREN_API}`)
.map(res => res.json())
.subscribe(
(children: Child[]) => { // on success
console.log(children);
this.children.next(children);
},
(err: any) => { // on error
console.log(err);
},
() => { // on completion
}
);
}
}
主页组件
import {Component, OnInit, ChangeDetectionStrategy} from '@angular/core';
import {NavController, ModalController} from 'ionic-angular';
import {AddChildPage} from "../add-child/add-child";
import {ChildrenService} from "../../providers/children/ChildrenService";
import {Child} from "../../models/child.model";
import {AsyncPipe} from "@angular/common";
@Component({
templateUrl: 'build/pages/home/home.html',
providers: [ ChildrenService ],
pipes: [AsyncPipe],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class HomePage implements OnInit {
constructor(
private nav: NavController,
private childrenService: ChildrenService,
private modalCtrl: ModalController) {}
}
goToAddChild() {
this.nav.push(AddChildPage);
}
}
主页模板
...
<ion-list>
<ion-item *ngFor="let child of childrenService.children | async">
{{ child.name }}
</ion-item>
</ion-list>
<button secondary fab fab-fixed fab-bottom fab-right margin
(click)="goToAddChild()"><ion-icon name="person-add"></ion-icon></button>
...
添加子组件
import {Component, Output} from '@angular/core';
import {NavController, NavParams, ViewController} from 'ionic-angular';
import {ChildrenService} from "../../providers/children/ChildrenService";
import {Child} from "../../models/child.model";
@Component({
templateUrl: 'build/pages/add-child/add-child.html',
providers: [ ChildrenService ]
})
export class AddChildPage {
newChild: Child;
constructor(private nav: NavController, private childrenService: ChildrenService, public viewCtrl: ViewController) {
this.newChild = new Child({ name: "" });
}
addChild() {
this.childrenService.createChild(this.newChild);
this.dismiss();
}
dismiss() {
this.viewCtrl.dismiss();
}
}
更新: 我只用 Angular 2(没有 Ionic 2)测试了我的反应式提供者,如果我在我的提供者中改变这两件事:
export class ChildrenService {
//Changed child from Subject to ReplaySubject
public children: ReplaySubject<Child[]> = new ReplaySubject<Child[]>();
...
createChild(newChild: Child) {
console.log("Creating child...");
this.http.post(`${Constants.CHILDREN_API}`, newChild)
.map(res=>res.json())
.subscribe((newChild: Child) => {
console.log("Child Created!");
console.log(newChild);
this.createStream.next(newChild);
this.loadAll(); //This is the NEW line in createChild
});
}
当我将 children 更改为 ReplaySubject 而不是 Subject,以及在为新 child 调用的函数中调用 this.loadAll()
时,children 在 Angular 中更新,但在我的 Ionic 应用程序中没有更新。
我注意到 curChildren(在 createStream
的函数中找到)总是空的。我假设 curChildren 将是当前显示的那些。
它打印出所有预期的日志,说它创建了 child,它正确地执行了 post 请求,在我收到请求后,它说它创建了 child 通过流但没有更新。
我认为这可能是 Ionic 没有通过流正确更新或者我没有正确使用 rxjs。可能是什么?
谢谢
解决此问题需要进行三个主要更改:
- 删除 AddChildComponent - 直接在主页中添加子项
- 将
ChildrenService
中的children
属性 更改为Observable
而不是Subject/ReplaySubject
- 而不是:
this.updates.scan(...).subscribe(this.children)
,将其更改为this.children = this.updates.scan(...)
ChildrenService.ts
...
export class ChildrenService {
public children: Observable<Child[]> = new Observable<Child[]>();
...
constructor(private http: Http) {
//AsyncPipe will take care of subscribing, so just set it to this.updates
this.children = this.updates
.scan((children : Child[], operation: IChildOperation) => {
return operation(children);
}, []);
...
}
}
要点: AsyncPipe 非常适合解决这个问题。但是,我仍然不确定为什么必须删除 AddChild 组件,因为所有创建逻辑都在 ChildrenService 中。