ActionController::ParameterMissing (param is missing or the value is empty: asset):

152 Views Asked by At

I am pretty new to react on rails, I am sorry if this is something obvious! I know there are pages of this question, but none have fixed the error I am getting. Any advice is greatly appreciated!

When I submit a form from my assets Component my rails rails server returns-

ActionController::ParameterMissing (param is missing or the value is empty: asset):

app/controllers/api/assets_controller.rb:48:in `asset_params'
app/controllers/api/assets_controller.rb:14:in `create'

Route.rb

Rails.application.routes.draw do
  mount_devise_token_auth_for 'User', at: 'api/auth'
  # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html

  # Defines the root path route ("/")
  # root "articles#index"
  namespace :api do
    resources :areas do
      resources :assets
    end
  end

end

model

class Asset < ApplicationRecord
  belongs_to :area
  validates :name, :description, :barcode, :price, :pdate, :status, :img, :category, presence: true
end

assets_controller

class Api::AssetsController < ApplicationController
  before_action :set_area
  before_action :set_asset, only: [:show, :update, :destroy]

  def index
    render json: @area.assets
  end

  def show
    render json: @asset
  end

  def create
    @asset = @area.assets.new(asset_params)
    if @asset.save
      render json: @asset
    else
      errKey = @asset.errors.messages.keys[0].to_s
      errValue = @asset.errors.messages.values[0][0]
      render json: {errors: "#{errKey} #{errValue}"}, status: :unprocessable_entity
    end
  end

  def update
    if @asset.update(asset_params)
      render json:@asset
    else
      render json: {errors: @asset.errors }, status: :unprocessable_entity
    end
  end

  def destroy
    @asset.destroy
    render json: {message: "Asset Deleted"}
  end

  private
  
    def set_area
      @area = Area.find(params[:area_id])
    end
    
    def set_asset
      @asset = @area.assets.find(params[:id])
    end
    
    def asset_params
      params.require(:asset).permit(:name, :description, :barcode, :price, :pdate, :status, :img, :category)
      # params.fetch(:asset).permit(:name :description, :barcode, :price, :pdate, :status, :img, :category)
    end
end

###Note when I use params.fetch I still get the same error

AssetProvider.js

import React, {useState } from 'react';
import axios from 'axios';
import {useNavigate} from 'react-router-dom';

export const AssetContext = React.createContext();

export const AssetConsumer = AssetContext.Consumer;

const AssetProvider = ({ children }) => {
  const [assets, setAssets] = useState([])
  const [errors, setErrors] = useState([])
  

  const getAllAssets = (areaId) => {
    axios.get(`/api/areas/${areaId}/assets`)
    .then (res => setAssets(res.data))
    .catch(err => {
      setErrors({
        variant: 'danger',
        msg: err.response.data.errors.full_messages[0]
      })
    })
  }

  const addAsset = (areaId, asset) => {
    axios.post(`/api/areas/${areaId}/assets`, { asset })
      .then ( res => setAssets([...assets, res.data]))
      .catch(err => {
        setErrors({
        variant: 'danger',
        msg: Object.keys(err.response.data.errors)[0] + " " + Object.values(err.response.data.errors)[0][0]
      })
    })
  }

  const updateAsset = (areaId, id, asset) => {
    axios.put (`/api/areas/${areaId}/assets/${id}`, {asset})
    .then ( res => {
      const newUpdatedAssets = assets.map(a => {
        if (a.id === id) {
          return res.data 
        }
        return a
      })
      setAssets(newUpdatedAssets)
    })
      .catch(err => {
        setErrors({
        variant: 'danger',
        msg: Object.keys(err.response.data.errors)[0] + " " + Object.values(err.response.data.errors)[0][0]
      })
    })
  }

   const deleteAsset = (areaId, id) => {
     axios.delete(`/api/areas/${areaId}/assets/${id}`)
     .then (res => {
       setAssets(assets.filter(a => a.id !== id))
     })
     .catch(err => {
      setErrors({ 
        variant: 'danger',
        msg: err.response.data.errors[0]
      })
    })
   }

  return (
    <AssetContext.Provider value={{
      assets,
      errors,
      setErrors,
      getAllAssets,
      addAsset,
      updateAsset,
      deleteAsset,
    }}>
    {children}
    </AssetContext.Provider>
  )
}
export default AssetProvider;

AssetForm.js

