Firestore:如何从数组中检索数据并将其传递到表中?

Posted

技术标签:

【中文标题】Firestore:如何从数组中检索数据并将其传递到表中?【英文标题】:Firestore: How to retrieve data from an array and deliver it into a table? 【发布时间】:2021-08-01 01:53:58 【问题描述】:

我正在构建一个用于练习的营养应用程序。

现在我有一个名为“inventar”的集合,它存储一个包含两个数组的文档。 “product”是一个包含所有产品的数组,如牛奶、香肠、奶酪等。另一个数组“expirationDate”包含产品何时腐烂。

我想在一个表格中显示这两个数组的内容。

听起来很简单,对吧?

嗯,我在这个练习中苦苦挣扎了 2 周多,并且被卡住了,所以我请求你的帮助。

首先,我以多种方式询问数据,直到我对 DocumentSnapshot、QuerySnapshot、DocumentReference、AsynchSnapshot 以及现在何时使用 get()、snapshot() 或 StreamBulder()/FutureBuilder() 感到完全困惑。我觉得,每次在网上找到一个合理的解决方案,一个小小事情都没有解决,导致整个方法失败,不得不寻找另一种方法。

在网上搜索查询数组的方法,这应该很容易,我只是在寻找解决方案,执行“只给我那些条目”之类的搜索。我只是想取出整个数组,没有从 x 到 y 的条目(至少现在不是)。或者我不能查询一个包含 where() 语句的数组吗?

在我没有找到适合我的方法后,我尝试在“查询”之后获取数组长度,以便我可以执行一个 for 循环来检索数据。但我似乎没有找到将查询的长度属性传递给 for 循环。

因此,在这种方法之后,我尝试使用 StreamBuilder 和 FutureBuilder 以及单独的异步方法。但是 FutureBuilder 抱怨我没有提供正确的 Widget 类型(如果涉及 Future,如何从 TableRow 类型方法返回新的 TableRow?)。 StreamBuilder 也不适合我。

我现在什至没有代码可以与您分享,因为我放弃了所有方法,希望下一个方法能够奏效。我想,我现在有很多困惑,因为我看不到 firestore 查询的各个部分是如何粘在一起的,我什么时候必须/可以使用 get(),什么时候使用 map() 和什么时候使用 snapshot() 等等。

因此,简而言之: 我只是想在集合中的一个文档中检索两个单独的数组,并将它们放入 TableCell 中的文本小部件中。

你们会怎么做呢?

如果您能向我解释何时使用 StreamBuilder()FutureBuilder() 以及何时/以何种顺序使用 get()、snapshot()ma​​p() 与 firestore 查询相关。我现在很困惑。

期待您的回复!

悟空

编辑:

所以,这就是我从 Frank 发送的链接中得到的:

            new StreamBuilder<QuerySnapshot>(
                stream: FirebaseFirestore.instance.collection("inventar").snapshots(),
                builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) 
                  if (snapshot.hasError) 
                    return Text('Something went wrong');
                  

                  if (snapshot.connectionState == ConnectionState.waiting) 
                    return Text("Loading");
                  

                  return new ListView(
                    children: snapshot.data!.docs.map((DocumentSnapshot document) 
                      return new ListTile(
                        title: new Text(document.data()?['produkt']),
                        subtitle: new Text(document.data()?['verfallsdatum']),
                      );
                    ).toList(),
                  );
                ,
              )

现在我得到 "type 'List' is not a subtype of type 'String'",即使我实际上是在调用数组中的字符串。尝试使用 .toString() 但随后显示 "参数类型 'String?'不能分配给参数类型'String'”

而且,我不需要一个索引值来调用数组中的每个单独的条目吗?还是使用 .map() 自动完成?

更不用说我目前将日期设置为字符串,因为我已经研究过将 firebase 时间戳转移到看起来非常复杂的实际日期。所以我推迟了这个,直到我首先获得了正确结构的数据。

【问题讨论】:

您可以编辑您的问题以显示您认为应该作为实际代码工作的一种方法,而不是用文字描述您尝试过的所有内容吗?看到这样的minimal reproduction 将使它更像这里的某个人可以提供超出此处文档显示的帮助:firebase.flutter.dev/docs/firestore/usage#realtime-changes 感谢您的回复,弗兰克。我刚刚编辑了我的帖子。希望它不会太混乱。期待您的帮助/解释。我很确定你将能够在黑暗中带来光明。 【参考方案1】:

您将 'product' 和 'expirationDate' 数组存储在一个文档中。 但是在您的代码中,我可以看到您正在尝试从“发明者”的查询中获取文档。 这就是为什么你得到“类型'列表'不是类型'字符串'的子类型”错误,因为

document.data()?['produkt']

这将返回一个列表。而且您正试图将该列表提供给仅接受字符串的 Text 小部件。 你应该做的是只获取一个文档,然后设计一个可迭代的小部件,它将遍历文档中的列表。 您做了相反的事情,您在获取文档之前进行迭代。

让我们按照我刚才解释的方式进行,即在获取文档后进行迭代-

我正在关注您问题的第一段并现在编写代码-

StreamBuilder<QuerySnapshot>(
            stream: FirebaseFirestore.instance.collection("inventar").snapshots(),
            builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) 
              if (snapshot.hasError) 
                return Text('Something went wrong');
              

              if (snapshot.connectionState == ConnectionState.waiting) 
                return Text("Loading");
              

              return new ListView(
                children: snapshot.data.docs.first.data()['products'].map((String product)
                  return ListTile(leading: Text(product));
                ).toList()
              );
            ,
          )

在您的代码中,您正在获取文档列表,这与您解释数据库的外观不同。 正如您在我的代码中看到的,我们只能获取产品或过期日期,因为它们存储在单独的列表中。 我在这段代码中所做的是,我去了 Inventar 集合并获取了该代码获取的该集合的查询快照-

stream: FirebaseFirestore.instance.collection("inventar").snapshots()

现在这个 QuerySnapshot 包含该集合中的所有文档。 正如您所说,您的收藏中只有一个文档,所以我以这种方式获取了-

snapshot.data.docs.first.data()['products']

现在这将返回一个列表,因为您已经在该文档中存储了一个列表。所以我使用 map 函数将产品列表映射到 ListTile 小部件并返回它。 所以这样我们只能在流构建器中获取一个列表,产品或过期日期。如果您不希望更改数据库,则必须使用函数(而不是 streambuilder)分别获取这两个列表。 但是还有另一种方法,我们可以按照您想要的方式获取您的数据,而无需更改您的数据库结构。通过使用 listview.builder 小部件-

StreamBuilder<QuerySnapshot>(
  stream: FirebaseFirestore.instance.collection("inventar").snapshots(),
  builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) 
    if (snapshot.hasError) 
      return Text('Something went wrong');
    

    if (snapshot.connectionState == ConnectionState.waiting) 
      return Text("Loading");
    

    return new ListView.builder(
      itemCount: snapshot.data.docs.first.data()['products'].length,
      itemBuilder: (ctx, i) 
        return ListTile(
          leading: Text(snapshot.data.docs.first.data()['products'][i]),
          trailing:
              Text(snapshot.data.docs.first.data()['expirationDate'][i]),
        );
      ,
    );
  ,
)

这里 listview.builder 小部件的长度将由 itemCount 给出。所以我假设两个列表的长度相同,否则会抛出错误。 这将返回一个列表-

snapshot.data.docs.first.data()['products']

然后这将返回它的长度-

snapshot.data.docs.first.data()['products'].length

假设列表的长度值为 5。 现在 listview.builder 小部件将自动返回 listTile 小部件 5 次,并且 i 是从 0 到 4 的索引。所以现在在 ListTile 中,您可以显示列表的第 i 个数据。

所以我想这段代码应该可以工作。我希望您了解我是如何先获取列表然后进行迭代的。

【讨论】:

早上好,感谢您的回复,Artisted!对我来说完全有意义。但是,我实际上想在其中显示我的数据的表格怎么可能呢? 如果您能够正确获取数据,那么 ListTile 非常适合显示您的数据。或者,也许您可​​以创建自己的自定义小部件来执行此操作。还有一个表格小部件。请点击此链接 - api.flutter.dev/flutter/widgets/Table-class.html 此链接也 - javatpoint.com/flutter-table

以上是关于Firestore:如何从数组中检索数据并将其传递到表中?的主要内容,如果未能解决你的问题,请参考以下文章

如何从 Cloud Firestore 上的对象中检索值?迅速

如何从firebase数据库中检索数据作为字符串而不是字典并将其添加到数组中?

如何从 Firestore 获取数组?

我应该如何从会话中检索登录用户并将其传递给我的业务层?

如何将数组从 c++ 传递给 python 函数并将 python 返回的数组检索到 c++

从使用 jQuery 加载的表单中检索数据并将其传递给父页面