v-for duplicate keys issue
This returns a list just fine, but when I modify a user inside the array, like user.role = 'something', it says that I have duplicate keys. The key is set with the user.id which is their firebase key, so duplication shouldn't be possible? The result is that the modified user takes over some other user in the list, so it shows up two times (I haven't tried with more than two users), but if I go back and then re-visit this list, it shows up as expected with the updated data.
V-for loop:
<ManageUsersListItem
v-for="user in sortedUsers"
:key="user.id"
:user="user"
#removeManager="removeManager(user)"
#makeManager="makeManager(user)"
#removeUser="removeUser(user)"
/>
Data:
data() {
return {
users: [],
}
},
firestore() {
return {
users: db.collection('brands').doc(this.brand.id).collection("users")
}
},
Sorting the array:
computed: {
sortedUsers() {
return this.users.sort(function(a,b) {
var c = new Date(a.userAddedOn)
var d = new Date(b.userAddedOn)
return c-d
})
}
},
The method I use to change a users role:
methods: {
makeManager(user) {
this.$firestore.users.doc(user.id).update({
role: 'admin'
})
},
},
to avoid duplicate key use index instead of user.id
<ManageUsersListItem
v-for="(user, index) in sortedUsers"
:key="index"
:user="user"
#removeManager="removeManager(user)"
#makeManager="makeManager(user)"
#removeUser="removeUser(user)"
/>
Related
I'm new in angular and I need some help.
I have an observable getting users of type User[]
User: [
id: string,
name: string
]
and I have another array Ids of type string getting the ids of the selected users from a mat-select
Ids = this.Form.controls['users'].value
what I need right now is to subscribe to users$ observable, and get only the users that they have an id in Ids
const selectedUsers = ids.forEach(id =>this.usersSub$.value.filter((user) => user.userId === id))
something like the above but it is not really the right thing to do because it returns undefined . I'm wondering how should I properly get my selectedUsers array.
You use combineLatest to merge both observables and map all elements to accomplish it.
First, Create an observable with ids.
selectedIds$ = of([1, 3]);
players$ = of([
{ id: 1, name: 'lebron' },
{ id: 2, name: 'irving' },
{ id: 3, name: 'love' },
]);
Next, combine both observables, using the combineLatest operator, and return the players using the map to iterate over the response from the combineLast, use the filter and find to match the playerid with the ids from the selectedIds array.
const seletedUsers$ = combineLatest([this.selectedIds$,
this.players$])
.pipe(
map(([ids, players]) => {
return players.filter((p) => ids.find((id) => id === p.id));
})
)
.subscribe((v) => {
console.log(v);
});
https://rxjs.dev/api/index/function/combineLatest
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
In my state I have an object called foodLog which holds all entries a user enters with one of the keys being foodSelectedKey and I'm trying to return all entries that have a matching value from that key with a different array called foodFilter.
However, this doesn't work and errors out saying foodLog.filter() isn't a function - I've looked this up and it's because it's an Object (I think). Any help would be greatly appreciated!
state = {
// log food is for the logged entries
foodLog: {},
// used for when filtering food entries
foodFilter: [],
};
findMatches = () => {
let foodLog = this.state.foodLog;
let foodFilter = this.state.foodFilter;
let matched = foodLog.filter((item) => {
return foodLog.foodsSelectedKey.map((food) => {
return foodFilter.includes(food);
});
});
};
I guess the reason behind the error Is not a function is that the object can not be looped. By that it means you can not iterate an object with differend variables inside, if it has no index to be iterated like an array. The same goes for map(), find() and similar functions which MUST be run with arrays - not objects.
As far as I understand you have an object named foodLog which has an array named foodsSelectedKey. We need to find intersected elements out of foodFilter with the array. This is what I came up with:
state = {
// log food is for the logged entries
foodLog: {
foodsSelectedKey: [
{ id: 1, name: "chicken" },
{ id: 2, name: "mashroom" }
]
},
// used for when filtering food entries
foodFilter: [
{ id: 1, name: "chicken" },
{ id: 2, name: "orange" }
]
};
findMatches = () => {
let foodLog = this.state.foodLog;
let foodFilter = this.state.foodFilter;
let matched = foodLog.foodsSelectedKey.filter((key) =>
{
for (let i=0; i<foodFilter.length;i++){
if(foodFilter[i].name===key.name)
return true
}
return false;
}
);
return matched;
};
The Output is filtered array, in this case, of one element only:
[{
id: 1
name: "chicken"
}]
In order to check the output - run console.log(findMatches()). Here is the CodeSandbox of the solution. (check console at right bottom)
I'm trying to use lodash's find method to determine an index based on one attribute. In my case this is pet name. After that I need to change the adopted value to true using setState. The problem is however; I do not understand how to combine setState and _.find()
As of right now I have this written. My main issue is figuring out how to finish this.
adopt(petName) {
this.setState(() => {
let pet = _.find(this.state.pets, ['name', petName]);
return {
adopted: true
};
});
}
This does nothing at the moment as it is wrong, but I don't know how to go from there!
In React you usually don't want to mutate the state. To do so, you need to recreate the pets array, and the adopted item.
You can use _.findIndex() (or vanilla JS Array.findIndex()) to find the index of the item. Then slice the array before and after it, and use spread to create a new array in the state, with the "updated" item:
adopt(petName) {
this.setState(state => {
const petIndex = _.findIndex(this.state.pets, ['name', petName]); // find the index of the pet in the state
return [
...state.slice(0, petIndex), // add the items before the pet
{ ...state[petIndex], adopted: true }, // add the "updated" pet object
...state.slice(petIndex + 1) // add the items after the pet
];
});
}
You can also use Array.map() (or lodash's _.map()):
adopt(petName) {
this.setState(state => state.map(pet => pet.name === petName ? ({ // if this is the petName, return a new object. If not return the current object
...pet,
adopted: true
}) : pet));
}
Change your adopt function to
adopt = petName => {
let pets = this.state.pets;
for (const pet of pets) {
if (!pet.adopted && pet.name === petName) {
pet.adopted = true;
}
}
this.setState({
pets
});
};
// sample pets array
let pets = [
{
name: "dog",
adopted: false
},
{
name: "cat",
adopted: false
}
]
Env: NodeJS service using aws-sdk for interacting with DynamoDb.
Problem: When I set an attribute of an item to an array, it is saved as a string. I expect x: ['1'] but I get x: '1'. I believe this is because I'm incorrectly writing my UpdateExpression/ExpressionAttributeValues.
Situation: I have a table with a field called users. Users is an array of uuids that can be updated. An example of an item in the table:
{ x_name: 'Hello',
owner: '123456',
x_uuid: '1357911',
users: []
}
I want to update the users array with a user uuid. To my update function I pass through:
{ users: ['13245395'] }
The update function (data is { users: ['13245395'] }):
updateX(data, { x_uuid }) {
if (!x_uuid) {
throw new Error('No x_uuid supplied')
}
// new doc client
const docClient = new AWS.DynamoDB.DocumentClient();
var params = {
TableName: this.table,
Key: {
'x_uuid': x_uuid
},
UpdateExpression: "set users = :users",
ExpressionAttributeValues:{
":users": `${data.users}`
},
ReturnValues:"ALL_NEW"
};
return new Promise((resolve, reject) =>
docClient.update(params, (error, x) => {
return error ? reject(error) : resolve(x)
})
)
}
}
The result I get is
{ x_name: 'Hello',
owner: '123456',
x_uuid: '1357911',
users: '13245395'
}
but what I expected was:
{ x_name: 'Hello',
owner: '123456',
x_uuid: '1357911',
users: ['13245395']
}
Previously tried:
wrapping data.users in an array when creating params (works for the first id but the second id added gets appended to the same string as the first so it looks like ['123,456'] instead ['123', '456'].
UpdateExpression: "set users = :users",
ExpressionAttributeValues:{
":users": [${data.users}]
},
Using the "L" and "S" data types to determine that it's an array of strings, i.e.
UpdateExpression: "set users = :users",
ExpressionAttributeValues:{
":users": { "L": { "S":${data.users} } }
},
You are converting your users array to a string
":users": `${data.users}`
Try
":users": data.users
This will set users to the array in data.users
I'm trying to insert into a user profile array after an autoform inserts into another collection (Meteor.users).
My simple schema array is set up like this -
(within the profile schema)
listings: {
type: [String],
optional: true
},
"listings.$.id": {
type: String,
optional: true
}
And this is my collection-hook method that should be inserting after listing insert.
//Add listing to user collection on submit
Listings.after.insert(function(userId, doc) {
console.log("STUFF");
Meteor.users.update({_id : userId},
{
$push :
{
'profile.listings.$.id' : this._id
}
}
In my eyes, this should work. The form inserts properly without the collection-hook, but now when I submit the form I get this error in my JS console:
Error: After filtering out keys not in the schema, your modifier is now empty(…)
The console.log("stuff") triggers, I see that in the console before the error.
Anybody have any ideas on how to do this?
EDIT - fixed a few things by switching it to :
Listings.after.insert(function(userId, doc) {
console.log("STUFF" + userId + ' ' + this._id);
Meteor.users.update({_id: userId },
{
$set :
{
"profile.listings.$.id" : this._id
}
}
)
});
Now I can't insert into the array because of the $ operator.
Assuming listings is just an array of objects with the id field, you could do:
listings: {
type: [Object],
optional: true
},
"listings.$.id": {
type: String,
optional: true
}
Listings.after.insert(function(userId, doc) {
var id = this._id;
Meteor.users.update({_id: userId }, {
$push : {
"profile.listings" : { id: id }
}
});
});
This changes your listings from an array of Strings to an array of objects - you can't have a property of id on a String. This then allows you to do $push on the profile.listings array with the object in question. If you're literally just storing an ID on listings though, you could simplify this further:
listings: {
type: [String],
optional: true
}
Listings.after.insert(function(userId, doc) {
var id = this._id;
Meteor.users.update({_id: userId }, {
$push : {
"profile.listings" : id
}
});
});
Maybe you're leaving some code out, but with your current schema you don't need anything but an array of strings - no need for an id property.