import { useState} from 'react'
import {Form, Button} from 'react-bootstrap'
import {AssetConsumer} from '../../providers/AssetProvider'
import { useParams} from 'react-router-dom'



const AssetForm = ({ setAdd, addAsset}) => {
    const [asset, setAsset] = useState ({ name: '', img: '', barcode: '', description: '', category: '', price: '', pdate: '', status: ''})
    const { areaId } = useParams();



    const handleSubmit = (e) => {
        e.preventDefault()
        addAsset(areaId, asset)
        setAdd(false)
        setAsset({ name: '', img: '', barcode: '', description: '', category: '', price: '', pdate: '', status: ''})
    }

    return (
        <>
        <Form onSubmit={handleSubmit}>
      <Form.Group className="mb-3" >
        <Form.Label>Asset Image</Form.Label>
        <Form.Control  
            name='img'
            value={asset.img}
            onChange={(e) => setAsset({...asset, img: e.targetvalue})}
        />
      </Form.Group>
      <Form.Group className="mb-3" >
        <Form.Label>Name</Form.Label>
        <Form.Control  
            name='name'
            value={asset.name}
            onChange={(e) => setAsset({...asset, name: e.targetvalue})}
            autoFocus
            required
        />
      </Form.Group>
      <Form.Group className="mb-3" >
        <Form.Label>Barcode</Form.Label>
        <Form.Control  
            name='barcode'
            value={asset.barcode}
            onChange={(e) => setAsset({...asset, barcode: e.targetvalue})}
          
        />
      </Form.Group>
      <Form.Group className="mb-3" >
        <Form.Label>Description</Form.Label>
        <Form.Control  
            name='description'
            value={asset.description}
            onChange={(e) => setAsset({...asset, description: e.targetvalue})}
            as="textarea"
            rows={3}
            
        />
      </Form.Group>
      <Form.Group className="mb-3" >
        <Form.Label>Category</Form.Label>
        <Form.Select name='category'
            value={asset.category}
            onChange={(e) => setAsset({...asset, category: e.targetvalue})}
           
            >
            <option>Select from list</option>
            <option value="camera">Camera</option>
            <option value="light">Light</option>
            <option value="tripod">Tripod</option>
        </Form.Select>
      </Form.Group>
      <Form.Group className="mb-3" >
        <Form.Label>Price</Form.Label>
        <Form.Control  
            name='price'
            value={asset.price}
            onChange={(e) => setAsset({...asset, price: e.targetvalue})}
        />
      </Form.Group>
      <Form.Group className="mb-3" >
        <Form.Label>Purchase Date</Form.Label>
        <Form.Control  
            name='pdate'
            value={asset.pdate}
            onChange={(e) => setAsset({...asset, pdate: e.targetvalue})}
            
        />
      </Form.Group>
      <Form.Group className="mb-3" >
        <Form.Label>Status</Form.Label>
        <Form.Control  
            name='status'
            value={asset.status}
            onChange={(e) => setAsset({...asset, status: e.targetvalue})}
        />
      </Form.Group>

      <Button variant="primary" type="submit">
        Submit
      </Button>
    </Form>
        </>
    )
}

const ConnectedAssetForm = (props) => (
    <AssetConsumer>
        { value => <AssetForm {...value} {...props} />}
    </AssetConsumer>
)

export default ConnectedAssetForm;

Schema.rb

create_table "areas", force: :cascade do |t|
    t.string "name"
    t.string "address"
    t.string "city"
    t.string "country"
    t.integer "zip"
    t.string "mcontact"
    t.string "pic"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "assets", force: :cascade do |t|
    t.string "name"
    t.string "description"
    t.string "barcode"
    t.decimal "price"
    t.datetime "pdate"
    t.string "status"
    t.string "img"
    t.bigint "area_id", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.string "category"
    t.index ["area_id"], name: "index_assets_on_area_id"
  end

enter image description here- Example of State being undefined while filling out the form

enter image description here- Example of network payload on form submit.

I have tried adding the :id to the assets model and controller but I still get the same error on my rails server.

I have also tried in the form doing the following for example

    <Form.Group className="mb-3" >
        <Form.Label>Asset Image</Form.Label>
        <Form.Control  
            name='asset[img]'
            value={asset.img}
            onChange={(e) => setAsset({...asset, img: e.targetvalue})}
        />
      </Form.Group>

but it still returns ActionController: :ParameterMissina (param is missing or the value is empty: asset) :

Thank you!

0

There are 0 best solutions below