Filters in VueJs - javascript

I'm trying to build a small application in Vuejs 2.0, where I'm using v-select component, I'm having a data format something like this:
{
"model":
[
{
"id":1,
"salutation":"Mr",
"first_name":"Rahul",
"last_name":"Bashisht",
"number":"9876521102",
"email":"rahul#icicibank.com",
"company":
{
"id":1,
"name":"ICICI Bank",
"is_client":1,
}
},
{
"id":2,
"salutation":"Mr",
"first_name":"Vikash",
"last_name":"Pandey",
"number":"0987654345",
"email":"vikash#hdfc.com",
"company":
{
"id":2,
"name":"HDFC Bank",
"is_client":0,
}
}
]
}
Now I'm setting this to a variable model and then trying to filter with client = 1 in computed property something like this:
contactClients: function() {
if(this.model)
{
return this.model
.filter(f => (f.company.is_client == 1))
.map(d => ({label: d.first_name+' '+d.last_name+' - '+d.company.name, value: d.id}))
}
},
Then I'm placing it in v-select options as:
<v-select multiple :options="contactClients" v-model="clientParticipants"></v-select>
Now I'm having a other v-select which is in accordance with company name but is_client is true, so I'm trying something like this:
I've data set of companies:
{
"model":
[
{
"id":1,
"name":"ICICI Bank",
"is_client":1,
},
{
"id":2,
"name":"HDFC Bank",
"is_client": 0,
},
{
"id":3,
"name":"BNP Paribas",
"is_client": 0,
}
{
"id":4,
"name":"Barclays Bank",
"is_client": 1,
}
]
}
I'm placing it in companies variable and filtering it something like this:
clients: function () {
if(this.companies)
{
return this.companies
.filter(f => f.is_client == 1)
.map(d => ({label: d.name, value: d.id}))
}
}
And in v-select I'm having:
<v-select :options="clients" v-model="summary.client"></v-select>
I want to have an extra filter in accordance to the selection of contactsClients, i.e. if any contactsClients are being selected in the first list, second list should have only those company as option and if there is no selection in first list(contactClients) then second list should have all default options with simple is_client filter which is in current situation. Since the selection in contactClients is multiple so I don't know how to filter elements. Please guide me.
Edit: Codepen Link

May be this will help you.
contactClients: function() {
if (this.model) {
return this.model.filter(f => f.company.is_client == 1).map(d => ({
label: d.first_name + " " + d.last_name + " - " + d.company.name,
value: d.id,
companyId: d.company.id
}));
}
},
clients: function() {
var self = this;
var res = [];
if (this.companies) {
if (this.clientParticipants.length) {
console.log(this.clientParticipants)
this.clientParticipants.forEach(function(cc) {
self.companies.forEach(function(c) {
if (cc.companyId === c.id) {
res.push(c);
}
});
});
return res.map(d => ({ label: d.name, value: d.id }));
} else {
return this.companies
.filter(f => f.is_client == 1)
.map(d => ({ label: d.name, value: d.id }));
}
}
}
example here

You could use a computed property to implement your filter logic there and bind it to your second list.
Ref.: https://v2.vuejs.org/v2/guide/computed.html#Computed-Properties

Related

How do I populate an array of objects where every object has an array inside of it using response from rest API?

