Firebase:如何将表单数据提交到不同的集合?

如何解决Firebase:如何将表单数据提交到不同的集合?

我有一张表格。表单中的字段之一是字段数组-用于可重复的字段。除了此字段外,所有其他表单字段都存储在单个集合(父集合)中。

父集合具有用于字段数组的数组,该数组保存每个重复条目的值,并存储在子集合(子集合)中。

在编写Firestore提交时,我试图将要提交给父集合的字段与要提交给子集合的字段分开。

我的尝试在下面。

<Formik
                  initialValues={{ term: "",category: [],relatedTerms: [],}}
                  
                  onSubmit={(values,{ setSubmitting }) => {
                     setSubmitting(true);
                     firestore.collection("glossary").doc().set({
                      term: values.term,category: values.category,createdAt: firebase.firestore.FieldValue.serverTimestamp()
                      }),firestore.collection("glossary").doc().collection('relatedTerms').doc().set({
                        dataType: values.dataType,title: values.Title,description: values.description,})
                    .then(() => {
                      setSubmitionCompleted(true);
                    });
                  }}
  

这会产生一条错误消息:

120:22行:预期分配或函数调用,而不是 看到一个表达式,表示未使用-

此外,如何在Sub Collection的提交处理程序中知道Parent Collection的文档引用?

我已经看到了这个post,它正试图在2个集合中使用相同的数据(出于同样的原因来寻找ID)。

我还看到了this blog,它显示了如何在子集合中使用“输入”作为引用,并且似乎有一种将它们附加到文档ID的方法-但是博客没有显示如何输入已定义。我看不到如何应用该示例。

作为参考,下面列出了具有可重复形式的字段字段数组(以单独的形式)的主要形式。

主表单

import React,{ useState } from "react";
import ReactDOM from "react-dom";
import {render} from 'react-dom';

import { Link  } from 'react-router-dom';
import firebase,{firestore} from '../../../../firebase';
import { withStyles } from '@material-ui/core/styles';

import {
  Button,LinearProgress,MenuItem,FormControl,InputLabel,FormControlLabel,TextField,Typography,Box,Grid,Checkbox,Dialog,DialogActions,DialogContent,DialogContentText,DialogTitle,} from '@material-ui/core';
import MuiTextField from '@material-ui/core/TextField';


import {
  Formik,Form,Field,ErrorMessage,FieldArray,} from 'formik';


import * as Yup from 'yup';
import {
  Autocomplete,ToggleButtonGroup,AutocompleteRenderInputParams,} from 'formik-material-ui-lab';
import {
  fieldToTextField,TextFieldProps,Select,Switch,} from 'formik-material-ui';

import RelatedTerms from "./Form2";

const allCategories = [
    {value: 'one',label: 'I'},{value: 'two',label: 'C'},];


