【发布时间】:2015-12-28 10:38:31
【问题描述】:
目前,我正在使用 AngularFire 学习 Firebase,但遇到了一个问题。
我想从 Firebase 查询两个列表,一个是每个经过身份验证的用户都可以获得的消息的公共列表(在 db 中称为 messages),另一个是只有创建者才能获得的消息(私人消息 users_private/{uid}/messages) .私人消息的限制是通过 Firebase 规则完成的。
使用$firebaseArray() 获取两个数组是有效的,但我怎样才能将它们组合起来以便我可以在一个ng-repeat 中使用它们?问题是与 Firebase 的同步必须工作($add 和 $remove 方法)但它没有。如果我发现类似array.concat(userArray) 的东西,同步。坏了。
我可能会做两个ng-repeats 并复制我的HTML 标记,但我想将它放在一个地方以保持干燥。后面的过滤也很困难,有两个列表,例如按日期过滤。私人和公共消息的模型方案是相同的。对象仅在可见性属性private 或public 上有所不同。
Users 集合仅存储用户的名称,并且在任何经过身份验证的用户的浏览器控制台中可见。这是必需的,所以我可以使用它的 uid 获取用户名。所以这些数据不是私有的,这就是为什么我添加了users_private 以及其他限制。
为什么是两个数组?
我想让私有数据只对创建者可见。目前,它是一个聊天应用,但以后您可以将其视为只有作者才能看到的公开帖子和私人帖子。
首先,我尝试使用 messages 集合中的属性 visibility 来执行此操作,但后来我注意到您无法使用 firebase 规则限制对它的访问。 See rules are not filters
数据结构如下所示(messages、users、users_private 是顶级文档):
如果您对如何创建私人和公共消息有其他想法,欢迎提出任何想法。但我认为我走在正确的轨道上。
我的代码基于angularFire seed project。
我也想列出私人消息的视图如下所示:
这是我的控制器,我想在其中组合数组(代码的一些细节:users 是来自 ng-route 的解析值,用户也可以在 $rootScope.user 和当前登录的用户而userSvc 在这里用于从其uid 中获取用户的名称)和`ng-repeat´ 的标记sn-p:
(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);
<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>
这是我当前的 firebase 规则(user_private 的规则尚未检查):
{
"rules": {
// todo: add other user lists and only keep the username in users/ --> anyone with auth can get that list
// add users_profile (email etc.), users_roles
// user roles stored separately so we can keep it secure and only accessible (read & write) with uid
"users": {
".read": "auth !== null",
"$user_id": {
//".read": "auth !== null", // && ( auth.uid === $user_id )",
".write": "auth !== null && ( auth.uid === $user_id )"
}
},
"users_private": {
"$user_id": {
".read": "auth !== null", //&& ( auth.uid === $user_id )",
".write": "auth !== null && ( auth.uid === $user_id )"
}
},
"messages": {
".read": "auth != null", //"(data.child('visibility').val() === 'public')",
"$message": {
//".read": true,
/*".read": "(data.child('visibility').val() === 'public') ||
//( auth.uid == data.child('uid').val() ) ||
( root.child('users').child(auth.uid).child('admin').val() === true )", // the world can view public and author can view their message or admins (just for debugging)*/
".write": "(data.child('uid').val() == auth.uid ) || ( auth.uid == newData.child('uid').val() ) ||
(root.child('users').child(auth.uid).child('admin').val() === true)" // only owner or admin can write, user = 10, moderator = 20, admin = 999
}
/*"$uid": {
".write": "auth.uid == $uid" // only owner can write/edit to it if not new
}*/
}
}
}
【问题讨论】:
-
我为我的应用创建了一个fiddle。它没有名称解析以使其更简单。这就是显示 uid 的原因。只需添加一个 firebase 应用程序 url 并输入用于测试的凭据即可查看它的运行情况。我稍微修改了
privateMessageList以使其工作。这就是显示私人消息的原因。
标签: javascript angularjs firebase angularfire