Функция JSON.stringify () заменяет объект файла пустым - PullRequest
0 голосов
/ 13 ноября 2018

ВЫПУСКА

Здравствуйте, ребята, кто-нибудь может помочь мне решить эту сложную проблему? : Я создаю приложение, используя Spring boot v2.0.5 и React.js v15.6.2, ReactDom v15.6.2, React Bootstrap v0.32.4, а связь между частями моего веб-интерфейса и серверной части сделана из спокойных веб-служб с использованием аннотаций Spring на назад и получить API на передней панели. Компоненты My React создаются в соответствии с концепцией шаблона проектирования Parent-Children, это означает, что некоторые из моих компонентов могут быть дочерними по отношению к другим, и наоборот.

Как это работает?

У меня есть таблица со столбцами и строками, каждая строка внутри таблицы имеет уникальный идентификатор, 2 раскрывающихся списка, 1 ввод текста, 2 средства выбора даты и 1 ввод загрузки файла, который вызывает основную проблему ; Пользователь может добавить больше строк, которые имеют те же компоненты, что и предыдущие, нажав кнопку «+ Документ»; Каждая строка имеет уникальный инкрементный идентификатор типа номер (целое число); выпадающие списки и входные события обрабатываются одним методом внутри родительского компонента на основе их имен тегов; Я храню все данные, введенные пользователем, в списке ([]) объектов ({}).

Пример: , если пользователь заполняет только первую строку; объект, хранящийся в состоянии списка, будет выглядеть так:

