关于数组:在 angularfire 1.0.0 中通过唯一的 firebase id 获取项目

Fetching item by unique firebase id in angularfire 1.0.0

我无法使用 angularfire 1.0.0 从我的 Firebase 中获取一件独特的物品。澄清一下,我希望我的应用能够获取给定唯一 Firebase id 的帖子,例如"-JkZwz-tyYoRLoRqlI_I"。它在应用程序中导航时有效,例如单击指向特定帖子的链接,但不单击刷新。我的猜测是它与同步有关。现在它在获取所有帖子并在 ng-repeat 中使用它时工作。这是导航到页面时为什么它适用于一个项目的线索。这可能不难,因为这应该是一个非常标准的操作,但我无法让它工作。我到处搜索,但实际上没有这方面的指南。在 API 中,它们指的是 $getRecord(key)

Returns the record from the array for the given key. If the key is not
found, returns null. This method utilizes $indexFor(key) to find the
appropriate record.

但这并没有按预期工作。还是我错过了什么?

它适用于 ng-repeat,如下所示:

1
2
3
      {{postt.title}}
      {{postt.timestamp}}
      {{postt.content}}

但不适用于像这样的独特物品:

1
2
3
   {{post.title}}
   {{post.timestamp}}
   {{post.content}}

这是服务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
'use strict';

angular.module('app.module.blog.post')

.factory("PostService", ["$firebaseArray","FIREBASE_URL", function($firebaseArray, FIREBASE_URL) {

    var ref = new Firebase(FIREBASE_URL +"posts");
    var posts = $firebaseArray(ref);

    return {
        all: posts, // ng-repeat on this works fine

        last: function(nr) {
            var query = ref.orderByChild("timestamp").limitToLast(nr);
            return $firebaseArray(query); // ng-repeat on this work fine to
        },
        create: function (post) {
            return posts.$add(post);
        },
        get: function (postId) {
            console.log(postId); // This is -JkZwz-tyYoRLoRqlI_I
            var post = posts.$getRecord(postId);
            console.log(post); // This print null
            return post;
        },
        delete: function (post) {
            return posts.$remove(post);
        }
    };
}]);

正如评论在get函数中所说,postId在那里并且posts也被设置了,但是post是null。

这是控制器

1
2
3
4
5
6
7
8
9
10
11
12
13
'use strict';

angular.module('app.module.blog.post', [])

.controller('PostCtrl', ['$scope', '$routeParams', 'PostService', function($scope, $routeParams, PostService) {

    // This returns e.g. postId"-JkZwz-tyYoRLoRqlI_I"
    console.log($routeParams.postId);

    $scope.post = PostService.get($routeParams.postId);    
    $scope.posts = PostService.all; // Illustrates the example not actually in this controller otherwise

}]);

这是关于 firebase 数据库中内容的示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<myfirebase>
 posts
 -JkUnVsGnCqbAxbMailo
 comments
 content: ...
 timestamp: ...
 title: ...
 -JkZwz-tyYoRLoRqlI_I
 comments
 content: ...
 timestamp: ...
 title: ...
 -JkhaEf9tQy06cOF03Ts
 content: ...
 timestamp: ...
 title: ...

我觉得这个问题很奇怪,因为它应该很标准。我显然错过了一些东西,但无法解决。非常感谢任何帮助!

提前致谢!


我知道 $getRecord() 函数的文档有点误导。您实际上从 $firebaseArray 得到的是一个数组的Promise。这意味着您的 posts 变量将在将来的某个时候包含您的帖子。话虽如此,似乎 $getRecord 函数仅在Promise已解决时才有效,即从 Firebase 下载数组时。为了确保在调用 $getRecord 函数时 promise 得到解决,您可以在 promise 上使用 $loaded()

1
2
3
4
5
6
7
var posts = $firebaseArray(ref);
posts.$loaded().then(function(x) {
    var post = x.$getRecord(postId);
    console.log(post);
}).catch(function(error) {
    console.log("Error:", error);
});

如果你想知道为什么它适用于 ng-repeat,那是因为 Angular 知道 posts 变量是一个Promise,并在渲染值之前等待它被解析。


这是由于Promise而发生的。

  • 正如加藤所说,Jean-Philippe 说,$firebaseArray 不是立即可用的,因为它需要下载。
  • 请参阅 .$loaded() 文档:

    .$loaded()"returns a promise which is resolved when the initial array data has been downloaded from Firebase. The promise resolves to the $firebaseArray itself."

这回答了你的问题,我只是想展示另一种方法:

  • 这是扩展 AngularFire 服务的一个很好的用例。

    • 正如 AngularFire API 文档所说:

      "There are several powerful techniques for transforming the data downloaded and saved by $firebaseArray and $firebaseObject. These techniques should only be attempted by advanced Angular users who know their way around the code."

  • 将所有这些放在一起,您可以通过以下方式完成您想做的事情:

    • 扩展 Firebase 服务 $firebaseArray
    • 遵循扩展服务的文档。

例子

  • 这是我放在一起的一个有效的 JSFIDDLE 示例,它与我的一个公共 Firebase 实例相关联。
  • 请务必注意,您应该将 ".indexOn":"timestamp" 添加到 /posts 的规则中。

工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
app.factory('PostsArray', function (FBURL, PostsArrayFactory) {
    return function (limitToLast) {
        if (!limitToLast) {
            console.error("Need limitToLast");
            return null;
        }
        var postsRef = new Firebase(FBURL + '/posts').orderByChild('timestamp').limitToLast(limitToLast);
        return new PostsArrayFactory(postsRef);
    }
});


app.factory('PostsArrayFactory', function ($q, $firebaseArray) {
    return $firebaseArray.$extend({
        getPost: function (postKey) {
            var deferred = $q.defer();
            var post = this.$getRecord(postKey);
            if (post) {
                console.log("Got post", post);
                deferred.resolve(post);
            } else {
                deferred.reject("Post with key:" + postKey +" not found.");
            }
            return deferred.promise;
        },
        createPost: function (post) {
            var deferred = $q.defer();
            post.timestamp = Firebase.ServerValue.TIMEST
            this.$add(post).then(function (ref) {
                var id = ref.key();
                console.log("added post with id", id,"post:", post);
                deferred.resolve(ref);
            }).
            catch (function (error) {
                deferred.reject(error);
            });
            return deferred.promise;
        }
    });
});

控制器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
app.controller("SampleController", function ($scope, PostsArray) {

    var posts = new PostsArray(5);
    $scope.posts = posts;

    $scope.newPost = {};
    $scope.createNewPost = function () {
        posts.createPost($scope.newPost);
    }

    $scope.postId = '';
    $scope.getPost = function () {
        posts.getPost($scope.postId).then(function (post) {
            $scope.gotPost = post;
        }).
        catch (function (error) {
            $scope.gotPost = error;
        });
    }
});