I can't google the right solution for this for about an hour straight,
So I'm getting a response from the API that looks like this:
[
{
"Name": "name1",
"Title": "Name One",
"Children": [
{
"Name": "Name 1.1",
"Title": "Name one point one"
},
]
And I need it to fit this kind of "mold" for the data to fit in:
{
title: 'Name One',
value: 'name1',
key: '1',
children: [
{
title: 'Name one point one',
value: 'Name 1.1',
key: 'key1',
},
I am trying to achieve this using a foreach but It's not working as intended because I need to do this all in one instance of a foreach.
Here's what I gave a go to(vue2):
created() {
getData().then(response => {
const formattedResponse = []
response.forEach((el, key) => {
formattedResponse.title = response.Title
formattedResponse.name = response.Name
formattedResponse.children = response.Children
})
})
Use map over the main array and use destructuring assignment to extract the properties by key, and relabel them, and then do exactly the same with the children array. Then return the updated array of objects.
const data=[{Name:"name1",Title:"Name One",Children:[{Name:"Name 1.1",Title:"Name one point one"}]},{Name:"name2",Title:"Name Two",Children:[{Name:"Name 1.2",Title:"Name one point two"}]}];
const result = data.map((obj, key) => {
const { Title: title, Name: value } = obj;
const children = obj.Children.map(obj => {
const { Title: title, Name: value } = obj;
return { title, value, key: (key + 1).toString() };
});
return { title, value, children };
});
console.log(result);
Your API response is JSON. All you need to do is:
var resp=JSON.parse(API response);

Loop through and delete elements in an array of objects

In my Vue.js project I have an array of objects which I want to list through and display in the browser.
My array contains four objects, I want to display only 3. The way I choose the 3 objects are dependent on a preference setting that the user has chosen somewhere else in the project and stored in a variable (below it is called userPreference). I am currently stuck on the best and most efficient way to remove one of the objects from my array based on the userPreference value.
My v-for in my template
<ul v-for="item in getOutroItems"><li>item<li></ul>
My object:
data() {
return {
outroItems: [{ title: "outro1", text: "XYZ" }, { title: "outro2", text: "ABC" }, { title: "outro3",
text`enter code here`: "QRS" }, { title: "outro4", text: "TUV" }],
userPreference: ""
};
}
My computed property (this is what I have so far)
getOutroItems() {
this.outroItems.filter((value) => {
if(this.userPreference === "newsletter") {
/// here I want to remove outro2 from my array and return an array with the other 3 values
} else (this.userPreference === "noNewsletter") {
/// here I want to remove outro3 from my array and return an array with the other 3 values
}
})
}
So, what is the best way to remove a specific element from an array?
Thanks in advance, and let me know if anything wasn't clear enough.
Your requirement can be fulfilled by below code as array.filter just wants true or false in its return to accept or remove an element from its array.
getOutroItems() {
this.outroItems.filter((value) => {
if(this.userPreference === "newsletter") {
// here I want to remove outro2 from my array and return an array with the other 3 values
return value.title != 'outro2';
} else (this.userPreference === "noNewsletter") {
// here I want to remove outro3 from my array and return an array with the other 3 values
return value.title != 'outro3';
}
})
}
However if you want to not create another array if it is big. you should go with swapping such elements to be removed with the end indexed element in the array and popping those many elements from the array.
There are multiple ways of getting the correct items from an array.
My preferred method and in your example: Using array.filter
const outroItems = [
{ title: "outro1", text: "XYZ" },
{ title: "outro2", text: "ABC" },
{ title: "outro3", text: "QRS" },
{ title: "outro4", text: "TUV" }
];
const leftOverItems = outroItems.filter((item) => item.title !== "outro2");
console.log(leftOverItems);
Another option is to find the index of the item to remove and then remove it with splice
const outroItems = [
{ title: "outro1", text: "XYZ" },
{ title: "outro2", text: "ABC" },
{ title: "outro3", text: "QRS" },
{ title: "outro4", text: "TUV" }
];
const itemToDelete = outroItems.find((item) => item.title === "outro2");
const indexToDelete = outroItems.indexOf(itemToDelete);
outroItems.splice(indexToDelete, 1);
console.log(outroItems);
Combining any of the functions above with a function will prevent you from writing duplicate code.
const itemToRemove = (arr, attr, name) => {
return arr.filter((item) => item[attr] !== name);
}
const outroItems = [
{ title: "outro1", text: "XYZ" },
{ title: "outro2", text: "ABC" },
{ title: "outro3", text: "QRS" },
{ title: "outro4", text: "TUV" }
];
// Remove from "outroItems" where "title" is "outro2"
const removed2 = itemToRemove(outroItems, "title", "outro2");
// Remove from "outroItems" where "title" is "outro3"
const removed3 = itemToRemove(outroItems, "title", "outro3");
// Remove from "outroItems" where "text" is "TUV"
const removedTUV = itemToRemove(outroItems, "text", "TUV");
console.log(removed2);
console.log(removed3);
console.log(removedTUV);

Search implementation for Multilevel array

i have JSON like below.
const testData = [
{
menu: 'Test',
submenu: [
{
menu: 'Test1',
submenu: [
{
menu: 'Test1.1',
},
{
menu: 'Test1.2',
},
{
secondLevel: [
{
menu: 'Test1.3',
submenu: [
{
menu: 'Test1.4',
},
{
menu: 'Test1.5',
},
],
},
],
},
],
},
i have used reduce function to traverse to search the expected word like Test1.1 i am getting proper value, whereas while search the Test1.4 is not coming up properly as it has secondLevel as parent object.
The code i used is below which is suggested in stackoverflow.
function search(data, value) {
return data.reduce((r, e) => {
const object = { ...e }
const result = search(e.submenu || [], value)
if (result.length) object.submenu = result
if (e.menu == value || result.length) r.push(object)
return r;
}, [])
}
Please suggest best way to search the element in secondLevel object as well. Thanks in advance

Updated nested object by matching ID

I have an array with nested objects that I need to update from another array of objects, if they match.
Here is the data structure I want to update:
const invoices = {
BatchItemRequest: [
{
bId: "bid10",
Invoice: {
Line: [
{
SalesItemLineDetail: {
ItemAccountRef: { AccountCode: "10110" },
},
},
{
SalesItemLineDetail: {
ItemAccountRef: { AccountCode: "11110" },
},
Amount: 2499,
},
],
},
},
{
bId: "bid10",
Invoice: {
Line: [
{
SalesItemLineDetail: {
ItemAccountRef: { AccountCode: "10110" },
},
},
{
SalesItemLineDetail: {
ItemAccountRef: { AccountCode: "10111" },
},
Amount: 2499,
},
],
},
},
],
};
Here is the array of objects I want to update it from:
const accounts = [
{ AccountCode: "10110", Id: "84" },
{ AccountCode: "11110", Id: "5" },
{ AccountCode: "10111", Id: "81" },
];
I want to update invoices, using accounts, by inserting Id if AccountCode matches, to get the following structure:
const invoices = {
BatchItemRequest: [
{
bId: "bid10",
Invoice: {
Line: [
{
SalesItemLineDetail: {
ItemAccountRef: { AccountCode: "10110", Id: "84" },
},
},
{
SalesItemLineDetail: {
ItemAccountRef: { AccountCode: "11110", Id: "5" },
},
Amount: 2499,
},
],
},
},
{
bId: "bid10",
Invoice: {
Line: [
{
SalesItemLineDetail: {
ItemAccountRef: { AccountCode: "10110", Id: "84" },
},
},
{
SalesItemLineDetail: {
ItemAccountRef: { AccountCode: "10111", Id: "81" },
},
Amount: 2499,
},
],
},
},
],
};
I have tried various methods, such as the following:
const mapped = invoices.BatchItemRequest.map((item1) => {
return Object.assign(
item1,
accounts.find((item2) => {
return item2 && item1.Invoice.Line.ItemAccountRef.AccountCode === item2.AccountCode;
})
);
});
Problem with this approach (it doesn't work as I think I need to do another nested map), but it also creates a new array, only including the nested elements of invoices.
Does anyone know a good approach to this?
This isn't the cleanest of code but it gets the job done:
function matchInvoiceWithAccount(invoices, accounts) {
const mappedInvoices = invoices.BatchItemRequest.map((request) => {
// Shouldn't modify input parameter, could use Object.assign to create a copy and modify the copy instead for purity
request.Invoice.Line = request.Invoice.Line.map((line) => {
const accountCode = line.SalesItemLineDetail.ItemAccountRef.AccountCode;
// If accounts was a map of AccountCode to Id you would't need to search for it which would be more effective
const account = accounts.find((account) => account.AccountCode === accountCode);
if (account) {
line.SalesItemLineDetail.ItemAccountRef.Id = account.Id;
}
return line;
});
return request;
});
return {
BatchItemRequest: mappedInvoices,
};
}
What you could and probably should do to improve this is to not modify the input parameters of the function, but that requires that you in a better way copy the original, either using Object.assign or spread operator.
At first, it will be good to create Map from your accounts array. We will go one time for array with O(n) and then will read ids by code with O(1). And nested fors is O(m*n), that will be much more slower at big arrays.
const idsByAccountCodes = new Map();
accounts.forEach((data) => {
idsByAccountCodes.set(data.AccountCode, data.Id);
})
or shorter:
const idsByAccountCode = new Map(accounts.map((data) => [data.AccountCode, data.Id]))
then if you want to mutate original values you can go through all nesting levels and add values
for ( const {Invoice:{ Line: line }} of invoices.BatchItemRequest){
for ( const {SalesItemLineDetail: {ItemAccountRef: item}} of line){
item.Id = idsByAccountCodes.get(item.AccountCode) || 'some default value'
// also if you don't have ids for all codes you need to define logic for that case
}
}
If you don't need to mutate original big object "invoices" and all of nested objects, then you can create recursive clone of if with something like lodash.cloneDeep

spread prevState with variable

I am trying to refactor a codebase and I got stuck somewhere. I am basically trying to update the state based on the onChange event of a select box.
In this case, my searchCriteria parameter in my handleFilterChange functions is ingredients.
But let's assume I have another select box that contains the options of anotherIngredients. I couldn't spread the prevState based on a variable.
I had to type ...prevState.ingredients
Is there a proper way to do that?
So my component state is:
state = {
ingredients: {
name: "",
operation: "",
value: ""
},
anotherIngredients: {
name: "",
operation: "",
value: ""
}
};
My handler is:
handleFilterChange = (event, searchCriteria, searchCriteriaKey) => {
let newValue = event.target.value;
this.setState(prevState => ({
[searchCriteria]: {
...prevState.ingredients,
[searchCriteriaKey]: newValue
}
}));
};
My Select box component is :
<Select
value={ingredients.name}
options={[
{ key: "", value: "Please choose an ingredient" },
{ key: "ingredient1", value: "INGREDIENT 1" },
{ key: "ingredient2", value: "INGREDIENT 2" },
{ key: "ingredient3", value: "INGREDIENT 3" },
{ key: "ingredient4", value: "INGREDIENT 4" }
]}
changeHandler={event =>
this.handleFilterChange(event, "ingredients", "name")
}
/>
Hope I could explain myself. Thank you!
You should reuse your variable searchCriteria to extract the previous values from the state.
handleFilterChange = (event, searchCriteria, searchCriteriaKey) => {
let newValue = event.target.value;
this.setState(prevState => ({
[searchCriteria]: {
...prevState[searchCriteria],
[searchCriteriaKey]: newValue
}
}));
};

Categories

Resources