![](/img/trans.png)
[英]Combine two $firebaseArrays into single object using javascript or angularfire
[英]How to combine two $firebaseArrays into one?
目前,我正在使用AngularFire學習Firebase,但遇到一個問題。
我想從Firebase中查詢兩個列表,一個列表是每個經過身份驗證的用戶都可以獲取的消息的公用列表(在db中稱為messages
),而另一個列表只有創建者可以獲取(私有消息users_private/{uid}/messages
)。 私人消息的限制是通過Firebase規則完成的。
使用$firebaseArray()
獲取兩個數組都可以,但是如何將它們組合在一起,以便可以在一個ng-repeat
使用它們? 問題是與Firebase的同步必須正常工作( $add
和$remove
方法),但事實並非如此。 如果我發現了諸如array.concat(userArray)
之類的東西,那么同步。 被打破。
我可能可以做兩個ng-repeat
並復制我的HTML標記,但是我想將它放在一個地方以使其干燥。 對於兩個列表,例如按日期過濾,以后的過濾也將很困難。 私人和公共消息的模型方案是相同的。 這些對象的唯一不同之處在於可見性屬性private
或public
。
Users
集合僅存儲用戶名,並且在瀏覽器控制台中對於任何經過身份驗證的用戶都是可見的。 這是必需的,因此我可以使用其uid獲得用戶名。 因此,這些數據不是私有的,這就是為什么我在其他限制下添加了users_private
的原因。
為什么要兩個數組?
我想讓私人數據僅對創建者可見。 現在,它是一個聊天應用程序,但稍后您可以將其視為只有作者才能看到的公開帖子和私人帖子。
首先,我嘗試使用messages
收集中的屬性visibility
,但是隨后我注意到您不能使用Firebase規則限制對其的訪問。 看到規則不是過濾器
數據結構如下所示(消息,用戶,users_private是頂級文檔):
如果您還有其他想法如何創建私人和公共消息,則歡迎您提出任何想法。 但是我認為我走在正確的軌道上。
我的代碼基於angularFire seed項目 。
我也想列出私人消息的視圖如下所示:
這是我要在其中組合數組的控制器(代碼的一些詳細信息: users
是ng-route的解析值,user也可以在$rootScope.user
並且使用當前登錄的user和userSvc
在此處從uid
)獲得用戶名)和ng-repeat的標記片段:
(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>
這是我當前的user_private
規則(尚未檢查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
}*/
}
}
}
首先,我對Angular / AngularFire的了解不多,但我認為您的問題實際上非常簡單且與框架無關:Firebase迫使您將帖子存儲在兩個單獨的集合中,因此現在在提取后必須合並這兩個集合他們。
請注意, users_private/{uid}/messages
不是數組,而是實際上的映射/對象。 這可能可以解釋為什么Array.concat無法完成這項工作。 如果messages
也與地圖/對象類似,則可以使用如下算法
合並它。 另一種可能性是將數據轉換為不可變映射:
https://facebook.github.io/immutable-js/
除了不變性(很好)之外,您還可以通過immutable-js獲得更好的API; 例如,合並地圖是受支持的OOTB。
Tomas Kulich感謝您的回答,它為我指明了正確的方向,您說的對,這更是概念性或JavaScript問題,而不是框架問題。 稍后我將檢查immutable.js
,目前它正在使用javascript和我擁有的兩個框架。
在您回答之后,我重新考慮了問題並找到了有效的解決方案。
合並並不是那么容易,因為正如您提到的, firebaseArrays
是對象。 因此,所有數組合並的東西在這里都不起作用。
這就是為什么我添加的屬性privateId
我的公開消息firebaseArray
如果消息是私有的,所以我有私人入口和簡化合並的參考。
因此,每個經過身份驗證的用戶都可以看到privateId
但是只有具有正確uid
的用戶才能訪問存儲在users_private/{uid}/messages
因為它們受Firebase規則限制。
為了顯示私人消息,我為ng-repeat
創建了一個過濾器,用於檢查消息,如果有私人消息,它將用文本替換消息(如果對用戶可用,否則返回null
)。 過濾器還將publicId
添加到私人消息中,因此以后在私人/公共之間進行切換更加容易。
創建公共消息很容易。 只是將消息添加到messages/
集合中。 私人消息首先將消息添加到users_private/{uid}/messages
列表中, then
回調中將privateId
ref.key()
到公共消息中。
我當前的firebase規則和源代碼可以在本要點中找到。 我仍然需要調試規則,但目前它們正在按預期工作。
我也更新了小提琴 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.