Dynamically populating dropdown based on 2 JSON files in React - reactjs

I have 2 JSON files that contain data for regions and provinces respectively.
Here is a sample data for my regions.json
[
{
"id": 1,
"long_name": "First region in the country",
"name": "firstRegion",
"key": "FR"
},
{
"id": 2,
"long_name": "Second region in the country",
"name": "secondRegion",
"key": "SR"
}
]
And this is my provinces.json
[
{ "id": 1, "name": "province One", "region_key": "FR", "key": "p1" },
{ "id": 2, "name": "province Two", "region_key": "SR", "key": "p2" },
{ "id": 3, "name": "province Three", "region_key": "FR", "key": "p3" },
{ "id": 4, "name": "province Four", "region_key": "SR", "key": "p4" }
]
What I want to happen is when the user chooses a region from the first dropdown, for example, First region in the country, the second dropdown, which is the provinces dropdown, must be dynamically populated with the provinces that belong to First region in the country or provinces with the region_key FR. From this logic, I assume there must be some mapping that could happen. I referred to one of the answers here but unfortunately, when I try to edit the options value from the provinces' selectcomponent, I get an error props.options.map is not a function. Also, my JSON files, I assume, need a different style for mapping? Since I need to match the key from regions.json to region.key in provinces.json but I'm fairly new to rendering data from the database to the front-end using React.
Here is a code snippet for my dropdown:
/* Dropdown for Regions */
<h4 className="mb-3 text-sm">Regions</h4>
<Select
className="basic-multi-select text-sm"
classNamePrefix="select"
defaultValue={selectedRegions}
getOptionLabel={(region) => region.long_name}
getOptionValue={(region) => region.id}
isMulti
name="regions"
options={regions}
onChange={handleChangeRegion}
placeholder="Region select"
/>
/* Dropdown for Provinces */
<h4 className="mb-3 text-sm">Provinces</h4>
<Select
className="basic-multi-select text-sm"
classNamePrefix="select"
defaultValue={selectedProvinces}
getOptionLabel={(province) => province.name}
getOptionValue={(province) => province.id}
isMulti
name="provinces"
options={provinces}
onChange={handleChangeProvince}
placeholder="Province Select"
/>
This is my handleChangeRegion, for context.
const handleChangeRegion = (selectedRegions) => {
setSelectedRegions([...selectedRegions])
regionID = []
for (var i = 0; i < Object.keys(selectedRegions).length; i++) {
regionID[i] = selectedRegions[i].id
}
}

Here is how I would go about this:
use a useState hook for each selection to store the selected value (say active_region and active_province)
use region.key as value for the first dropdown
filter the provinces list based on the selected region_key and use this for the second dropdown:
provinces.filter(o => o.region_key === active_region)
This is, of course, assuming that you are using functional components (hence my mentioning of hooks)

Related

Display categories from API instead of writing manually in ReactJS

I have created a ReactJS application which can display the data of a restaurant menu from an API and it is working properly.
I have added a function to filter the data with left menu according to the category (for example if I want to see only items with category Pizza items I will click on Pizza, if I want to view only items with category Juices then on Juices).
So what I have done for my left menu is that I have written down all the categories and made use of a function to filter the data which makes me change the left menu whenever the API categories change.
Now I want to get the categories list in the left menu from my API so that I don't need to change the left menu every time I change the API.
How can I make changes in my code based on the requirement I have so that I can achieve the functionality I want.
My API data is in this way:
[
{
"Name": "Chicken pizza",
"Category": "Pizza",
"Type": "non-veg",
"Price": 376,
"id": "1"
},
{
"Name": "Paneer Cheese Pizza",
"Category": "Pizza",
"Type": "veg",
"Price": 350,
"id": "2"
},{
"Name": "Orange Juice",
"Category": "Juices",
"Type": "veg",
"Price": 45,
"id": "3"
},
{
"Name": "Badam Fruit Mix",
"Category": "Juices",
"Type": "veg",
"Price": 50,
"id": "4"
},
{
"Name": "Vanilla Icecream",
"Category": "Ice Creams",
"Type": "veg",
"Price": 50,
"id": "5"
}
]
Now the functionality I added to filter data using the side menu is this:
const filterItem = (category) =>{
const updatedItems = mainArray.filter((curcategory)=>{
return curcategory.Category === category;
})
setData(updatedItems);
}
<ul>
<li onClick={()=>setData(mainArray)}>All</li>
<li onClick={()=>filterItem("Pizza")}>Pizza</li>
<li onClick={()=>filterItem("Bread")}>Bread</li>
<li onClick={()=>filterItem("Shakes")}>Shakes</li>
<li onClick={()=>filterItem("Ice-Cream")}>Ice Cream</li>
<li onClick={()=>filterItem("Cakes")}>Cakes</li>
<li onClick={()=>filterItem("Juices")}>Juices</li>
</ul>
From here what are the I have to make so that I can display the categories of the items from API without the need to write it down myself.
I have created an array in this way for the categories:
const allCategory = ["All", ...new Set(filteredData.map((curElem)=> curElem.Category ))];
console.log(allCategory);
Now what are things I have to implement to achieve the functionality so that I can display the categories of the API in the left menu in my application. Guide me with whatever the knowledge you have which can help me overcome this problem and find the solution I'm searching for.
I'll give my sandbox link of my application for clear understanding of my code.
https://codesandbox.io/s/qr-menu-smvr6h?file=/src/App.js
I want to change my left menu from hardcoding and get the categories list from API.

