Formik、Material UI Autocomplete 和 Firestore - 在哪里查询以查找数组参数

2024-03-22

如何修改 formik onChange 处理程序,以便它仅保存传递给 Material UI Autocomplete 字段的选项的值(而不是值加标签的数组)?

我有一个集合,其中有一个带有名为类别的属性的文档。目前,该类别使用表单输入选项中的标签和值进行填充。

我正在努力寻找一种方法来获取 firebase where 查询以查找数组的 value 属性。

我想知道如果我尝试将值而不是标签和值都保存到 firestore 中,是否会更接近工作解决方案。

我有一个 Formik 表格:

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,
  Divider,
  InputLabel,
  FormControlLabel,
  TextField,
  Typography,
  Box,
  Grid,
  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,
  CheckboxWithLabel,
  Checkbox
} from 'formik-material-ui';


const allCategories = [
    {value: 'health', label: 'Health & Medical'},
    {value: 'general', label: 'General'},    
];


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 Summary(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 
          </Button>
        <Dialog
          open={open}
          onClose={handleClose}
          aria-labelledby="form-dialog-title"
        >
          {!isSubmitionCompleted &&
            <React.Fragment>
              
              <DialogContent>
                <Formik
                  initialValues={{ title: "",  category: [], subcategory: "" }}
                  
                  onSubmit={(values, { setSubmitting }) => {
                     setSubmitting(true);
                     
                 
    firestore.collection("study").doc().set({
                      ...values,
                      createdAt: firebase.firestore.FieldValue.serverTimestamp()
                      })
                    .then(() => {
                      setSubmitionCompleted(true);
                    });
                  }}
  
                  validationSchema={Yup.object().shape({
                    title: Yup.string()
                      .required('Required'),
                    category: Yup.string()
                      .required('Required'),
                    
                  })}
                >
                  {(props) => {
                    const {
                      values,
                      touched,
                      errors,
                      dirty,
                      isSubmitting,
                      handleChange,
                      handleBlur,
                      handleSubmit,
                      handleReset,
                    } = props;
                    return (
                      <form onSubmit={handleSubmit}>
                        <TextField
                          label="Title"
                          name="title"
                          //   className={classes.textField}
                          value={values.title}
                          onChange={handleChange}
                          onBlur={handleBlur}
                          helperText={(errors.title && touched.title) && errors.title}
                          margin="normal"
                          style={{ width: "100%"}}
                        />

                        
                        <Box margin={1}>
                          <Field
                            name="category"
                            multiple
                            component={Autocomplete}
                            options={allCategories}
                            // value={values.label}
                            // value={values.value}
                            // value={allCategories.value} 
                           // value={values.category.allCategories.value}

我尝试了每一个尝试(一次一个)来让数组 仅填充单个字段 - 但它们都不起作用 那。相反,firebase 在其数组中记录标签和值。

                            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> 
                        
                        
                        <TextField
                          label="Subcategory "
                          name="subcategory"
                          //   className={classes.textField}
                          value={values.subcategory}
                          onChange={handleChange}
                          onBlur={handleBlur}
                          helperText={(errors.subcategory && touched.subcategory) && errors.subcategory}
                          margin="normal"
                          style={{ width: "100%"}}
                        />
  
                        
                      
                        <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">Done!</DialogTitle>
              <DialogContent>
               
                <DialogActions>
                  <Button
                    type="button"
                    className="outline"
                    onClick={handleClose}
                  >
                    Close
                    </Button>
                  {/* <DisplayFormikState {...props} /> */}
                </DialogActions>
              </DialogContent>
            </React.Fragment>}
        </Dialog>
      </React.Fragment>
    );
  }

export default Summary;

然后,当我尝试查询 firebase 时,我试图查找类别包含 health 的文档。

我已经尝试了下面的每个 where 查询,但我无法让它们中的任何一个返回查询结果(如果删除 where 查询,我可以返回所有结果):

function useHealthTerms() {
    const [healthTerms, setHealthTerms] = useState([])
    useEffect(() => {
      firebase
        .firestore()
        .collection("study")
    //.where("title", "==", "ss") 

注意 - 这可以找到标题。标题字段相同 level 作为类别字段

        //.where('category', '==', 'health')
        //.where('category.value', "array-contains", 'health")
        //.where('category', 'array-contains', 'health')
        //.where('category', 'array-contains', 1)
    //.where("category.1.value", '==', 'health')
        .onSnapshot(snapshot => {
          const healthTerms = snapshot.docs.map(doc => ({
            id: doc.id,
            ...doc.data(),
          }))
          setHealthTerms(healthTerms)
        })
    }, [])
    return healthTerms
  }

我见过这个帖子 https://stackoverflow.com/questions/54600915/firestore-how-to-query-data-from-a-map-of-an-array,但我不够聪明,无法从答案中理解任何意义。

我也见过这个帖子 https://stackoverflow.com/questions/47168288/firestore-query-using-an-object-element-as-parameter以及贝蒂建议的答案。我已经尝试了以下查询构造的多种变体来尝试使用这个想法,但每次我都会收到查询形式的错误。

.where(new firebase.firestore().FieldPath("category", "value"), '==', 'health')

我想知道是否可以尝试获取 formik 中的类别表单字段以保存 option.value 而不是标签和值。

我看不出 formik handleChange 如何要求它仅保存值。

即使这样,我也看不到如何查询 firestore 以使用数组的内容作为查询参数。

有人知道吗:

  1. 如何通过 formik 表单提交到 firestore 在自动完成中仅保存选项值(而不是同时保存选项标签和值)?

  2. 如何查询firestore中数组的内容以查看其属性之一是否与查询匹配?

这很奇怪,因为这个帖子 https://stackoverflow.com/questions/46849222/firestore-query-by-item-in-array-of-document建议使用我上面尝试过的形式可以对数组进行 where 查询。然而,这篇文章建议使用以下格式 .collection(study/[docId]).where("value", "==", "health")。我需要它来搜索集合中的每个文档,所以我不知道如何应用该方法来解决这个问题。

下面 gso_gabriel 的答案表明了两件令人困惑的事情。首先,假设我使用了子集合。我没有。添加下图以显示类别字段位于父文档中。我可以使用上面显示的格式对标题进行 where 查询来提取值。

其次,也是最令人困惑的一点 - 它说:“因为你无法在数组中搜索对象”。这是什么意思?是否提示category字段内的value内容无法查询?如果是这种情况,是否有资源提供如何查询该数据的指导?

我也见过这个帖子 https://stackoverflow.com/questions/52351321/how-to-query-documents-containing-array-of-objects-in-firestore-collection-using- 答案表明使用 firebase 不可能查询类别内的值。问题是我无法理解建议的替代方法。如果我正确理解了这篇文章,是否有任何教程可以扩展这些原理,以便我可以尝试找到不同的查询策略?

第一个答案关于这个帖子 https://stackoverflow.com/questions/54081799/firestore-to-query-by-an-arrays-field-value还表明不可能查询类别内的值。第二个答案建议在 where 查询中使用不同的格式 - 如下所示。

.where("category", "array-contains", {value: "health", label: "Health & Medical"})

答案强调了将整个数组内容添加到花括号的重要性。这有效。

所以 - 这让我回到自动完成提交处理程序。这是一个多选字段,因此可能会选择多个值。如何将它们变成 firebase 文档上的一组单个值。即使只有一个,我如何更改提交处理程序,以便它只发送选择选项值,而不是同时发送值和标签?

如果无法查询数组中的对象 - 如何更改提交处理程序以仅将选定的选项值添加到 firebase,而不是同时添加标签和值?第一个答案中建议的解决方法这个帖子 https://stackoverflow.com/questions/54081799/firestore-to-query-by-an-arrays-field-value就是添加一个只保存要查询的值的字段(例如:health)。


在提交到 firebase 之前onSubmit您可以更改发送的数据的形状

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

    firestore.collection("study").doc().set({
     ...values,
     category: values.category.map(c => c.value),
     createdAt: firebase.firestore.FieldValue.serverTimestamp()
    })
    .then(() => {
        setSubmitionCompleted(true);
    });
}}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Formik、Material UI Autocomplete 和 Firestore - 在哪里查询以查找数组参数 的相关文章

随机推荐