'How to implement edit form with file upload

Trying to update a form without uploading a new image. I'm using multer for the image upload. it works very well when i create a form.

I'm using reactJs for frontend and node for the server side.

This is the front end code with react

import React, { useState, useEffect } from 'react';
import { useParams } from "react-router-dom";
import useFetch from "./fetch"; 

function EditForm() {  
    const { id } = useParams();
    const { contestant: form, error, isPending } = 
    useFetch("http://localhost:5000/dashboard/form_single/" + 
    id);
    
    const [name, setName] = useState('');
    const [address, setAddress] = useState('');
    const [fileName, setFileName] = useState('');

    useEffect(() => {
        setName(form.full_name);
        setAddress(form.home_address);
    }, [form])
    
        const editForm = async (id) => {
          try { 
            const formData = new FormData();
            formData.append("name", name);
            formData.append( "home_address", address);
            formData.append("image", fileName);

            const myHeaders = new Headers();
            myHeaders.append("jwtToken", localStorage.jwtToken);
      
            await 
fetch(`http://localhost:5000/dashboard/form_update/${id}`, {
              method: "PUT",
              headers: myHeaders,
              body: formData,
            });

          } catch (err) {
            console.error(err.message);
          }
        };

        const onChangeFile = e => {
          setFileName(e.target.files[0]);
        }

    return (
        <div>
            { isPending && <div>Loading...</div> }
            { error && <div>{ error }</div> }
            <form encType="multipart/form-data" 
             onSubmit={() => editForm(form.form_id)}>
                 <input
                  type="text"
                  className="form-control"
                  value={name || ''}
                 onChange={e => setName(e.target.value)}
                 />
                  <input
                  type="text"
                  className="form-control"
                  value={address || ''}
                  onChange={e => setAddress(e.target.value)}
                  />
                   <input
                   type="file"
                   id="update"
                   name="image"
                   onChange={onChangeFile}
                   />
                    <button type ="submit" >Save</button>
                    </form>
                    <div>
                      <img 
                      alt="contestant" 
          src= {`http://localhost:5000/upload/${form.passport}`} 
          className="rounded-circle" style={{width: "100px", 
           height: "100px"}}/>
        </div>
      </div>
    );
}

export default EditForm;

UNFORTUNATELY I GET Cannot read properties of undefined (reading 'filename'). I've tried to make multer image upload optional but it did'nt work. The code bellow is the api.

This is the server side code. Nodejs

router.put("/form_update/:id", upload.single('image'), async(req, 
res) => {
    try { 
       const { id } = req.params;
       const image = req.file.filename;
       const { name, home_address } = req.body;
       
      const updateForm = await pool.query("UPDATE form SET 
       full_name = $1, home_address = $2, passport = $3 WHERE 
       form_id = $4 
       RETURNING *", [name, home_address, image, id]);
        
       res.json("Form was updated");
      } catch (err) {
        console.error(err.message);
      }
});

how do i not always have to change image everytime i need to edit a form.



Solution 1:[1]

on the server, check if fields are provided, and then store values for each, and construct query dynamically, and when done checking, execute query

you could list fields in an array and then iterate them and construct query and values against req.body

you could also add some validation (check if there is id etc., you could add that on the front-end as well)

try this:

router.put("/form_update/:id", upload.single('image'), async(req, res) => {
  try {

    // fields. same as in req.body
    const fields = ['full_name', 'home_address', 'something', 'else'];

    // store values
    const values = [];

    // dynamic query string
    let stmt = [];

    const {id } = req.params;

    // add some validation
    if(!id) {
      console.error('no id..');
      return res.json({msg:"err: no id"});
    }

    const image = req.file ? req.file.filename : '';

    // build query
    fields.map((field)=>{

      if(req.body[field]) {
        values.push(req.body[field]);
        stmt.push(`${field} = $${values.length}`);
      }
    });

    // check image, as it's not in req.body
    if(image) {
      values.push(image);
      stmt.push(`passport = $${values.length}`);
    }    


    // no data..end
    if(!values.length) {
      console.log('no data..');
      return res.json({msg:'no data..'});
    }


    // finish
    stmt = "UPDATE form SET " + stmt.join(', ');
    
    values.push(id);
    stmt += ` WHERE form_id = $${values.length}`;

    stmt += ' RETURNING *';

    const updateForm = await pool.query(stmt, values);

    res.json({msg:"Form was updated"});

  } catch (err) {
    console.error(err.message);
  }
});

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1