-1

In my angular app I have a reactive form for editing values. I am stuck in put request only in the frontend. I am using multer.upload.fields because I've two different types of file inputs. One is for a single image and other is for an image array. The problem occurs while I try to update the form without uploading a new image. I have already set that If I don't get a new image in the form the image url remains the same. The put request is successful while I try with postman even If I don't pick a new Image it takes the old image url. But If I don't pick a new Image in frontend form I get this error : const file = req.files.image;^TypeError: Cannot read properties of undefined (reading 'image')

backend put request : Which works perfectly fine with postman even If I don't pick a new image.

 router.put('/:id',uploadOptions.fields([{name: 'image',maxCount: 1},{name: 'images',maxCount: 10}]), async (req, res)=>{
        if (!mongoose.isValidObjectId(req.params.id)) {
            return res.status(400).send('Invalid Product Id');
        }
        const category = await Category.findById(req.body.category);
        if (!category) return res.status(400).send('Invalid Category');
    
        const product = await Product.findById(req.params.id);
        if (!product) return res.status(400).send('Invalid Product!');
    
        const file = req.files.image;
        // console.log(typeof(file))
        let imagepath;
       
        const basePath = `${req.protocol}://${req.get('host')}/public/uploads/products/`;
    
        if (file) {
            const fileName = file.map(filename =>filename.filename) ;
            imagepath = `${basePath}${fileName}`;
        } else {
            imagepath = product.image;
        }
    
        const files = req.files.images;
        // console.log(files);
        let imagesPaths = [];
        if (files) {
            files.map((file) => {
                imagesPaths.push(`${basePath}gallery/${file.filename}`);
            });
        }
        else{
            imagesPaths = product.images
        }
        // console.log(imagesPaths)
    
        const updatedProduct = await Product.findByIdAndUpdate(
            req.params.id,
            {
                name: req.body.name,
                description: req.body.description,
                richDescription: req.body.richDescription,
                image: imagepath,
                images : imagesPaths,
                brand: req.body.brand,
                price : req.body.price,
                category: req.body.category,
                countInStock: req.body.countInStock,
                rating: req.body.rating,
                numReviews: req.body.numReviews,
                dateCreated: req.body.dateCreated
            },
            {new : true}
        ).then(productUpdated =>{
            if(productUpdated){
                res.status(200).json({message : 'Product updated!', product : {...productUpdated}})
            }
            else{
                res.status(400).json({success : false, message : 'Product did not update'})
            }
        }).catch(err =>{
            res.status(500).json({success : false, error : err})
        })
    })

products.service.ts - frontend service file image refers to the single image file and images refers to the images array.

 editProduct(id: string,name: string,description: string,richDescription : string,image : File | string,images: any = [], brand:string, price: any, category:Category,countInStock: any){
        let productData : Product | FormData; 
        if(typeof image === 'object'){
            productData = new FormData();
                productData.append('id',id)
                productData.append('name',name);
                productData.append('description',description);
                productData.append('richDescription', richDescription);
                productData.append('image',image,name);
                for(let image in images){
                    productData.append('images',images[image])
                }
                productData.append('brand',brand);
                productData.append('price',price);
                productData.append('category',category.toString());
                productData.append('countInStock',countInStock);
          }
        else {
            productData = {
                id : id, 
                name : name, 
                description : description, 
                richDescription: richDescription, 
                image : image, 
                images : images, 
                brand: brand, 
                price : price, 
                category : category,
                countInStock: countInStock
            }
        }
        // for(let i in productData){
        //     console.log(productData[i])
        // }
        this._http.put<{message : string; product : Product}>("http://localhost:3000/api/v1.0/products/" + id, productData)
        .subscribe(responseData => console.log(responseData))
    }

product-edit.ts

this.secondFormGroup = this._formBuilder.group({
      image : [{validators: [Validators.required], asyncValidators:[mimeType]}],
      images : this._formBuilder.array([])
    })
this.secondFormGroup.patchValue({image: this.product.image,images: this.product.images})
 OnUpdateProduct(){
    this._productService.editProduct(
       this.secondFormGroup.value.image,
      this.secondFormGroup.value.images,)}

If I run a for loop and get console.log(productData) all values. I can see the image url. for image value. If I try to save OnUpdateProduct() with picking a new image my product get updated. But If I don't pick an image my backend server crashes with the above error.

Dharman
  • 30,962
  • 25
  • 85
  • 135
debugger
  • 1,442
  • 1
  • 9
  • 14

1 Answers1

0

I have solved it. The error was occurring because of

if(typeof image === 'object'){....}else{productData....}

in my product.service.ts file. Solution : I have removed the if else condition and appending the productData. like :

let productData : Product | FormData; 
        productData = new FormData();
        productData.append('id',id)
        productData.append('name',name);
         ..................
  

 this._http.put<{message : string; product : Product}>("http://localhost:3000/api/v1.0/products/" + id, productData)
    .subscribe(responseData => console.log(responseData))

If I don't pick a new image which is type of object ,then the productData image were receiving the old string. But in the backend I was requesting a File instead of a string. Such a silly mistake. Apologies for asking an useless question. Check the question code if you don't know how to set image and image array in a single form.

debugger
  • 1,442
  • 1
  • 9
  • 14