在 value/key 上加入 AngularFire 路径不起作用(将用户配置文件合并到记录中)

Joining AngularFire paths on a value/key is not working (merging user profiles into records)

我正在使用 Firebase (1.0) 和 Angular (1.4) 开发应用程序。我遇到的问题是确保视图中的数据与 Firebase 同步,同时从 Firebase 中的两个 tables 获取非规范化数据:

这本书 table 看起来像这样:

books:
    -JyDpkQrUozE3L7flpo: {
    title: "title1",
    author: {-JyDpkQrUozE3L7fl1x: true}
    },
    -JyDpkQrUozE3L7flp1: {
    title: "title2",
    author: {-JyDptrrrezE3L7flKx: true}
    },
    -JyDpkQrUozE3L7flp2: {
    title: "title3",
    author: {-JyDpkQrUozE3L7f222: true}
    }
    

作者table长这样:

authors:
    -JyDpkQrUozE3L7flKx: {
        firstname: "jacques",
        lastname: "smith"
    },
    -JyDptrrrezE3L7flKx: {
        firstname: "bill",
        lastname: "halley"
    },
    -JyDpkQrUozE3L7f222: {
        firstname: "john",
        lastname: "ford"
    }
    

我的控制器是这样的:

.controller('booksController', ['$scope', '$firebaseArray', function($scope, $firebaseArray) {
     var ref = new Firebase("https://[PATH].firebaseio.com/";
     $scope.bookList = $firebaseArray(ref.child('books'));
     $scope.authorList = $firebaseArray(ref.child('authors'));
     $scope.getAuthor = function(id) {
        return $scope.authorList.$getRecord(id);
    };
});
    

我的 html 看起来像这样:

<pre>
<p ng-repeat="book in books" ng-init="author = getAuthor(book.author)">
The author of {{book.title}} is {{author.firstName}} {{author.lastName}}.
</p>
</pre>

上面的期望输出应该是:"The author of title1 is Jacques Smith. The author of title2 is Bill Halley ..."。然而,我得到的是:"The author of title 1 is. The author of title2 is..." 所以作者在我的 html returns 中一片空白。

有什么想法吗?

我看到的是您的 json 书籍数据将作者作为对象。这是传递给 $getRecord 的内容 method.The 作者的 ID 是键,而不是值。

我相信如果你像这样构建你的数据:

books: { -JyDpkQrUozE3L7flpo: { title: "title1", author: "-JyDpkQrUozE3L7fl1x" } -JyDpkQrUozE3L7flp1: { title: "title2", author: "-JyDptrrrezE3L7flKx" }, -JyDpkQrUozE3L7flp2: { title: "title3", author: "-JyDpkQrUozE3L7f222" }

应该可以的,但是好久没用firebase了

Brandon 的回答是对所提出问题的技术正确回答。我将详细说明加入这些记录的更好方法。

其实我已经回答了this exact question in a fiddle, and also provided a more sophisticated, elegant, and simpler solution of how to cache and merge user profiles into objects。我将在这里重申其工作原理的细节。

app.factory('NormalizedPosts', function($firebaseArray, userCache) {
  var PostsWithUsers = $firebaseArray.$extend({

   // override $$added to include users
   $$added: function(snap) {
       // call the super method
       var record = $firebaseArray.prototype.$$added.call(this, snap);
       userCache.$load( record.user ).$loaded(function( userData ) {
            record.userData = userData;
       });
       // return the modified record
       return record;
   }

  });
  return PostsWithUsers;
});

这里我决定使用缓存的用户列表,因为它们很可能是高度冗余的,这提供了一种使所有内容保持同步的优雅方式。这不是绝对必要的——我们可以在 $$added 中查找它们,但这会留下一些边缘情况需要处理。所以同步用户的缓存感觉就在这里。

缓存实用程序如下:

app.factory('userCache', function ($firebase) {
    return function (ref) {
        var cachedUsers = {};
        // loads one user into the local cache, you do not need to
        // wait for this to show it in your view, Angular and Firebase
        // will work out the details in the background
        cachedUsers.$load = function (id) {
            if( !cachedUsers.hasOwnProperty(id) ) {
                cachedUsers[id] = $firebaseObject(ref.child(id));
            }
            return cachedUsers[id];
        };
        // frees memory and stops listening on user objects
        // use this when you switch views in your SPA and no longer
        // need this list
        cachedUsers.$dispose = function () {
            angular.forEach(cachedUsers, function (user) {
                user.$destroy();
            });
        };
        // removes one user, note that the user does not have
        // to be cached locally for this to work
        cachedUsers.$remove = function(id) {
            delete cachedUsers[id];
            ref.child(id).remove();
        };
        return cachedUsers;
    }
});

这里是 a gist putting it all together

注意,如果我们知道当我们的控制器被销毁时,数据将不再有用,我们可以通过调用 $destroy 来清理监听器和内存。这不是绝对必要的,可能是过早的优化,但对于拥有需要跟踪数万条记录的复杂生产应用程序的用户来说,可能值得一提:

app.controller('...', function(NormalizedPosts, userCache, $scope) {
   $scope.posts = new NormalizedPosts(<firebase ref>);
   $scope.$on('$destroy', function() {
      $scope.posts.$destroy();
      userCache.$dispose();
   });
});

我意识到这是一个老问题,但我想我将我的解决方案分享给那些仍在谷歌搜索的人。

就像 Brandon 提到的那样,您不应该将作者作为对象,而只是 ID(参考)

您的数据应如下所示:

{ 
    books: {
        JyDpkQrUozE3L7flpo: {
            title: "title1",
            authorId: JyDpkQrUozE3L7fl1x
        },
        JyDpkQrUozE3L7flp1: {
            title: "title2",
            authorId: JyDptrrrezE3L7flKx
        },
        JyDpkQrUozE3L7flp2: {
            title: "title3",
            authorId: JyDpkQrUozE3L7f222
        }

    },
    authors: {
        JyDpkQrUozE3L7flKx: {
            firstname: "jacques",
            lastname: "smith"
        },
        JyDptrrrezE3L7flKx: {
            firstname: "bill",
            lastname: "halley"
        },
        JyDpkQrUozE3L7f222: {
            firstname: "john",
            lastname: "ford"
        }
    }
}

请注意,我将 author 更改为 authorId 以更明确地说明什么是 authoer 对象以及什么只是 id。现在让我们获取所有的书和作者。

app.factory('BooksWithAuthor', function($firebaseArray, $firebaseObject) {
      const books = firebase.database().ref('books')
      const authors = firebase.database().ref('authors');

      const bookList = $firebaseArray.$extend({
        $$added: function(snap) {
          const book = $firebaseArray.prototype.$$added.call(this, snap);
          book.author = $firebaseObject(authors.child(book.authorId));
          return record;
        }
      });

    return new bookList(books)
});

app.controller('BookCtrl, function(BooksWithAuthor) {
  $scope.BookList = BooksWithAuthor;
});

然后在你的HTML中做

<div ng-repeat="book in booklist">
{{ book.title }} was written by {{ book.author.firstname }} {{ book.author.lastname }}
</div>