function UpperCasingTextField(props: TextFieldProps) {
    const {
      form: {setFieldValue},field: {name},} = props;
    const onChange = React.useCallback(
      event => {
        const {value} = event.target;
        setFieldValue(name,value ? value.toUpperCase() : '');
      },[setFieldValue,name]
    );
    return <MuiTextField {...fieldToTextField(props)} onChange={onChange} />;
  }

  function Glossary(props) {
    const { classes } = props;
    const [open,setOpen] = useState(false);
    const [isSubmitionCompleted,setSubmitionCompleted] = useState(false);
    
    function handleClose() {
      setOpen(false);
    }
  
    function handleClickOpen() {
      setSubmitionCompleted(false);
      setOpen(true);
    }
  
    return (
      <React.Fragment>
          <Button
              // component="button"
              color="primary"
              onClick={handleClickOpen}
              style={{ float: "right"}}
              variant="outlined"
          >
              Create Term
          </Button>
        <Dialog
          open={open}
          onClose={handleClose}
          aria-labelledby="form-dialog-title"
        >
          {!isSubmitionCompleted &&
            <React.Fragment>
              <DialogTitle id="form-dialog-title">Create a defined term</DialogTitle>
              <DialogContent>
                <DialogContentText>
                  Your contribution to the research community is appreciated. 
                </DialogContentText>
                <Formik
                  initialValues={{ term: "",definition: "",context: "",relatedTerms: []  }}
                  
                  onSubmit={(values,definition: values.definition,context: values.context,title: values.title,})
                    .then(() => {
                      setSubmitionCompleted(true);
                    });
                  }}
  
                  validationSchema={Yup.object().shape({
                    term: Yup.string()
                      .required('Required'),definition: Yup.string()
                      .required('Required'),category: Yup.string()
                      .required('Required'),context: Yup.string()
                      .required("Required"),// relatedTerms: Yup.string()
                    //   .required("Required"),})}
                >
                  {(props) => {
                    const {
                      values,touched,errors,dirty,isSubmitting,handleChange,handleBlur,handleSubmit,handleReset,} = props;
                    return (
                      <form onSubmit={handleSubmit}>
                        <TextField
                          label="Term"
                          name="term"
                        //   className={classes.textField}
                          value={values.term}
                          onChange={handleChange}
                          onBlur={handleBlur}
                          helperText={(errors.term && touched.term) && errors.term}
                          margin="normal"
                          style={{ width: "100%"}}
                        />
  
                        <TextField
                          label="Meaning"
                          name="definition"
                          multiline
                          rows={4}
                        //   className={classes.textField}
                          value={values.definition}
                          onChange={handleChange}
                          onBlur={handleBlur}
                          helperText={(errors.definition && touched.definition) && errors.definition}
                          margin="normal"
                          style={{ width: "100%"}}
                        />
  
                        
                        
                        <TextField
                          label="In what context is this term used?"
                          name="context"
                        //   className={classes.textField}
                          multiline
                          rows={4}
                          value={values.context}
                          onChange={handleChange}
                          onBlur={handleBlur}
                          helperText={(errors.context && touched.context) && errors.context}
                          margin="normal"
                          style={{ width: "100%"}}
                        />
                        
  
                        
                        <Box margin={1}>
                          <Field
                            name="category"
                            multiple
                            component={Autocomplete}
                            options={allCategories}
                            getOptionLabel={(option: any) => option.label}
                            style={{width: '100%'}}
                            renderInput={(params: AutocompleteRenderInputParams) => (
                              <MuiTextField
                                {...params}
                                error={touched['autocomplete'] && !!errors['autocomplete']}
                                helperText={touched['autocomplete'] && errors['autocomplete']}
                                label="Category"
                                variant="outlined"
                              />
                            )}
                          />
                        </Box>     
                        
                        <FieldArray name="relatedTerms" component={RelatedTerms} />
                        <Button type="submit">Submit</Button>
                        
                        <DialogActions>
                          <Button
                            type="button"
                            className="outline"
                            onClick={handleReset}
                            disabled={!dirty || isSubmitting}
                          >
                            Reset
                          </Button>
                          <Button type="submit" disabled={isSubmitting}>
                            Submit
                          </Button>
                          {/* <DisplayFormikState {...props} /> */}
                        </DialogActions>
                      </form>
                    );
                  }}
                </Formik>
              </DialogContent>
            </React.Fragment>
          }
          {isSubmitionCompleted &&
            <React.Fragment>
              <DialogTitle id="form-dialog-title">Thanks!</DialogTitle>
              <DialogContent>
                <DialogContentText>
                  
                </DialogContentText>
                <DialogActions>
                  <Button
                    type="button"
                    className="outline"
                    onClick={handleClose}
                  >
                    Close
                    </Button>
                  {/* <DisplayFormikState {...props} /> */}
                </DialogActions>
              </DialogContent>
            </React.Fragment>}
        </Dialog>
      </React.Fragment>
    );
  }



export default Glossary;

可重复表格字段的字段数组

import React from "react";
import { Formik,Field } from "formik";
import Button from '@material-ui/core/Button';

const initialValues = {
  dataType: "",title: "",description: "",};

const dataTypes = [
  { value: "primary",label: "Primary (raw) data" },{ value: "secondary",label: "Secondary data" },];

class DataRequests extends React.Component {
  render() {
    
    const {form: parentForm,...parentProps} = this.props;

    return (
      <Formik
        initialValues={initialValues}
        render={({ values,setFieldTouched }) => {
          return (
            <div>
              {parentForm.values.relatedTerms.map((_notneeded,index) => {
                return (
                  <div key={index}>
                    
                            <div className="form-group">
                              <label htmlFor="relatedTermsTitle">Title</label>
                              <Field
                                name={`relatedTerms.${index}.title`}
                                placeholder="Add a title"
                                className="form-control"
                                onChange={e => {
                                  parentForm.setFieldValue(
                                    `relatedTerms.${index}.title`,e.target.value
                                  );
                                }}
                              ></Field>
                            </div>
                          
                            <div className="form-group">
                              <label htmlFor="relatedTermsDescription">
                                Description
                              </label>
                              <Field
                                name={`relatedTerms.${index}.description`}
                                component="textarea"
                                rows="10"
                                placeholder="Describe use"
                                className="form-control"
                                onChange={e => {
                                  parentForm.setFieldValue(
                                    `relatedTerms.${index}.description`,e.target.value
                                  );
                                }}
                              ></Field>
                            </div>
                          
                            
                            
                          <Button
                            
                            onClick={() => parentProps.remove(index)}
                          >
                            Remove
                          </Button>
                        
                  </div>
                );
              })}
              <Button
                variant="primary"
                size="sm"
                onClick={() => parentProps.push(initialValues)}
              >
                Add another
              </Button>
            </div>
          );
        }}
      />
    );
  }
}

export default DataRequests;

下一次攻击

当我尝试下面BrettS提出的建议时,我收到一个控制台警告,提示:

警告:从SubmitForm()捕获了未处理的错误FirebaseError:函数DocumentReference.set()调用了无效数据。不支持的字段值:未定义(在字段标题中找到)

我看过this帖子,它讨论了结构化尝试中使用的对象,但是我看不到如何将这些思想应用于该问题。

我尝试过的另一种尝试如下:

onSubmit={(values,{ setSubmitting }) => {
                     setSubmitting(true);

                    //   const newGlossaryDocRef = firestore.collection("glossary").doc(); 
                    //   newGlossaryDocRef.set({
                    //     term: values.term,//     definition: values.definition,//     category: values.category,//     context: values.context,//     createdAt: firebase.firestore.FieldValue.serverTimestamp()
                    //     });
                    //   newGlossaryDocRef.collection('relatedTerms').doc().set({
                    // //     dataType: values.dataType,//       title: values.title,// //     description: values.description,//    })

                    const glossaryDoc = firestore.collection('glossary').doc()
                      
                    const relatedTermDoc = firestore
                      .collection('glossary')
                      .doc(glossaryDoc.id) // <- we use the id from docRefA
                      .collection('relatedTerms')
                      .doc()
                      

                    var writeBatch = firestore.batch();

                    writeBatch.set(glossaryDoc,{
                      term: values.term,createdAt: firebase.firestore.FieldValue.serverTimestamp(),});

                    writeBatch.set(relatedTermDoc,{
                      // dataType: values.dataType,// description: values.description,});

                    writeBatch.commit().then(() => {
                      // All done,everything is in Firestore.
                    })
                    .catch(() => {
                      // Something went wrong.
                      // Using firestore.batch(),we know no data was written if we get here.
                    })
                    .then(() => {
                      setSubmitionCompleted(true);
                    });
                    
                  }}
  

当我尝试这样做时,我会收到同样的警告。它说:

警告:从SubmitForm()捕获了未处理的错误 FirebaseError:函数WriteBatch.set()用无效数据调用。 不支持的字段值:未定义(在字段标题中找到)

此拆分参考格式出现另一个错误,提示:

警告:列表中的每个孩子都应该有一个唯一的“关键”道具。

我认为这一定与引用的新结构有关-但我看不出如何解决它。

下一个尝试

当我尝试布雷特修订后的建议答案时,我会发现:

            onSubmit={(values,{ setSubmitting }) => {
                 setSubmitting(true);
                 
                //  firestore.collection("glossary").doc().set({
                //   ...values,//   createdAt: firebase.firestore.FieldValue.serverTimestamp()
                //   })
                // .then(() => {
                //   setSubmitionCompleted(true);
                // });
              // }}
              const newDocRef = firestore.collection("glossary").doc() 

// auto generated doc id saved here
  let writeBatch = firestore.batch();
  writeBatch.set(newDocRef,{
    term: values.term,createdAt: firebase.firestore.FieldValue.serverTimestamp()
  });
  writeBatch.set(newDocRef.collection('relatedTerms').doc(),{
    // dataType: values.dataType,})
  writeBatch.commit()
    .then(() => {
      setSubmitionCompleted(true);
    });
}}

请注意,我评论了relatedTerms文档中除了title属性之外的所有内容,以便我能看到它是否完全有效。

不是。表单仍然呈现,当我尝试按Submit时,它只是挂起。控制台中不会生成任何错误消息,但是会生成一条警告消息,内容为:

0.chunk.js:141417警告:从submitForm()FirebaseError捕获了未处理的错误:函数WriteBatch.set()调用 无效数据。不支持的字段值:未定义(在字段中找到 标题)

我在Google上搜索时-从此post看来,在relatedTerm集合中定义父级文档ID的方式可能有问题。

我还想知道是否可能需要为每个集合分别定义和初始化初始值?

当我尝试使用控制台记录表单条目的值时,可以看到捕获了一个具有title值的对象。表单的初始值包括一个名为relatedTerms的数组(初始值:[])。

enter image description here

在尝试将其发送到Firestore之前,也许我需要做一些事情以将该数组转换为其中的值。我该怎么办?

我链接的帖子将其分为两个步骤,但我太慢了,无法弄清他们在做什么或自己如何做。奇怪的是,当我不尝试在Firestore集合之间拆分表单值时,不会出现此问题-如果我只使用一个文档,则默认情况下会发生任何事情。

我不确定我要执行的操作是否是custom objects部分中firestore文档所描述的内容。我注意到上面的添加数据示例显示了添加数组,但未采取任何步骤即可在提交之前将数组中的项目转换为数据类型。我不确定这是否是正确的查询,因为如果我不尝试在集合之间拆分数据,则提交工作正常。

下一个尝试

安德烈亚斯(Andreas)对this post的回答很简单,我很容易理解。传播运算符可以在relatedTerms条目的Submit方法中使用的地方使用。

enter image description here

但是,这引发了下一个挑战-即如何读取子集合数据。 firebase documentation的这一部分让我感到困惑。我无法理解。

它说:

无法通过手机/网络检索收藏列表 客户端库。

这是否意味着我无法读取relatedTerms表中的值?

以前,我能够按以下方式读取relatedTerms数据数组:

function useGlossaryTerms() {
    const [glossaryTerms,setGlossaryTerms] = useState([])
    useEffect(() => {
      firebase
        .firestore()
        .collection("glossary")
        .orderBy('term')
        .onSnapshot(snapshot => {
          const glossaryTerms = snapshot.docs.map(doc => ({
            id: doc.id,...doc.data(),}))
          setGlossaryTerms(glossaryTerms)
        })
    },[])
    return glossaryTerms
  }

然后:

{glossaryTerm.relatedTerms.map(relatedTerm => (
                                
                                <Link to="" className="bodylinks" key={relatedTerm.id}>
                                 {relatedTerm.title}
                          </Link>                                   ))}

relatedTerms现在是词汇表集合中的子集合,而不是词汇表集合中的数组。从this post我了解到,我必须分别查询集合。

因此,第一个查询是如何获取newDocRef.id作为属性保存在relatedTerms文档中。我尝试将属性添加到提交中。

glossaryId: newDocRef.id,...values.relatedTerms

尽管在我尝试提交表单时它没有产生任何错误,但它也没有在relatedTerms文档中创建一个名为lossaryId的条目。值的日志也不包含它。

我看过this post和吉姆的回答。我不明白如何在单独的useEffect中使用我的glossaryTerm.id作为文档ID来查找相关条款。

解决方法

每次调用doc()时,都将生成对新随机生成的文档的引用。这意味着您对firestore.collection("glossary").doc()的第一次调用将生成一个新的ID,以及随后的调用。如果要重用文档引用,则必须将其存储在变量中。

const firstDocRef = firestore.collection("glossary").doc()
firstDocRef.set(...)

稍后使用相同的变量:

const secondDocRef = firstDocRef.collection('relatedTerms').doc()
secondDocRef.set(...)
,

我没有足够的业力或其他东西要评论,所以我在这里发表评论。

这是用您的代码实现Doug解决方案的一种方法。抱歉,对于任何语法错误-我没有测试运行此代码。

即使在提交时生成了自动ID,您也可以在执行前传递文档ID。

onSubmit={(values,{ setSubmitting }) => {
  setSubmitting(true);
  const newDocRef = firestore.collection("glossary").doc() // auto generated doc id saved here
  let writeBatch = firestore.batch();
  writeBatch.set(newDocRef,{
    term: values.term,definition: values.definition,category: values.category,context: values.context,createdAt: firebase.firestore.FieldValue.serverTimestamp()
  }),writeBatch.set(newDocRef.collection('relatedTerms').doc(),{
    dataType: values.dataType,title: values.title,description: values.description,})
  writeBatch.commit()
    .then(() => {
      setSubmitionCompleted(true);
    });
}}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-