I am using multiple dropdown list and fetching the data based on url. There are two dropdowns like below.
1 . data: [{"id": 1, "value":"Shoe"},{"id": 2, "value":"Clothing"}] --> categories
url -> '/api/products/category'
2. data : [{ "category_name": "Shoe", sizes: ['7','8','9'] }] --> available sizes
url -> '/products/${item_id}/category/${category_id}
Now, i am using service and vuex action to load dropdown based url.
Here is service
export async function fetchItemsOfDrowdown(url) { // passing the url
const resp = await http.get(url);
return resp.data;
}
Here is the action
async fetchDropdownItems({ state, commit }, payload) {
if (!state.fieldItems[payload.key]) {
const items = await service.fetchItemsOfDrowdown(payload.url);
const parsedItems = [];
for (let i = 0; i < items.length; i += 1) {
const item = items[i];
parsedItems.push({
text: item[payload.text],
value: item[payload.value]
});
}
commit('DropdownFieldItems', {
key: payload.key,
items: parsedItems
});
}
}
Mutation
DropdownFieldItems(state, payload) {
state.fieldItems = {
...state.fieldItems,
[payload.key]: payload.items
};
}
How i am configuring dropdown fields type
fields : {
category_id: {
key: 'category_id',
label: 'Category',
uri: '/products/category',
value: '',
items: []
},
product_size: {
key: 'product_size',
label: 'Available Size',
value: '',
uri: '/products/${item_id}/category/${category_id}
items: []
}
}
Calling the action into action and passing the data like this
created() {
if (this.fields && Object.keys(this.fields).length > 0) {
const fieldKeys = Object.keys(this.fields);
for (let i = 0; i < fieldKeys.length; i += 1) {
const key = fieldKeys[i];
if (this.fields[key] && this.fields[key].uri) {
this.fetchDropdownItems({
key,
url: this.fields[key].uri,
text: 'value',
value: 'id'
});
}
}
}
},
I want to store dropdown fields into state like this.
payload: object --> items: Array[2] key --> "category_id" --> for 1st one working fine
I want to format the 2nd url payload so we can store it as same as 1st one. How to format the 2nd payload structure like 1st one in action itself?
Related
I am using the NHL api to try to grab players stats for a given season. I have a utility function with these season values :
export const seasonOptions = [
{ value: "19861987", label: "1986/1987" },
{ value: "19871988", label: "1987/1988" },
{ value: "19881989", label: "1988/1989" },
{ value: "19891990", label: "1989/1990" },
{ value: "19901991", label: "1990/1991" },
{ value: "19911992", label: "1991/1992" },
{ value: "19921993", label: "1992/1993" },
{ value: "19931994", label: "1993/1994" },
{ value: "19941995", label: "1994/1995" },
{ value: "19951996", label: "1995/1996" },
];
... and so on. In my component I have this state to setSelect on what was selected:
const [select, setSelect] = useState(seasonOptions[seasonOptions.length - 1]);
const handler = (selected) => {
setSelect((select) => select);
handlePlayerStats(
`https://statsapi.web.nhl.com/api/v1/people/${props.playerId}/stats?stats=statsSingleSeason&season=${selected.value}`
);
}
};
<Select
id="select"
instanceId={"select"}
options={seasonOptions}
placeholder={select.label}
autoFocus
value={select.value}
onChange={handler}
/>
Which calls this custom hook:
const handlePlayerStats = async (url) => {
try {
const req = await fetch(url).then((response) => response.json());
console.log(req);
if (req.messageNumber) {
setFetchedData([]);
} else if (req.stats[0].splits.length > 0) {
setFetchedData(req);
}
} catch (error) {
console.log(error);
}
};
I'm not really sure how to go about looping through all the seasonOptions dynamically and filtering out each season where req.stats[0].splits.length === 0?
Here is the codesandbox link for anyone curious: https://codesandbox.io/s/friendly-kapitsa-c97rzy?file=/components/PlayerStats.js:357-855
To Answer The first parst of your question you can map over this Array of Objects with this method for example using the .map method
React Code SandBox
MDN-.map() method JS
export const seasonOptions = [
{ value: 8471214, label: "198601987" },
{ value: 8471215, label: "198701988" },
{ value: 8471216, label: "198801989" },
{ value: 8471217, label: "198901990" },
{ value: 8471218, label: "199001991" },
{ value: 8471219, label: "199101992" },
{ value: 8471220, label: "199201993" },
{ value: 8471221, label: "199301994" },
{ value: 8471222, label: "199401995" },
{ value: 8471223, label: "199501996" },
];
//MAKE SURE TO MAP OVER THE <option></option> tag not the <Select/>
//CHECK CODE SANDBOX FOR EXAMPLE IN PURE HTML5 AND REACT
{seasonOptions.map((option,index)=>
<Select
key={index}
id="select"
instanceId={"select"}
options={option?.value}
placeholder={option?.label}
autoFocus
value={select.value}
onChange={handler}
/>
)}
Check Out my Answer Here or for other examples here how to map over an array and access it values this method is just 1 of many .
How to find a value from array object in JavaScript? Stack Over Flow Question
For the Second Part Of the Question you can use the new SandBox
Steps
Change the value property here from a string to a number by removing the quotation marks export const seasonOptions = [{value: 8471214, label: "198601987"},]
Assign a useState hook to handle the active filtered item
const [activeFilter, setActiveFilter] = useState([]);
3.Assign an arrow function to handle Filtering the Seasons
using the setTimeOut() method setTimeout()-MDN-DOCS Where 500 is the time the function is executed for that time and
const handleSeasonsFilter = (item) => {
setActiveFilter(item);
setTimeout(() => {
if (!item) {
setFilterSeasons(item);
} else {
setFilterSeasons(
seasonOptions.filter((season) =>
seasonOptions?.includes(season?.label)
)
);
}
}, 500);
};
Pass that to url that searches the API url = `https://statsapi.web.nhl.com/api/v1/people/${activeFilter}/stats?stats=statsSingleSeason&season=200402005 Like in Line 65 in The Sand Box
Display Them using useEffect() hook also add in the values in the dependency array or leave it empty to avoid infinite loops.
useEffect(() => {
//debug data
console.log(stringDataDisplay);
setActiveFilter(activeFilter);
//DEBUG IF VALUE IF PASSED
//setDatasearched(activeFilter);
}, [
/**EMPTY DEPENDENCY ARRAY TO AVOID INFINITE LOOPS */
stringDataDisplay,
activeFilter
]);
in My Example i Displayed Them using another useState Hook and State action
const [datasearched, setDatasearched] = useState([])
& Finally Just Assigned a new const stringDataDisplay = JSON.stringify([datasearched]);
To Stringify the [datasearched] Array Here.
Note
Make sure to pass the handleSeasonsFilter to OnClick as an empty arrow function and pass the option.value property as a String so the API Accepts the request.
Hope this helps with your Example and Ill try to Check the code sandbox also with your method.
Bear in Mind i still i am developing this and i understand you want the values of the seasons to be shown when no player id is selected Am i correct?
I am trying to add values inside the object id. Object id is created before but I want to add more values in the future inside the object ID.
This is my MongoDB database:
[{
label: 'colors',
options: [
{ label: 'Black', value: 1 },
{ label: 'Green', value: 2 },
]
}]
My expectation is when I will send a new object it will insert a new object inside the options property. suppose my new object is {label: 'blue', value: 3} it will be like this:
[{
label: 'colors',
object: [
{ label: 'Black', value: 1 },
{ label: 'Green', value: 2 },
{label: 'blue', value: 3}
]
},
I am trying this way, I am storing the previous data value, inserting a new value, and sending it to the backend. but it's not working. is there any different way I can fix it?
const addAttributeValues = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
if (label === '') {
return
}
const slug = label?.split(' ').join('-')
const options: any = [] // previous value storing
let update; // inserting previous and new value
if (attr.options) { // checking if the previous data has options property or not
options.push(attr.options)
const length: any = options.length
update = { options: { ...options, [length]: { label, slug } }, id: attr._id }
}
else { // if option property doesn't exist it means no new value added before
update = { options: { label, slug }, id: attr._id }
}
fetch('http://localhost:5000/dashboard/attributes', {
method: 'PUT',
headers: { 'content-type': 'application/json' },
body: JSON.stringify(update)
})
.then(res => res.json())
.then(data => {
setLabel('')
setIfShouldUpdate(true)
})
}
Backend API, I use put method, I don't know is there any other method for inserting new values in the future.
// ADD ATTRIBUTES VALUE
app.put('/dashboard/attributes/', async (req, res) => {
const { options, id } = req.body
const filter = { _id: objectId(id) }
const updateDoc = {
$set: {
options: options
},
};
const result = await unityMartAttributes.updateOne(filter, updateDoc);
res.json(result)
console.log(result);
})
You need to use $push to add new object in your array. $set will reset your previous values with new one. Also don't use ObjectId, just pass simple id which needs to be string. And if you want to pass in ObjectId then use
mongoose.Types.Objectid(id)
I am trying to build a JSON file where I store all the answers from a Form. Some of the inputs are having an extra dataset attribute (data-tag). When I'm saving the Form I want to extract all these 'tags' in the JSON file as keys and as value the input's value. I've tried to do that by adding references to these inputs and taking the name of the tag with $refs.
I am getting an error:
Error in v-on handler: "TypeError: Cannot read property 'push' of undefined"
I am trying currently to store the 'tags' in a separate Array and then append this to the Form Output.
Not sure if this is the right solution, but I couldn't think of anything else, so feel free if you have any other idea.
Vue.js version: 2.6
vuetify.js version: 2.3
Form inputs:
<v-text-field label="ICD" id="pos_t_1" name="pos_t_1" ref="icd" data-tag="icd_tag" v-
model="textfield" hide-details="auto" />
<v-radio-group v-model="radio" hide-details="auto" row>
<v-radio
v-for="radio in group"
ref="radioGroup"
:key="radio.id"
:id="radio.id"
:name="radio.id"
color="primary"
:data-tag="radio.tag"
:label="radio.text"
:value="radio.text"
>
</v-radio>
</v-radio-group>
Script:
export default Vue.extend({
name: 'Test',
data: function () {
return {
tags: [],
radio: '',
group: [
{id: 'pos_r_2', text: 'Radio 1', tag: 'radio_tag_2'},
{id: 'pos_r_3', text: 'Radio 2', tag: 'radio_tag_3'},
{id: 'pos_r_4', text: 'Radio 3', tag: 'radio_tag_4'},
{id: 'pos_r_5', text: 'Radio 4', tag: 'radio_tag_5'},
],
}
},
methods: {
onSubmit() {
Object.keys(this.$refs).forEach((value) => {
const refs = this.$refs[value];
if (Array.isArray(refs)) {
for (let i = 0; i <= this.$refs[value].length; i++) {
let key = this.$refs[value][i].$attrs['data-tag']
this.tags[key].push(this.radio)
}
} else {
let key = this.$refs[value].$attrs['data-tag']
this.tags[key].push(this.textfield)
}
})
}
}
})
Form's JSON structure:
[{
"pos_t_1":"Test",
"pos_r_2":"",
"pos_r_3":"Radio 3",
"pos_r_4":"",
"pos_r_5":"",
}],
The JSON structure I want:
[{
"pos_t_1":"Test",
"icd_tag":"Test",
"pos_r_2":"",
"radio_tag_2":"",
"pos_r_3":"Radio 3",
"radio_tag_3":"Radio 3",
"pos_r_4":"",
"radio_tag_4":"",
"pos_r_5":"",
"radio_tag_5":"",
}],
When you try to push to an empty array you cannot push to a specified key as it does not exist.
So when you declare tags = [] and then try to push tags[key].push(value) tags[key] is undefined so push method is not available
Instead you change the onSubmit method as below
onSubmit() {
Object.keys(this.$refs).forEach((value) => {
const refs = this.$refs[value];
if (Array.isArray(refs)) {
for (let i = 0; i <= this.$refs[value].length; i++) {
let key = this.$refs[value][i].$attrs['data-tag']
this.tags[key] = this.radio
}
} else {
let key = this.$refs[value].$attrs['data-tag']
this.tags[key] = this.textfield
}
})
}
Thanks to #Donkarnash I did it like that:
Object.keys(this.$refs).forEach((value) => {
const refs = this.$refs[value];
if (Array.isArray(refs)) {
for (let i = 0; i < refs.length; i++) {
let key = refs[i].$attrs['data-tag']
if (refs[i].isActive === true) {
this.tags[key] = this.radio
}
else {
this.tags[key] = ''
}
}
} else {
let key = refs.$attrs['data-tag']
this.tags[key] = this.textfield
}
})
Maybe this will help also someone else.
I have a list of items. Items are fetched dynamically from an API and need to display in <select/>
I'm using semantic-ui react components.
options accepts an object in the form:
{ key: "", text: "", value: "" }
This is the form:
<Form size="large">
<Form.Select
fluid
label="Application Name"
name="appName"
onChange={(event) => fetchHandler(event)}
options={} --> Item to be passed
/>
</Form>
This is what I tried:
Here options is the response and I'm adding a item appName into a list appNames.
let appNames = [];
options.map((value) => {
value.map((app) => {
return appNames.push(app.appName);
});
});
const appNameHandler = () => {
let appOptions = [
{
key: '',
text: '',
value: '',
},
];
for (let key = 0; key >= appNames.length; key++) {
appOptions.push(...appNames, {
key: key,
text: appNames,
value: appNames,
});
}
console.log(appOptions);
return appOptions;
};
Is there any better workaround for this?
A little hard to tell what you are doing, but I would do it like this
const appNames = [];
options.forEach((value) => {
value.forEach((app, index) => {
appNames.push({
key: index,
text: app.appName,
value: app.appName,
});
});
});
and then pass appNames to options.
No need to use 'map' if you aren't using the returned array.
I receive arrays from an API in a JSON format. I want to merge them into the nested array called which consists of objects graphData. How do I extend the data object by one entry?
mounted() {
var i = 0;
const axios = require('axios');
//Usage
axios.get("https://3.11.40.69:9001/app/usageUnitForCustomer/1521/2019-12-30/2020-12-30")
.then(response => {
const time = response.data.event_time; //["2019-12-30T14:06:21.000Z", "2019-12-30T14:06:21.000Z"]
const unit = response.data.unit; //[44.67, 75.89]
for (i = 0; i < 1; i++) {
if (time[i] !== time[i + 1]) {
this.graphData[1].data.time[i] = unit[i];
}
}
})
//Revenue
axios.get("https://3.11.40.69:9001/app/revenueUnitForMachine/345/2019-12-30/2020-12-30")
.then(response => {
const time = response.data.event_time; //["2019-12-30T14:06:21.000Z", "2019-12-30T14:06:21.000Z"]
const unit = response.data.revenue; //[44, 75]
for (i = 0; i < 10; i++) {
if (time[i] !== time[i + 1]) {
this.graphData[0].data.time[i] = unit[i];
}
}
})
},
data() {
return {
graphData: [
{name: 'Revenue', data: {}},
{name: 'Usage', data: {}}
]
}
}
After executing the above code both data objects are still empty.
The outcome looks like this and following error message:
0:
name: "Revenue"
data: Object []
1:
name: "Usage"
data: Object []
Uncaught (in promise) TypeError: Cannot set property '0' of undefined
Expected outcome:
graphData: [
{name: 'Revenue', data: {'2019-12-30T14:06:21.000Z': 448, '2019-12-30T14:06:22.000Z': 44}},
{name: 'Usage', data: {'2019-12-30T14:06:21.000Z': 448, '2019-12-30T14:06:22.000Z': 44}}
]
Has anyone an idea?
One issue I see is that you access time[i] in this.graphData[1].data.time[i] = unit[i]; but it is not defined inside your objects.
Maybe initializing it as an array helps?
data() {
return {
graphData: [
{name: 'Revenue', data: { time: [] }},
{name: 'Usage', data: { time: [] }}
]
}
}
Edit: Ok, after you edited the expected outcome I see another issue.
Can you try setting the value like this?
this.graphData[1].data[time[i]] = unit[i];
Note the extra [] around time[i].
You cannot reach outside variables from within a .then() block. You should be able to make the whole outside block into an async and use await for your axios calls.
Try something like this:
async mounted() {
var i = 0;
const axios = require('axios');
//Usage
const response = await axios.get("https://3.11.40.69:9001/app/usageUnitForCustomer/1521/2019-12-30/2020-12-30");
const time = response.data.event_time;
const unit = response.data.unit;
for (i = 0; i < 1; i++) {
if (time[i] !== time[i + 1]) {
this.graphData[1].data.time[i] = unit[i];
}
}
...
... continue to use await for the rest of your axios calls.