Firebase:如何将表单数据提交到不同的集合? [英] Firebase: How to submit form data to different collections?

查看:71
本文介绍了Firebase:如何将表单数据提交到不同的集合?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have one form. One of the fields in the form is a Field Array - for repeatable fields. Apart from this field, all the other form fields are stored in a single collection (the Parent Collection).

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

The Parent Collection has an array for the Field Array, which holds the values of each repeated entry, to be stored in a sub-collection (the Sub Collection).

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

When I'm writing my firestore submit, I'm trying to separate the fields to be submitted to the Parent Collection, from the fields to be submitted to the Sub Collection.

我的尝试在下面.

<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);
                    });
                  }}
  

这会产生一条错误消息:

This produces an error that says:

第120:22行:应使用赋值或函数调用,而不是 看到一个表达式,表示未使用-

Line 120:22: Expected an assignment or function call and instead saw an expression no-unused-

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

Also, how can I make the doc reference of the Parent Collection known in the submit handler for the Sub Collection?

我已经看到了这条帖子 ,它正尝试在2个集合中使用相同的数据(出于同样的原因,寻找ID).

I have seen this post, which is trying to use the same data in 2 collections (with the same concern for finding the id).

我还看到了此博客,其中显示了如何使用输入"作为子集合中的参考,并且似乎有一种将它们附加到文档ID的方法-但博客未显示输入的定义方式.我看不到如何应用该示例.

I have also seen this blog which shows how to use "inputs" as a reference in a sub-collection and seems to have a way to attach them to a doc id - but the blog doesn't show how inputs is defined. I can't see how to apply that example.

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

For reference, the main form, with the repeatable form field array (in a separate form) is set out below.

主表单

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: "",  category: [],   context: "", relatedTerms: []  }}
                  
                  onSubmit={(values, { setSubmitting }) => {
                     setSubmitting(true);
                     firestore.collection("glossary").doc().set({
                      term: values.term,
                      definition: values.definition,
                      category: values.category,
                      context: values.context,
                      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);
                    });
                  }}
  
                  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提出的建议时,我收到一个控制台警告,提示:

When I try the suggestion set out by BrettS below, I get a console warning that says:

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

Warning: An unhandled error was caught from submitForm() FirebaseError: Function DocumentReference.set() called with invalid data. Unsupported field value: undefined (found in field title)

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

I have seen this post that talks about structuring the object to use in the attempt, but I can't see how to apply those ideas to this problem.

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

Another attempt I've tried is set out below:

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,
                      category: values.category,
                      createdAt: firebase.firestore.FieldValue.serverTimestamp(),
                    });

                    writeBatch.set(relatedTermDoc, {
                      // dataType: values.dataType,
                      title: values.Title,
                      // 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);
                    });
                    
                  }}
  

当我尝试此操作时,会收到相同类型的警告.它说:

When I try this, I get the same sort of warning. It says:

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

Warning: An unhandled error was caught from submitForm() FirebaseError: Function WriteBatch.set() called with invalid data. Unsupported field value: undefined (found in field title)

使用此拆分参考格式时,我又遇到了另一个错误:

I get another error with this split reference format, which says:

警告:列表中的每个孩子应该有一个唯一的键".道具.

Warning: Each child in a list should have a unique "key" prop.

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

I think that must be something to do with the new structure of the references - but I can't see how to address it.

下次尝试

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

When I try Brett's revised suggested answer, I have:

            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,
    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);
    });
}}

请注意,我注释了relatedTerms文档上除了title属性之外的所有内容,以便我可以看到它是否完全有效.

Note, I commented everything but the title attribute on the relatedTerms document so that I could see if this works at all.

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

It doesn't. the form still renders and when I try to press submit, it just hangs. No error messages are generated in the console, but it does generate a warning message that says:

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

0.chunk.js:141417 Warning: An unhandled error was caught from submitForm() FirebaseError: Function WriteBatch.set() called with invalid data. Unsupported field value: undefined (found in field title)

当我在Google上搜索时-它从此发布,可能是在relatedTerm集合中定义父文档的doc ID的方式有问题.

When I google this - it looks from this post that maybe there is a problem with the way the doc id of the parent is defined in the relatedTerm collection.

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

I'm also wondering if the initial values maybe need to be separately defined and initialised for each collection?

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

When I try console logging the values of the form entries, I can see that an object with a value of title is captured. The initial values for the form include an array called relatedTerms (initial value: []).

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

Maybe I need to do something to convert that array into the values that go in it before I try sending this to firestore. How would I do that?

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

The post I linked breaks this into 2 steps, but I am too slow to figure out what they are doing or how to do them myself. It is strange though that this problem doesn't arise when I don't try to split the form values between firestore collections - if I just use a single document, then whatever needs to happen here is being done by default.

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

I'm not sure if what I'm trying to do is what the firestore docs are describing in the custom objects section. I note that the adding data example above it shows adding an array without any steps taken to convert the items in the array to the data type before submitting. I'm not sure if this is the right line of enquiry given that the submission works fine if I don't try to split the data between collections.

下次尝试

Andreas在

The answer from Andreas on this post is simple enough for me to grasp. The spread operator works where it is used in the submit method for the relatedTerms entries.

但是,这引发了下一个挑战-即如何读取子集合数据. firebase文档的这一部分令我感到困惑.我说不通.

However, that throws up the next challenge - which is how to read the sub collection data. This part of the firebase documentation is baffling to me. I can't make sense of it.

它说:

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

Retrieving a list of collections is not possible with the mobile/web client libraries.

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

Does it mean I can't read the values in relatedTerms table?

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

Previously, I was able to read the array of relatedTerms data as follows:

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现在是词汇表集合中的子集合,而不是词汇表集合中的数组.我从这篇文章了解到,我必须查询分别收集.

relatedTerms is now a sub collection in the glossary collection instead of an array in the glossary collection. I understand from this post that I have to query the collections separately.

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

So the first query is how to get newDocRef.id to save as an attribute in the relatedTerms document. I tried adding an attribute to the submit for it.

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

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

Whilst it didn't generate any errors when I try submitting the form, it also didn't create an entry in the relatedTerms document called glossaryId. The log of values doesn't include it either.

我看过这篇文章和吉姆的答案.我不明白如何在单独的useEffect中使用我的lossaryTerm.id作为文档ID来查找相关术语.

I have seen this post and the answer by Jim. I don't understand how to use my glossaryTerm.id as the doc id in a separate useEffect to find the relatedTerms.

推荐答案

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

Every time you call doc(), you're going to generate a reference to a new randomly generated document. That means your first call to firestore.collection("glossary").doc() will generate a new ID, as well as the subsequent call. If you want to reuse a document reference, you're going to have to store it in a variable.

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

稍后使用相同的变量

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

这篇关于Firebase:如何将表单数据提交到不同的集合?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