早上好
从firebase加载列表并使用AngularFire2将其呈现到我的视图时遇到问题。
说明:
我的页面中有一个功能,可以加载帖子列表,并将帖子映射到用户数据,然后将整个列表绑定到视图:
Timeline.ts:
// Get the timeline data
getTimeline() {
console.log('Get timeline data');
this.loadingProvider.show();
this.createUserData(); // create user data if accoutn just created
this.dataProvider.getCurrentUser().valueChanges().subscribe((user) => {
this.user = <any>user;
});
this.getUserFriends().then((friends) => {
this.friends = friends;
}).then(() => {
this.dataProvider.getTimelinePost().valueChanges().subscribe((posts) => {
console.log("Change in timeline");
this.timelineData = posts.slice(0).reverse().map(p => {
return this.checkPostData(p);
});
this.loadingProvider.hide();
});
});
}
// Get the friend's user so we can display only the post who are relevants for the user
getUserFriends() {
return new Promise((resolve) => {
this.dataProvider.getFriends().valueChanges().subscribe((f) => {
resolve(f)
});
});
}
// Map post data with user's info such as avatar, etc
checkPostData(currentPost) {
let postedByFriend = this.checkIfPostedByFriend(currentPost);
if (postedByFriend || currentPost.postBy === firebase.auth().currentUser.uid) {
this.dataProvider.getUser(currentPost.postBy).valueChanges().subscribe((user) => {
currentPost.avatar = user.img;
currentPost.name = user.name
});
// Liked ?
this.dataProvider.getLike(currentPost.key).valueChanges().subscribe((likes) => {
currentPost.likes = likes.length;
currentPost.isLike = this.checkIfPostIsLiked(likes);
});
// Disliked ?
this.dataProvider.getdisLike(currentPost.key).valueChanges().subscribe((dislikes) => {
currentPost.dislikes = dislikes.length;
currentPost.isdisLike = this.checkIfPostIsDislike(dislikes);
});
// Commented ?
this.dataProvider.getComments(currentPost.key).valueChanges().subscribe((comments) => {
currentPost.comments = comments.length;
currentPost.isComment = this.checkIfPostIsCommented(comments);
});
}
return currentPost;
}
Timeline.html:
<ion-card *ngFor="let item of timelineData">
<ion-item >
<ion-avatar item-left>
<img src="{{item.avatar}}" >
</ion-avatar>
<h2>{{item.name}}</h2>
<p>{{item.dateCreated | DateFormat}}</p>
</ion-item>
<ion-card-content>
<p>{{item.postText}}</p>
</ion-card-content>
[...]
</ion-card>
DataProvider.ts:
// Get Timeline post
getTimelinePost(){
return this.angularDb.list<any>('/timeline');
}
我的问题如下:当我在时间线中添加帖子时,需要花费几秒钟的时间,因为Timeline.ts页面会重新加载整个列表(这是正常的,因为使用getTimeLinePost()进行了订阅)。我想知道是否有可能仅检测列表中的最后更改以将其添加到视图中,从而避免重新加载所有数据。
我已经尝试过SnapshotChanges()和StateChange(),但没有成功。
我注意到添加帖子时,订阅被触发两次:
更改时间轴(2)时间轴。ts:89:8
Addpost.ts代码:
pushNewPost(url) {
this.angularDb.list('timeline').push({
dateCreated: new Date().toString(),
postBy: firebase.auth().currentUser.uid,
postText: this.postText,
image: url
})
}
使用的版本: “ @angular”:“ 5.2.11”, “ angularfire2”:“ ^ 5.0.0-rc.5-next”,
对此有任何想法吗?我想念什么吗?
提前感谢
更新:
我发现了一种对渲染性能(在视图上)影响较小的替代方法,但是当在数据库上触发更改时,仍会加载所有数据:
getTimeline() {
console.log('Get timeline data');
this.loadingProvider.show();
this.createUserData();
this.dataProvider.getCurrentUser().valueChanges().subscribe((user) => {
this.user = <any>user;
});
this.getUserFriends().then((friends) => {
this.friends = friends;
}).then(() => {
this.dataProvider.getTimelinePost().snapshotChanges().map(actions =>
actions.map(a => ({ type: a.type, ...a.payload.val() }))
).subscribe(items => {
this.checkPostData(items);
this.loadingProvider.hide();
});
});
}
getUserFriends() {
return new Promise((resolve) => {
this.dataProvider.getFriends().valueChanges().subscribe((f) => {
resolve(f)
});
});
}
checkPostData(currentPost) {
// Check if whe have a child added to the timeline
if (this.timelineLoaded === true) {
currentPost.forEach(p => {
if (p.type === 'child_added') {
this.mapPost(p);
}
});
} else {
this.timelineData = [];
currentPost.map(p => {
return this.mapPost(p);
});
}
}
mapPost(currentPost) {
let postedByFriend = this.checkIfPostedByFriend(currentPost);
if (postedByFriend || currentPost.postBy === firebase.auth().currentUser.uid) {
this.dataProvider.getUser(currentPost.postBy).valueChanges().subscribe((user) => {
currentPost.avatar = user.img;
currentPost.name = user.name
});
// Liked ?
this.dataProvider.getLike(currentPost.key).valueChanges().subscribe((likes) => {
currentPost.likes = likes.length;
currentPost.isLike = this.checkIfPostIsLiked(likes);
});
// Disliked ?
this.dataProvider.getdisLike(currentPost.key).valueChanges().subscribe((dislikes) => {
currentPost.dislikes = dislikes.length;
currentPost.isdisLike = this.checkIfPostIsDislike(dislikes);
});
// Commented ?
this.dataProvider.getComments(currentPost.key).valueChanges().subscribe((comments) => {
currentPost.comments = comments.length;
currentPost.isComment = this.checkIfPostIsCommented(comments);
});
this.timelineData.unshift(currentPost);
this.timelineLoaded = true
}
return currentPost;
}
答案 0 :(得分:0)
根据我的经验,没有方便的方法可以做到这一点。
但是由于这是一个时间轴,列表更改将始终是子项添加。您可以执行以下操作:
this.timelineData = [];
this.dataProvider.getTimelinePost().valueChanges().subscribe((posts) => {
posts.forEach(p => {
if (!this.timelineData.find(data => data.dateCreated === p.dateCreated)) {
const elem = this.checkPostData(p);
// this will put it in front of the list, don't need to reverse
this.timelineData.unshift(elem);
}
})
});
顺便说一句,您不应该在订阅中进行订阅,也不要通过订阅创建承诺。
让我们考虑一下:
const obs1$ = afDb.object(ANYREF).valueChanges();
obs1$.subscribe(data => {
const obs2$ = afDb.object(data).valueChanges();
obs2$.subscribe(a => console.log(a));
});
这将起作用,但是只要obs1 $触发,obs2 $就会被“重新”订阅。
ie:如果obs1 $上有5个更改,则您将有5个obs2 $'的订阅。 每更改一个新的obs2 $,您将获得5倍的console.log ...,这是内存泄漏
修复程序可能使用take(1)运算符。这样可以强制订阅被触发一次。
const obs1$ = afDb.object(ANYREF).valueChanges();
obs1$.subscribe(data => {
const obs2$ = afDb.object(data).valueChanges();
// Rxjs 5 way :
obs2$.take(1).subscribe(a => console.log(a));
// Rxjs 6 way :
obs2$.pipe(take(1)).subscribe(a => console.log(a));
});
obs2 $将始终被触发一次,并且不会发生内存泄漏,但是obs2 $不会包含“实时数据库数据”。
更好的方法:
const obs1$ = afDb.object(ANYREF).valueChanges();
// Rxjs 5 way :
obs1$.switchMap(data => afDb.object(data).valueChanges())
.subscribe(a => console.log(a));
// Rxjs 6 way :
obs1$.pipe(switchMap(data => afDb.object(data).valueChanges()))
.subscribe(a => console.log(a));
使用这种方法,您可以将两个可观察对象“实时绑定”到数据库中,而不会泄漏。 (除了创建的那个是因为它永远不会被取消订阅)。
您可以这样做:
// ionViewWillEnter
const obs1$ = afDb.object(ANYREF).valueChanges();
this.sub = obs1$.pipe(switchMap(data => afDb.object(data).valueChanges()))
.subscribe(a => console.log(a));
// ionViewWillLeave
this.sub.unsubscribe();
如果不这样做,无论何时不在同一页面上,订阅都将保持有效。
您的代码对我来说似乎真的很复杂,但是如果您是“通过示例学习的人”,则可以参考以下文档:https://www.learnrxjs.io/