If you are using Formik and class component the please see the following sloution:
let say I have following json on every attribute need to create one field in the create product form:
{
"name": "keychoda",
"attributes": {
"General_Attributes": [
{
"name": "Product Type",
"code": "product_code",
"mandatory": "1",
"type": "text",
"max_length": "30",
"default_value": "cellularphonecase",
"Hint": "Enter Feed Product Type here",
"AmazonExportHeader": "feed_product_type",
"ShopifyExportHeader": "Type",
"EbayExportHeader": "*Category",
"GoogleExportHeader": "Google Shopping / Google Product Category"
},
{
"name": "Shop Keeping Unit",
"code": "shop_keeping_unit",
"mandatory": "1",
"type": "text",
"max_length": "12",
"default_value": "",
"Hint": "Enter Product SKU Number",
"AmazonExportHeader": "item_sku",
"ShopifyExportHeader": "Variant SKU",
"EbayExportHeader": "CustomLabel",
"GoogleExportHeader": ""
},
{
"name": "Brand Name",
"code": "brand_name",
"mandatory": "1",
"type": "dropdown",
"options": [
"Oxnoble Group",
"Membrane"
],
"max_length": "",
"default_value": "Oxnoble Group",
"Hint": "Select Brand Name",
"AmazonExportHeader": "item_sku",
"ShopifyExportHeader": "Vendor",
"EbayExportHeader": "*C:Brand",
"GoogleExportHeader": ""
},
{
"name": "Barcode",
"code": "barcode",
"mandatory": "1",
"type": "number",
"min_length": "13",
"max_length": "13",
"default_value": "",
"Hint": "Enter EAN / GTIN HERE",
"AmazonExportHeader": "external_product_id",
"ShopifyExportHeader": "Variant Barcode",
"EbayExportHeader": "Product:EAN",
"GoogleExportHeader": ""
},
{
"name": "Barcode Type",
"code": "barcode_type",
"mandatory": "1",
"type": "dropdown",
"options": [
"GTIN",
"EAN",
"UPC",
"ASIN"
],
"min_length": "",
"max_length": "",
"default_value": "",
"Hint": "Select Barcode Type",
"AmazonExportHeader": "external_product_id_type",
"ShopifyExportHeader": "",
"EbayExportHeader": "C:Type",
"GoogleExportHeader": ""
},
{
"name": "condition_type",
"code": "condition_type",
"mandatory": "1",
"type": "dropdown",
"options": [
"New",
"Used - Acceptable",
"Used - Good",
"Used - Like New",
"Used - Very Good"
],
"min_length": "",
"max_length": "",
"default_value": "",
"Hint": "condition_type",
"AmazonExportHeader": "condition_type",
"ShopifyExportHeader": "",
"EbayExportHeader": "",
"GoogleExportHeader": ""
},
{
"name": "condition_note",
"code": "condition_note",
"mandatory": "1",
"type": "text",
"min_length": "",
"max_length": "",
"default_value": "",
"Hint": "condition_note",
"AmazonExportHeader": "condition_note",
"ShopifyExportHeader": "",
"EbayExportHeader": "",
"GoogleExportHeader": ""
}
],
"Variation_Attributes": [
],
"Dimentions_Attributes": [
{
"name": "image1",
"code": "image1",
"mandatory": "1",
"type": "file",
"default_value": "",
"Hint": "condition_type",
"AmazonExportHeader": "condition_type",
"ShopifyExportHeader": "",
"EbayExportHeader": "",
"GoogleExportHeader": ""
},
{
"name": "image2",
"code": "image2",
"mandatory": "1",
"type": "file",
"default_value": "",
"Hint": "condition_type",
"AmazonExportHeader": "condition_type",
"ShopifyExportHeader": "",
"EbayExportHeader": "",
"GoogleExportHeader": ""
},
{
"name": "Short Description",
"code": "short_description",
"mandatory": "0",
"type": "textarea",
"min_length": "",
"max_length": "",
"default_value": "",
"Hint": "condition_note",
"AmazonExportHeader": "condition_note",
"ShopifyExportHeader": "",
"EbayExportHeader": "",
"GoogleExportHeader": ""
}
]
}
}
createproduct.js
import React from 'react';
import { connect } from 'react-redux';
import { axiosInstance } from "../../network/apis/index";
import { END_POINTS } from "../../utils/Constants";
import { Link } from "react-router-dom";
import DashboardTemplate from '../../components/DashboardTemplate';
import AttributeSet from '../../components/AttributeSet';
import { Formik, Form, Field } from 'formik';
import { ToastContainer, toast } from 'react-toastify';
import { Btn } from '../../components/Controls/Button/Button';
class CreateProduct extends React.Component {
constructor(props) {
super(props);
this.state = {
redirect: false,
catId: "",
attributes : {},
initialvalues: {}
}
}
componentDidMount() {
const params = new URLSearchParams(window.location.search);
let cat;
this.props.Categories.some( (item) => {
return (item._id === params.get('catid')) ? cat = item : "";
});
let iniValues = {};
const obj = Object.values(cat.attributes);
obj.map( entries => {
if(entries.length > 0) {
entries.map(entry => {
let name = "\""+ entry.code + "\"";
if(entry.type == "file") {
iniValues[name] = null;
} else {
iniValues[name] = "";
}
})
}
});
this.setState(
{
catId: cat._id,
attributes: cat.attributes,
initialvalues: iniValues
}
);
}
handleProduct = async (values) => {
console.log(values);
let newProductPayload = {
name: "",
category_id: "",
attributes: {}
}
let combined = [];
Object.entries(this.state.attributes).map( attributes => {
combined = combined.concat(...attributes[1]);
});
//console.log(combined);
this.buildProductPayload(values, newProductPayload, combined);
console.log(newProductPayload);
let url = END_POINTS.createProduct;
try {
const response = await axiosInstance.post(url,newProductPayload);
console.log(response);
toast.success("Product saved");
this.setState({redirect: true})
} catch (error) {
console.log(error);
toast.error("Unable to save Product");
}
}
buildProductPayload(values, newProductPayload, combined) {
Object.entries(values).map((value) => {
if (value[0] == "name") {
newProductPayload.name = value[1];
newProductPayload.category_id = this.state.catId;
} else {
for (let i = 0; i < combined.length; i++) {
if (combined[i].code == value[0]) {
newProductPayload.attributes[value[0]] = { value: value[1], AmazonExportHeader: combined[i].AmazonExportHeader, EbayExportHeader: combined[i].EbayExportHeader, GoogleExportHeader: combined[i].GoogleExportHeader, ShopifyExportHeader: combined[i].ShopifyExportHeader };
break;
}
}
}
});
}
render() {
console.log(this.state.initialvalues,this.state.attributes);
return (
<DashboardTemplate>
<div className="content_header_bar">
<div className="content_header_bar_child content_bar_left">
<h3>Create Product</h3>
</div>
<div className="content_header_bar_child content_bar_right">
<Link to="/category-select" className="gray-button">Back</Link>
</div>
</div>
<div className="create_product_main">
<div className="create_product_inner">
<ToastContainer />
<Formik
initialValues={ this.state.initialvalues }
onSubmit={(values, { setSubmitting }) => {
setTimeout(() => {
setSubmitting(false);
this.handleProduct(values);
}, 500);
}}
>
{({ submitForm, isSubmitting, setFieldValue, values }) => (
<Form>
<div className="create_product_item">
<AttributeSet title="Product Name" fieldname="name" type="text" required="1" />
</div>
{ Object.keys(this.state.attributes).map((attribute,i) => {
return(
<div key={i} className="create_product_item">
<div className="attribute_set_name">{ attribute.replace("_", " ") }</div>
{this.state.attributes[attribute].length > 0 && this.state.attributes[attribute].map((entry,index) => {
return(
<AttributeSet key={index} title={ entry.name } fieldname={entry.code} type={ entry.type } options={entry.options} required={entry.mandatory} sfv={setFieldValue} values={values} />
)
})}
</div>
)
})}
<Btn className="create_button create_product_button" handleClick={submitForm} text="Create" />
</Form>
)}
</Formik>
</div>
</div>
</DashboardTemplate>
)
}
}
const mapStateToProps = ({Categories}) => {
return {
Categories: Categories
}
}
export default connect(mapStateToProps,null)(CreateProduct);
and child component which is responsible to return dynamic form is Attributeset.js
import React, {Component} from "react";
import { Field } from 'formik';
import PlaceholderImage from '../../assets/images/image-placeholder.jpg';
class AttributeSet extends Component {
constructor(props) {
super(props)
this.state = {
file: PlaceholderImage
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(event) {
//console.log(this.props.sfv);
const reader = new FileReader();
reader.onload = () => {
if (reader.readyState === 2) {
this.setState({file: reader.result})
}
}
reader.readAsDataURL(event.target.files[0]);
//console.log(this.props.values);
this.props.sfv(this.props.fieldname, event.currentTarget.files[0]);
//console.log(this.props.values);
}
render() {
let column = 'four_column';
var inputValue = '';
switch (this.props.type) {
case 'textarea':
column = 'full_width';
inputValue = <Field
className="attribute_input"
name={this.props.fieldname}
type="textarea"
rows="4" cols="50"
placeholder={
this.props.title
}
/>
break;
case 'file':
inputValue = <>
<img src={
this.state.file
}
alt=""
className="image_preview"/>
<input
className="attribute_input"
name={this.props.fieldname}
type="file"
onChange={this.handleChange}
/>
</>
break;
case 'dropdown':
inputValue = <Field
as="select"
name={this.props.fieldname}
className="attribute_input"
>
<option value=""></option>
{ this.props.options && this.props.options.map( (option,i) => {
return (
<option key={i} value={ option }>{ option }</option>
);
})}
</Field>
break;
default:
inputValue = <Field
className="attribute_input"
name={this.props.fieldname}
type="text"
placeholder={
this.props.title
}
/>
break;
}
return (
<div className={
`attibute_set ${column}`
}>
<div className="attibute_name">
{
this.props.title
}</div>
<div className="attribute_input">
{inputValue}
<div className="attribute_field_error"></div>
</div>
</div>
);
}
}
export default AttributeSet;