[{id:0,type:"forms",lang:"all",libelle:"some strings",dateBegin:"11-12-2018",dateEnd:"12-12-2018",document:{File(154845)}]

если пользователь добавит еще одну строку, а затем заполнит ее, как и первую, список будет выглядеть следующим образом:

  [{id:0,type:"forms",lang:"all",libelle:"some strings",dateBegin:"11-12-2018",dateEnd:"12-12-2018",document:{File(154845)},{id:1,type:"howTo",lang:"en",libelle:"some strings",dateBegin:"11-12-2018",dateEnd:"01-01-2019",document:{File(742015)}]

Проверьте это изображение, чтобы увидеть, как выглядит таблица: Таблица Demo

Таблица в виде кода в классе Presentational компонента (дочерний элемент основного компонента)

class Presentational extends React.Component {

  constructor(props) {
   super(props);

   this.state = {
    docObjList: [],
    element: (
      <FormDocRowItem // this contains the table tbody tds elements..
        id={1}
        handleChanges={this.props.handleChanges}/>)
   };

     this.handleAddDocumentRow = this.handleAddDocumentRow.bind(this);
 }

  // handleAddDocumentRow method
  handleAddDocumentRow(e) {

   const value = e.target.value;
   const name = e.target.name;

   if (name === 'add') {
    let arr = this.state.docObjList; // get the list state

    // assign the new row component
    arr = [...arr, Object.assign({}, this.state.element)]; 

    // set the new list state
    this.setState({docObjList: arr});
   }

   // if name === 'delete' logic..
 }

   // render method
   render() {
          const {handleReset} = this.props;
      return(
       <FormGroup>
              <Form encType="multipart/form-data">
                <Table striped bordered condensed hover>
                  <thead>
                  <tr>
                    <th>id</th>
                    <th>Type</th>
                    <th>Lang</th>
                    <th>Title</th>
                    <th>Date begin</th>
                    <th>Date end</th>
                    <th>+ Document</th>
                    <th>Options</th>
                  </tr>
                  </thead>
                  <tbody>

                  {this.state.element} // this row is required as initialization
                  {
                    this.state.docObjList.map((doc, index) => {
                      // as index in map() starts from 0 and there is an   
                      // already row component above => The index inside the 
                      // table should start from 1 except The key property 
                      // which should know the right index of the function 
                      const id = index+1; 

                      return (
                        <tr key={index}> 
                          <td>
                            {id}
                          </td>
                          <td>
                            <DocumentTypes id={id} handleChange={this.props.handleChanges}/>
                          </td>
                          <td>
                            <DocumentLanguage id={id} handleChange={this.props.handleChanges}/>
                          </td>
                          <td>
                            <DocumentLibelle id={id} handleChange={this.props.handleChanges}/>
                          </td>
                          <td>
                            <FormControl  id={''+id} name="dateBegin" componentClass="input" type="date"
                                         onChange={this.props.handleChanges}/>
                          </td>
                          <td>
                            <FormControl  id={''+id} name="dateEnd" componentClass="input" type="date"
                                         onChange={this.props.handleChanges}/>
                          </td>
                          <td>
                            <Document  id={id} handleChange={this.props.handleChanges}/>
                          </td>
                          {
                            this.state.docObjList.length == index + 1 &&
                            <td>

                              <button type="button" style={{verticalAlign: 'middle', textAlign: 'center'}} id={index + 1}
                                      name="delete"
                                      onClick={this.handleAddDocumentRow}>
                                Delete
                              </button>
                            </td>
                          }
                        </tr>
                      );
                    })
                  }
                  </tbody>
                </Table>
                <button type="button" name="add" onClick={this.handleAddDocumentRow}>+ Document</button>

                <FormGroup>
                  <Button type="reset"
                          style={{marginRight: '20%'}}
                          className="btn-primary"
                          onClick={this.props.handleClickSubmit}>Submit</Button>
                  <Button name="back" onClick={this.props.handleClickSubmit}>Annuler</Button>
                </FormGroup>
              </Form>
       </FormGroup>
        )
  }
}

Класс компонента строки (дочерний компонент Presentational)

const FormDocRowItem = (props) => {

  const {id} = props; // the ID here is refering the column that is going to be 
                      // show inside the table not the index of the map function
  return(
    return (
      <tr>
        <td>
          {id}
        </td>
        <td>
          <DocumentTypes id={id} handleChange={this.props.handleChanges}/>
        </td>
        <td>
          <DocumentLanguage id={id} handleChange={this.props.handleChanges}/>
        </td>
        <td>
          <DocumentLibelle id={id} handleChange={this.props.handleChanges}/>
        </td>
        <td>
          <FormControl id={''+id} name="dateBegin" componentClass="input" type="date" onChange={this.props.handleChanges}/>
        </td>
        <td>
          <FormControl id={''+id} name="dateEnd" componentClass="input" type="date" onChange={this.props.handleChanges}/>
        </td>
        <td>
          <Document id={id} handleChange={this.props.handleChanges}/>
        </td>
      </tr>
    );
  }

}

Класс родительского компонента (основной компонент)

   constructor(props) {
     this.state ={
       docDataList: [],
       formIsReadyToSubmit: false
     } 

     this.handleSubmit = this.handleSubmit.bind(this); // button submit click
     this.handleReset = this.handleReset.bind(this); // button reset click
     this.fillWithData = this.fillWithData.bind(this); // handle changes

   }

   // handleReset method..

   fillWithData(e) {

    const name = e.target.name; // get the name of the target
    const id = parseInt(e.target.id); // get the id of the target
    let value = e.target.value; // get the value of the target
    let arr = this.state.docDataList; // get the list state

    // if the target is a file upload
    if (name === 'selectDocument') 
      value = e.target.files[0];

    // create properties with null values starting from the first onchange 
    // event handling, to not get a misplaced properties inside the
   // objects of the list state
    arr.map((x) => {
      x.type = x.type ? x.type : null;
      x.lang = x.lang ? x.lang : null;
      x.libelle = x.libelle ? x.libelle : null;
      x.dateBegin = x.dateBegin ? x.dateBegin : null;
      x.dateEnd = x.dateEnd ? x.dateEnd : null;
      x.document = x.document ? x.document : null;
    });

    // if the event target name is not delete
    if (name != 'delete') {
      // check if the object id already exist in the table
      // if it exists, the new value should replace the previous one
      // and not allowed to add a new object to the list state
      if ((arr.find((x) => x.id == id))) {
        // loop through the list state to find the id of the object
        arr.map((x) => {
          if (x.id == id) {
            // helper variable to prevent empty strings
            const val = value != '' ? value : null;

            switch (name) {
              case 'selectType':
                x.type = val;
                break;
              case 'selectLang':
                x.lang = val;
                break;
              case 'libelle':
                x.libelle = val;
                break;
              case 'dateBegin':
                x.dateBegin = val;
                break;
              case 'dateEnd':
                x.dateEnd = val;
                break;
              case 'selectDocument':
                x.document = val;
                break;
            }
          }
        });
        // assign the new list to my docDataList state
        // mentioning that the id of the element already exist
        this.setState({docDataList: arr}, () => {
          console.log(' ID exist; new dataList :', this.state.docDataList);
        });
      }
      // if the id doesn't exist (means that the button +document is clicked)
      else {
        // again, a helper variable as the previous statement 
        const val = value != '' ? value : null;

        this.setState({
          docDataList: [...arr, Object.assign({
            id: id,
            type: name === 'selectType' ? val : null,
            lang: name === 'selectLang' ? val : null,
            libelle: name === 'libelle' ? val : null,
            dateBegin: name === 'dataBegin' ? val : null,
            dateEnd: name === 'dateEnd' ? val : null//,
            //document: name==='selectDocument'? val:null
          })]
        }, () => {
          console.log('ID doesnt exist; new dataList :', this.state.docDataList);
        });
      }
    }
  }

Метод HandleSubmit () (внутри класса родительского компонента)

// Submit button click handler
  handleSubmit(e) {

      let docDataList = this.state.docDataList;

      // if the user didn't touch any thing on the table rows
      // that means the list is empty and its length = 0
      if (docDataList.length === 0) {
        this.setState({
          alerts: {
            message: 'Please enter your document information ',
            show: true
          }
        });
      }
      // if the user has entered a data on the table row
      else if (docDataList.length > 0) {

        let data = new FormData(); // object which will be sent 

        // check the docDataList before request
        console.log('DocDataList before request:', docDataList); 
        data.append('docDataList', JSON.stringify(docDataList)); 

        fetch('http://localhost:8080/api/files/uploadFile', {
          method: 'POST',
          body: data
        }).then(response => {
          console.log('success document upload', response);
        }).catch(error => {
          console.log('error', error);
        });

        this.setState({
          formIsReadyToSubmit: true, 
          docDataList: [], // reset the list
          alerts: {updateAlert: true} // make an alert
        });
      }

    }

Чтобы увидеть, что показывает консоль, когда я заполняю строку данными: НАЖМИТЕ ЗДЕСЬ, ПОЖАЛУЙСТА

Чтобы увидеть ответ на запрос: НАЖМИТЕ ЗДЕСЬ, ПОЖАЛУЙСТА

ПРИМЕЧАНИЕ. После просмотра этих снимков экрана вы можете заметить, что существует дополнительный список данных, называемый "arrContrats", который я не упомянул в своей проблеме, потому что у него нет проблем; проблема со списком "docDataList". Заранее спасибо

Ответы [ 2 ]

0 голосов
/ 24 июля 2019
function stringifyFileObject(arrWithFiles = []) {
  const arrData = [];
   for (let i=0; i < arrWithFiles.length; i++) {
    const file = arrWithFiles[i];
    const obj = {
        lastModified: file.lastModified,
        name: file.name,
        size: file.size,
        type: file.type,
        webkitRelativePath: file.webkitRelativePath,
    }
    arrData.push( obj );
  }
}

Или что угодно, что соответствует вашим потребностям. Вы поняли ...

0 голосов
/ 13 ноября 2018

Если ваша проблема в том, что вы получаете объект File из браузера, а затем позже используете JSON.stringify на нем (или что-то содержащее его) и получаете {} для него в JSON, это правильно. Объект браузера File не имеет своих перечисляемых свойств. JSON.stringify включает только собственные перечисляемые свойства.

Если вы хотите, чтобы различные свойства, которыми обладают File объекты (унаследованные свойства доступа), вам необходимо скопировать их в новый объект.

Если вам нужны данные файла, вы не можете получить их напрямую от объекта File, для этого вы должны использовать FileReader.

...