目前,我正在使用AngularFire学习Firebase,我遇到了一个问题。
我想查询Firebase中的两个列表,一个是db中的公共消息列表(在db中称为messages
),每个经过身份验证的用户都可以获取,另一个只有创建者可以获取(私有消息{{ 1}})。私信的限制是使用Firebase规则完成的。
使用users_private/{uid}/messages
获取两个数组是有效的,但如何将它们组合在一起,以便我可以在一个$firebaseArray()
中使用它们?问题是与Firebase的同步必须有效(ng-repeat
和$add
方法),但事实并非如此。如果我找到类似$remove
同步的东西。坏了。
我可能会做两个array.concat(userArray)
并复制我的HTML标记,但我想将它放在一个地方以保持干燥。后来的过滤也很难用两个列表,例如按日期过滤。私人和公共消息的模型方案是相同的。对象仅在可见性属性ng-repeat
或private
中有所不同。
public
集合仅存储用户名,并且在浏览器控制台中可见任何经过身份验证的用户。这是必需的,所以我可以用它的uid获取用户名。所以这些数据不是私有的,这就是为什么我添加了Users
的其他限制。
为什么有两个阵列?
我想保持私人数据仅对创作者可见。现在,它是一个聊天应用程序,但稍后您可以将其视为只有作者才能看到的公开帖子和私人帖子。
首先,我尝试使用users_private
集合中的属性visibility
进行此操作,但之后我注意到您无法使用firebase规则限制对其的访问。 See rules are not filters
数据结构如下所示(messages,users,users_private是顶级文档):
如果您对如何创建私人和公共消息有另一个想法,欢迎任何想法。但我认为我走在正确的轨道上。
我的代码基于angularFire seed project。
我想列出私人消息的视图如下所示:
这是我的控制器,我想组合数组(代码的一些细节:messages
是来自ng-route的解析值,用户也可以在users
获得当前登录的用户和$rootScope.user
用于获取用户名userSvc
)和“ng-repeat”的标记片段:
uid
(function(angular) {
"use strict";
var app = angular.module('myApp.chat', [
'ngRoute', 'firebase.utils', 'firebase', 'angularSpinner'
]);
app.controller('ChatCtrl', ['$scope',
'messageList', 'privateMessageList', 'user', 'userSvc', '$q',
function($scope, messageList, privateMessageList, user, userSvc, $q) {
$scope.messages = [];
$scope.user = user;
$scope.getDisplayName = userSvc.getDisplayName;
// messageList security set with firebase rules
messageList.$loaded().then(function() {
if (user) {
$scope.messages = messageList; //comibinedlist;
privateMessageList.$loaded().then(function(list) {
$scope.showSpinner = false;
$scope.privateMessages = privateMessageList;
//$scope.displayMessages = [messageList,
// privateMessageList];
//updateDisplayMessages(messageList, privateMessages);
console.log('messageList', messageList, 'private list', privateMessageList, list);
});
}
});
$scope.addMessage = function(newMessage) {
var dataList; // storage users/messages for private or /messages
if (newMessage) {
if (newMessage.visibility === 'private') {
dataList = $scope.privateMessages;
console.log('added', dataList);
//angular.extend($scope.messages, $scope.privateMessages);
} else {
dataList = $scope.messages; //$scope.messages.$add(newMessage);
}
// add a timestamp
angular.extend(newMessage, {
timestamp: Firebase.ServerValue.TIMESTAMP
});
dataList.$add(newMessage);
}
};
$scope.removeMessage = function(msg) {
var dataList;
if (msg.visibility === 'private') {
dataList = $scope.privateMessages;
} else {
dataList = $scope.messages;
}
$scope.displayMessages.$remove(msg).then(function(ref) {
console.log('removed', msg, ref.key() === msg.$id, ref.key(), msg.$id); // true
});
// $scope.messages.$remove(msg).then(function(ref) {
// console.log('removed', msg, ref.key() === msg.$id, ref.key(), msg.$id); // true
// });
};
}
]);
app.factory('privateMessageList', [
'fbutil', '$firebaseArray', '$rootScope',
function(fbutil, $firebaseArray, $rootScope) {
var ref = fbutil.ref('users_private', $rootScope.user.uid, 'messages')
.limitToLast(10);
console.log('privMsg user', $rootScope.user.uid);
return $firebaseArray(ref);
}
]);
app.factory('messageList', ['fbutil', '$firebaseArray',
function(fbutil, $firebaseArray) {
var ref = fbutil.ref('messages').limitToLast(10);
return $firebaseArray(ref);
}
]);
app.config(['$routeProvider',
function($routeProvider) {
$routeProvider.whenAuthenticated('/chat', {
templateUrl: 'chat/chat.html',
controller: 'ChatCtrl',
//authRequired: true,
// resolve: {
// // forces the page to wait for this promise to resolve before controller is loaded
// // the controller can then inject `user` as a dependency. This could also be done
// // in the controller, but this makes things cleaner (controller doesn't need to worry
// // about auth status or timing of accessing data or displaying elements)
// user: ['userSvc', function(userSvc) {
// return userSvc.getUser();
// }]
// }
});
}
]);
})(angular);
以下是我当前的firebase规则(尚未检查<div class="list-group" id="messages" ng-show="messages.length">
<div class="list-group-item" ng-repeat="message in messages | reverse">
<strong>{{getDisplayName(message.uid) || 'anonymous'}}: </strong>{{message.text}}
<button href="#" class="btn btn-default btn-xs" ng-click="removeMessage(message)" ng-if="user.uid === message.uid"><i class="fa fa-remove"></i>
</button>
<span class="badge">{{message.visibility}}</span>
</div>
</div>
的规则):
user_private
答案 0 :(得分:0)
首先,我对Angular / AngularFire了解不多,但我认为你的问题实际上非常简单且与框架无关:Firebase强制你将帖子存储在两个独立的集合中,所以现在你必须合并这两个集合你拿到它们之后。
请注意users_private/{uid}/messages
不是数组,而是实际上是地图/对象。这可能解释了为什么Array.concat不能完成这项工作。如果messages
也是地图/对象,则可以使用
How can I merge properties of two JavaScript objects dynamically?
合并它。另一种可能性是将数据转换为不可变映射:
https://facebook.github.io/immutable-js/
除了不变性(这很好),你可以使用immutable-js获得更好的API;例如,支持合并地图OOTB。
答案 1 :(得分:0)
Tomas Kulich感谢你的回答,它指出了我正确的方向,你说它更像是概念或javascript问题,而不是框架问题。我稍后会检查immutable.js
,现在它正在使用javascript和我拥有的两个框架。
在你回答之后我已经重新考虑了我的问题并找到了一个有效的解决方案。
合并并不容易,因为你提到firebaseArrays
是对象。所以所有数组合并的东西都不能在这里工作。
这就是为什么我已将属性privateId
添加到我的公共消息firebaseArray
中,如果该消息是私有的,那么我引用了私有条目并简化了合并。
因此,每个经过身份验证的用户都可以看到privateId
,但只有具有正确uid
的用户才能访问users_private/{uid}/messages
中存储的消息,因为他们受到了firebase规则的限制。< / p>
要显示私人消息,我已为ng-repeat
创建了一个过滤器,用于检查消息,如果有私人消息,则会将消息替换为文本(如果用户可用)否则返回null
)。过滤器还会将publicId
添加到私人消息中,以便以后更容易在私人/公共之间进行拼写。
创建公共消息很容易。它只是将消息添加到messages/
集合中。
私信将首先将邮件添加到users_private/{uid}/messages
列表,并在then
回调中将privateId
与ref.key()
添加到公开邮件中。
我可以在此gist中找到我当前的firebase规则和源代码。我仍然需要调试规则,但现在他们按预期工作了。
我也更新了fiddle。