Filtering list of products in React

pls help me to arrange filtering an array with currencies in React application.
i'm rendering the list of products. And it should contain price block. i have an array of prices in different currencies. (Actually it is an array of products, which contains subArray of prices in different currencies for each product). Like this:
"data": {
"category": {
"products": [
{
"id": "1",
"name": "Nike Air Huarache Le",
"gallery": [
"https://cdn.shopify.com/s/files/1/0087/6193/3920/products/DD1381200_DEOA_2_720x.jpg?v=1612816087",
"https://cdn.shopify.com/s/files/1/0087/6193/3920/products/DD1381200_DEOA_1_720x.jpg?v=1612816087"
],
"prices": [
{
"currency": {
"label": "USD",
"symbol": "$"
},
"amount": 144.69
},
{
"currency": {
"label": "GBP",
"symbol": "£"
},
"amount": 104
},
{
"currency": {
"label": "RUB",
"symbol": "₽"
},
"amount": 10941.76
}
]
},
there is a state which contains currency, which customer choose.
the problem is that i can not arrange filter to display in card list the price in chosen currency.
return (
data.category.products.map(({id, name, gallery, prices, currency, label, amount}) =>
<div key={id} >
<img src={gallery[0]} alt={id} /> // it works
<p>{name}</p> // it also works
//then i'd like to filter array of prices and find an object where price.currency.label === 'USD' (for example, presume that state is USD). Then i need to show to customer price in format: $ 144.69
so i make filter like this:
<p>{prices.filter((item) => {
{/* console.log(item.currency.label) */}
return item.currency.label.includes('USD').label })} </p>
id does not work. i think OK, filter returned me an array with only position which i have to display, let's make a map
<p>{prices.filter((item) => {
return item.currency.label.includes('USD').label}).map((el)=>el)}
</p>
it does not work again...
I tried many different ways, but all of them does not show neither price nor even currency. Pls help me to understand what i've missed.
You are not filtering correctly, what you need to do is the following:
// This will return a filtered prices array, which only includes the USD currency object as the element of index 0
prices.filter((item) => {
return item.currency.label.includes('USD')
})
// and in order to access the label you can do the following
prices.filter((item) => {
return item.currency.label.includes('USD')
})[0].currency.lable

Two kinds of labels for options in select dropdown

Using angularjs, I want to change what is being used as the label. I have objects which are either companies or people, and use a different value to display as the label. So with the example below, my dropdown options should read: Aida Whitburg, Jones Investments, Edison Yuen, using either the name as the value if it's a person or companyName if the entry is a company.
{ id: 1,
name: "Aida Whitburg",
type: "person"
},
{ id: 2,
companyName: "Jones Investments",
type: "company"},
{ id: 3,
name: "Edison Yuen",
type: "person"
}
<select id="entityDropdown"
ng-options="entity as entity.name for entity in ctrl.entities"
ng-model="ctrl.model.markedEntity" class="form-dropdown"
ng-change="ctrl.onChangeModel()">
</select>
make a new object with merged field and use it, something like that:
let people = [
{
"id": 1,
"name": "Aida Whitburg",
"type": "person"
},
{
"id": 2,
"companyName": "Jones Investments",
"type": "company"
},
{
"id": 3,
"name": "Edison Yuen",
"type": "person"
}
].map(p => {
p.label = p.name || p.companyName
return p;
});
console.log(people)

Reach form hooks complex nested object get and submit change to api

I have started to use this library and hit an issue.
Basically i have the following object structure which i receive from an api request.
{
"introduction": "hello",
"imageUri": "someimage.jpg",
"sections": [
{
"subSections": [
{
"sectionMedia": [
{
"externalUri": "https://vimeo.com/1212",
"id": 17127,
"type": "video",
"name": null,
"description": null,
"displayOrder": null,
}
],
"id": 172,
"name": "Section 1",
"displayOrder": 1,
},
{
"sectionMedia": [
{
"externalUri": "https://vimeo.com/1212",
"id": 178121,
"type": "video",
"name": null,
"description": null,
"displayOrder": null
}
],
"id": 178121,
"name": "Section 2",
"displayOrder": 2
},
],
"sectionMedia": [
{
"externalUri": "external.jpg",
"id": 176,
"type": "download",
"name": "Bar Modelling - Series 1 Workbook",
"description": null,
"displayOrder": null,
"createdAt": "2020-06-08T05:13:25+00:00",
"createdByUser": "/users/109",
"updatedAt": "2020-07-16T23:08:29+00:00",
"updatedByUser": "/users/109"
}
],
"id": 17111,
"name": "Series",
"description": "some description",
"displayOrder": 1,
]
}
So once the user has edited the data and this may not be all fields i need to submit and merge it with the object that we received and post full object back to the server.
However if i edit something within the array i need to show as the array name such as e.g if i updated the externalUri in sectionMedia with id 176 it should have the correct object structure when submitting the structure is flat like so:
name: "some name" externalUri: "newimage.jpg"
So wanted to see if there is a nice way to do this without actually searching the object and replacing when doing an onblur as will affect performance
Hope this makes sense
Not sure whether this is what you want but we can loop through the object
const [ fields, setFields ] = useState(
{
"externalUri": "external.jpg",
"id": 176,
"type": "download",
"name": "Bar Modelling - Series 1 Workbook",
"description": null,
"displayOrder": null,
"createdAt": "2020-06-08T05:13:25+00:00",
"createdByUser": "/users/109",
"updatedAt": "2020-07-16T23:08:29+00:00",
"updatedByUser": "/users/109"
})
We render the inputs here.
return Object.keys(fields).map( item => <input name={item} value={fields[item]} onChange={onChange} />
Then for the onchange.. we use the input name and add it back to the object.
const onChange = (e) => {
const value = e.target.value
const name = e.target.name
setFields( prev => ({ ...fields, [name]: value})) //this retains the original object, but change only the edited field
}
THOUGH.. submitting back the entire document back to the server is probably bad practice.
You should probably only submit fields that were changed.
WHY?
Because we cannot trust anything submitted from client, if you merge and resubmit the entire document, you need to make sure you re-validate the ENTIRE document.

How to select specific option using ng-options

I am starting with an array of sources
$scope.sources = [
{
"type": "register",
"name": "Register 1",
"balance": 100
},
{
"type": "register",
"name": "Register 2",
"balance": 100
},
{
"type": "register",
"name": "Register 3",
"balance": 200
},
{
"type": "office",
"name": "Change Drawer",
"balance": 200
},
{
"type": "office",
"name": "Safe",
"balance": 500
}
];
I'm successfully loading the options
<div class="form-group">
<label>Transfer <strong>{{amount(count, start, selectedItem.balance) | currency}}</strong> To:</label>
<select id="transferTo" class="form-control" ng-model="form.to" ng-options="item.name for item in sources | filter:{type:'office'}">
<option value="">-- Select a Source --</option>
</select>
</div>
I've tried using a $timeout function to select it after it works, but it doesn't pass back the correct value to my function
$timeout(function () {
$('#transferTo').val('1');
}, 200);
How would I set the "Safe" as the default option selected when the form loads?
You will need to set a value on your scope that you're setting ng-model equal to:
$scope.form.to = $scope.sources[4];
If your list (sources) is dynamic you can filter the array like this, which will return an array (but leave your array untouched).
filterFilter($scope.sources, {name: 'Safe'})
fiddle

Resources