【问题标题】:How to combine two $firebaseArrays into one?如何将两个 $firebaseArrays 合二为一?
【发布时间】: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


【解决方案1】:

首先,我对 Angular / AngularFire 了解不多,但我认为您的问题实际上非常简单且与框架无关:Firebase 强制您将帖子存储在两个单独的集合中,所以现在您必须合并这两个集合在你拿到它们之后。

注意users_private/{uid}/messages 不是一个数组,而是一个地图/对象。这可能解释了为什么 Array.concat 不能完成这项工作。如果messages 也是 map/object -like,你可以使用算法比如

How can I merge properties of two JavaScript objects dynamically?

合并它。另一种可能是将数据转换为不可变映射:

https://facebook.github.io/immutable-js/

除了不变性(这很好)之外,您还可以使用 immutable-js 获得更好的 API;例如,OOTB 支持合并地图。

【讨论】:

    【解决方案2】:

    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 规则和源代码可以在这个gist 中找到。我仍然需要调试规则,但现在它们按预期工作。

    我也更新了我的fiddle。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-12-13
      • 2014-08-17
      • 2018-04-03
      • 1970-01-01
      • 1970-01-01
      • 2016-12-21
      • 2015-12-30
      相关资源
      最近更新 更多