所以这就是我最终得到的实时主演/喜欢(在我的情况下社区)使用Firebase数据存储。这真是一团糟,我肯定错过了一些基本面。
此处,我的元素获取社区,每个社区都存储在观察到的Map community
中List communities
。它必须多次重写该列表,因为它会根据更改后的星数和用户的已加星标状态更改每个社区地图,以及其他一些乐趣:
getCommunities() {
// Since we call this method a second time after user
// signed in, clear the communities list before we recreate it.
if (communities.length > 0) { communities.clear(); }
var firebaseRoot = new db.Firebase(firebaseLocation);
var communityRef = firebaseRoot.child('/communities');
// TODO: Undo the limit of 20; https://github.com/firebase/firebase-dart/issues/8
communityRef.limit(20).onChildAdded.listen((e) {
var community = e.snapshot.val();
// snapshot.name is Firebase's ID, i.e. "the name of the Firebase location",
// so we'll add that to our local item list.
community['id'] = e.snapshot.name();
print(community['id']);
// If the user is signed in, see if they've starred this community.
if (app.user != null) {
firebaseRoot.child('/users/' + app.user.username + '/communities/' + community['id']).onValue.listen((e) {
if (e.snapshot.val() == null) {
community['userStarred'] = false;
// TODO: Add community star_count?!
} else {
community['userStarred'] = true;
}
print("${community['userStarred']}, star count: ${community['star_count']}");
// Replace the community in the observed list w/ our updated copy.
communities
..removeWhere((oldItem) => oldItem['alias'] == community['alias'])
..add(community)
..sort((m1, m2) => m1["updatedDate"].compareTo(m2["updatedDate"]));
communities = toObservable(communities.reversed.toList());
});
}
// If no updated date, use the created date.
if (community['updatedDate'] == null) {
community['updatedDate'] = community['createdDate'];
}
// Handle the case where no star count yet.
if (community['star_count'] == null) {
community['star_count'] = 0;
}
// The live-date-time element needs parsed dates.
community['updatedDate'] = DateTime.parse(community['updatedDate']);
community['createdDate'] = DateTime.parse(community['createdDate']);
// Listen for realtime changes to the star count.
communityRef.child(community['alias'] + '/star_count').onValue.listen((e) {
int newCount = e.snapshot.val();
community['star_count'] = newCount;
// Replace the community in the observed list w/ our updated copy.
// TODO: Re-writing the list each time is ridiculous!
communities
..removeWhere((oldItem) => oldItem['alias'] == community['alias'])
..add(community)
..sort((m1, m2) => m1["updatedDate"].compareTo(m2["updatedDate"]));
communities = toObservable(communities.reversed.toList());
});
// Insert each new community into the list.
communities.add(community);
// Sort the list by the item's updatedDate, then reverse it.
communities.sort((m1, m2) => m1["updatedDate"].compareTo(m2["updatedDate"]));
communities = toObservable(communities.reversed.toList());
});
}
在这里我们切换星星,当我们更新受影响社区地图中的计数时,它会再次替换观察到的社区列表几次,从而重写列表以反映:
toggleStar(Event e, var detail, Element target) {
// Don't fire the core-item's on-click, just the icon's.
e.stopPropagation();
if (app.user == null) {
app.showMessage("Kindly sign in first.", "important");
return;
}
bool isStarred = (target.classes.contains("selected"));
var community = communities.firstWhere((i) => i['id'] == target.dataset['id']);
var firebaseRoot = new db.Firebase(firebaseLocation);
var starredCommunityRef = firebaseRoot.child('/users/' + app.user.username + '/communities/' + community['id']);
var communityRef = firebaseRoot.child('/communities/' + community['id']);
if (isStarred) {
// If it's starred, time to unstar it.
community['userStarred'] = false;
starredCommunityRef.remove();
// Update the star count.
communityRef.child('/star_count').transaction((currentCount) {
if (currentCount == null || currentCount == 0) {
community['star_count'] = 0;
return 0;
} else {
community['star_count'] = currentCount - 1;
return currentCount - 1;
}
});
// Update the list of users who starred.
communityRef.child('/star_users/' + app.user.username).remove();
} else {
// If it's not starred, time to star it.
community['userStarred'] = true;
starredCommunityRef.set(true);
// Update the star count.
communityRef.child('/star_count').transaction((currentCount) {
if (currentCount == null || currentCount == 0) {
community['star_count'] = 1;
return 1;
} else {
community['star_count'] = currentCount + 1;
return currentCount + 1;
}
});
// Update the list of users who starred.
communityRef.child('/star_users/' + app.user.username).set(true);
}
// Replace the community in the observed list w/ our updated copy.
communities.removeWhere((oldItem) => oldItem['alias'] == community['alias']);
communities.add(community);
communities.sort((m1, m2) => m1["updatedDate"].compareTo(m2["updatedDate"]));
communities = toObservable(communities.reversed.toList());
print(communities);
}
还有一些其他的疯狂,我们必须在app.changes时再次获取社区列表,因为我们只在应用程序和列表初始加载后加载app.user,现在我们有了我们需要的用户打开适当的星星。所以我的附件()看起来像:
attached() {
app.pageTitle = "Communities";
getCommunities();
app.changes.listen((List<ChangeRecord> records) {
if (app.user != null) {
getCommunities();
}
});
}
在那里,似乎我可以获得星星并更新所说的每个受影响的社区地图,然后重新填充观察到的社区列表,但这是最不重要的。
完整的事情:https://gist.github.com/DaveNotik/5ccdc9e74429cf87d641
如何改进所有这些地图/列表管理,例如每当我更改社区地图时,我都要重写整个社区列表?我应该以不同的方式思考它吗?
所有这些查询Firebase怎么样?当然,这是一种更好的方法,但似乎我需要做很多事情来保持它的实时性,并且元素也会被附加和分离,所以我似乎每次都需要运行getCommunities()。除非OOP方式是对象被创建,并且每当元素被附加时它们总是被观察到?我错过了那些基本面。
此app.changes业务处理我们加载列表之前我们有app.user(这意味着我们想加载她的星星)的情况 - 有更好的方法吗?
< / LI>其他荒谬的事情?
很大的问题,我知道。感谢您帮助我在前进的过程中掌握正确的方法!
答案 0 :(得分:3)
我认为,如果您希望保持应用程序的数据与服务器数据库实时同步,有两种不同的选择方式:
1轮询(拉方法,即客户端从服务器提取数据)
应用程序民意调查即。从服务器请求更新的数据。轮询可以是自动的(例如,间隔为60秒)或由用户请求(=刷新)。较短的自动间隔会导致服务器负载过高,间隔时间过长会让您失去实时感。
2全双工(推送方法,即服务器可以将数据推送到客户端)
应用程序和服务器之间具有全双工连接,服务器能够发送数据或客户端可用数据的通知。然后客户端可以决定是否检索数据。
这种方法很现代,因为它可以将网络流量和服务器负载保持在最低水平,并提供实时更新。
firebase拥有这种更新,但我不确定它是完全dublex还是只是一种聪明的投票方式。 Websocket协议是一个真正的全双工连接,dart服务器支持它。
来自服务器的更新数据可包括:
1完整数据集
服务器基本上发送一个完整的数据集(=初始查询),服务器不知道&#34;知道&#34;关于更新数据的任何事如果您有合理的小数据集,这是最简单的方法。很多时候,你会在大数据集中拥有一个非常小的数据集,所以这种方式很有用。
2仅包含新数据的数据集
服务器可以根据修改的时间戳发送数据集,即。每次数据库中的记录发生更改时,都会保存更新的时间戳,并根据此时间戳过滤查询。换句话说,应用程序知道它上次更新数据的时间,然后请求更新的数据。
3更改记录
服务器跟踪更新的数据并将其发送到应用程序。当发生更改时,可以逐个记录地发送数据,或者服务器可以收集要发送的更大块的数据。此方法要求服务器跟踪每个连接的客户端,以便向每个客户端发送正确的数据。为客户端添加身份验证过程时。不是每个数据都可以发送给所有人,它可能变得非常复杂。
我认为最简单的方法是使用方法号2来更新数据。
最后一件事......
如何处理收到的数据?
1将所有内容处理为新的
如果应用程序收到更新的数据,它将销毁/清除所有列表和地图,并使用新数据重新创建/重新填充它们。这方面的典型问题是用户丢失页面上的当前位置或数据用户正在寻找跳跃。如果应用程序由于某种原因修改或扩展了旧数据,则所有这些修改都将丢失。如果用户请求刷新,则此方法可以正常工作。
2仅更新已更改的数据
应用程序永远不会清除初始列表或映射,它只是使用新接收的数据更新它们。通常,您将根据特定需求(例如某个视图)从查询数据构造新的组合地图。组合地图已经包含您要在特定视图中显示的所有信息(即使初始查询没有该字段的数据,也会显示默认值),您只需在其中更新新值。
如果更新的信息需要列表中的新成员,则最后将其添加。
如果更新的信息需要从列表中删除,那么使用额外字段&#34; active&#34;可能是件好事。并用它过滤列表/地图。通过过滤,您不会失去任何相关的参考。
如果您需要对数据进行排序或对其进行过滤,则应通过视图或用户请求来完成。基本上,数据存储在应用程序中并根据需要进行更新。当用户需要以特定方式查看数据时,视图应以正确的方式显示数据。这称为模型 - 控制器 - 视图,主要思想是将数据与视图分开。
对不起,这个长期答案没有回答你的任何问题,但我试图把这个挑战缩小到一个较小的块。很多时候,您可以看到这些块之间的接口,您可以使用这些接口很好地设计和组织代码。