簡體   English   中英

如何將兩個$ firebaseArrays合並為一個?

[英]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標記,但是我想將它放在一個地方以使其干燥。 對於兩個列表,例如按日期過濾,以后的過濾也將很困難。 私人和公共消息的模型方案是相同的。 這些對象的唯一不同之處在於可見性屬性privatepublic

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也與地圖/對象類似,則可以使用如下算法

如何動態合並兩個JavaScript對象的屬性?

合並它。 另一種可能性是將數據轉換為不可變映射:

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM