I populating the Vuetify data table with some user data. When I remove a user from the data table I update the user array like this:
handleDelete(user) {
confirm("Are you sure you want to delete this user?") &&
axios
.delete("user/" + user.id)
.then(response => {
// Delete user from user array
this.users = this.users.filter(function(el) {
return el.id != user.id;
});
})
},
When I register a new user the array is also updated but now like this:
handleRegister(user) {
axios
.post("user/register", user)
.then(response => {
// Update User array
this.users.push(response.data.user);
})
},
This all works fine, except when I update a user. In my function I search for the user object in the array of users and replace it for the updated one. But somehow the data table don't get updated with the new values. The update function looks like this:
handleUpdate(user) {
const id = user.id;
axios
.put("user/" + id, user)
.then(response => {
// TODO: Update User array
let foundIndex = this.users.findIndex(
x => x.id == response.data.user.id
);
this.users[foundIndex] = response.data.user;
})
},
When I console.log the values of this.users[foundIndex] and response.data.user it shows me the right values. But somehow it seems like the data table doesn't get updated.
i think its problem with scope of "this" variable.
// it should be like this below
handleDelete(user) {
const _this_0 = this;
confirm("Are you sure you want to delete this user?") &&
axios
.delete("user/" + user.id)
.then(response => {
// Delete user from user array
_this_0.users = _this_0.users.filter(function(el) {
return el.id != user.id;
});
})
}
handleRegister(user) {
const _this_0 = this;
axios
.post("user/register", user)
.then(response => {
// Update User array
_this_0.users.push(response.data.user);
})
},
handleUpdate(user) {
const id = user.id;
const _this_0 = this;
axios
.put("user/" + id, user)
.then(response => {
// TODO: Update User array
let foundIndex = _this_0.users.findIndex(
x => x.id == response.data.user.id
);
_this_0.users[foundIndex] = response.data.user;
})
}
I currently fixed it by writing this function:
updateUserArray(user) {
// Look for the index of the user
let index = this.users.findIndex(
x => x.id == user.id
);
// Remove the user from the array
this.users= this.users.filter(function(el) {
return el.id != user.id
});
// Add the updated value to the array
this.users.splice(index, 0, user);
},
Seems like I had to use the splice function because this forces the DOM to refresh while directly updating the array doesn't.
Related
Im new to firebase and I need help understanding how to work with documents and collections. Specifically I want to be able to write to a document and let it hold an array of 'loan objects' and let it be specific per user. All of these documents will be held in a collection. This code is making multiple documents for one user and I want it to only make one document per user and if I want to add more data for that user I just want to add it to the existing document
const loansRef = firebase.firestore().collection('goals')
useEffect(() => {
getPW()
let isMounted = true;
if (isMounted) {
loansRef.where('authorID', '==', userId).orderBy('createdAt', 'desc').onSnapshot(
(querySnapshot) => {
const newGoals = [];
querySnapshot.forEach((doc) => {
const goal = doc.data();
goal.id = doc.id + goalCounter.toString();
newGoals.push(goal);
});
console.log('new Goals: '+ newGoals)
console.log('old goals: '+ oldGoals)
// this is my attempt to try to make all loans appear in one array
var oldGoals = courseGoals
for(let j =0; j < newGoals.length; j++){
oldGoals.push(newGoals[j])
}
setCourseGoals(oldGoals);
setGoalCounter(goalCounter+1)
},
(error) => {
console.log(error);
}
);
}
return () => {
isMounted = false;
};
}, []);
I have a collection of items which all have serial numbers and other fields attached to them. A document looks like this
{
_id: ObjectId(),
serialNum: "123456789",
...otherfields
}
I want to insert a new document but only if none of the existing documents match the serialNum field.
I currently use the approach below, but it requires me grabbing the entire collection, looping through it, and then performing the insert. Is there any alternative method that I could be using as this is quite slow on my large collection
Current code:
const insertItems = (newItem) => {
const itemsCollection = mongodb.db("database").collection("customers");
itemExists = false;
itemsCollection.find({}).toArray()
.then((items) => {
for(let i = 0; i < items.length; i++){
if(items[i].serialNum == newItem.serialNum){
itemExists = true
}
}
})
.then(() => {
if(itemExists){
//error here
} else {
//insert new item
}
})
}
instead of looping all collection why not fetch only one which has that serial no as:
const insertItems = (newItem) => {
const itemsCollection = mongodb.db("database").collection("customers");
itemExists = false;
itemsCollection.find({serialNum:newItem.serialNum}).toArray()
.then((items) => {
if(items.length){
itemExists=true
}
})
.then(() => {
if(itemExists){
//error here
} else {
//insert new item
}
})
}
Try this code
const insertItems = (newItem) => {
const itemsCollection = mongodb.db("database").collection("customers");
itemsCollection.update({serialNum:newItem.serialNum},{
// New fields which you want to insert or update
},{upsert: true})
.then((items) => {
console.log(item);
}).catch((err)=>{
// error here
})
}
I'm making chat inside my website. To store data I use Chat, User, Messages collections.
I want results to be in Array containing:
[{
username (another one, not me)
last update
last message
}]
In Chat model I have only chatid and array of two members, so I need to loop through User collection to get user name using user id from it. I want to save in array all names (in future I would also like to loop through messages to get latest messages for each chatid). Issue is that when I return chatsList it is empty. I think I need somehow to use Promise, but I'm not completely sure how it should work.
Chat.find({ members: userId })
.then(chats => {
let chatsList = [];
chats.forEach((chat, i) => {
let guestId = chat.members[1 - chat.members.indexOf(userId)];
User.findOne({ _id: guestId })
.then(guest => {
let chatObj = {};
name = guest.name;
chatsList.push(name);
console.log("chatsList", chatsList)
})
.catch(err => console.log("guest err =>", err))
})
return res.json(chatsList)
})
.catch(err => {
errors.books = "There are no chats for this user";
res.status(400).json(errors);
})
Indeed, Promise.all is what you are looking for:
Chat.find({ members: userId })
.then(chats => {
let userPromises = [];
chats.forEach((chat, i) => {
let guestId = chat.members[1 - chat.members.indexOf(userId)];
userPromises.push(User.findOne({ _id: guestId }));
});
return Promise.all(userPromises).then(guests => {
let chatsList = [];
guests.forEach(guest => {
chatsList.push(guest.name);
});
return res.json(chatsList);
});
});
});
although it would probably be better to do a single call to DB with a list of ids ($in query). Something like this:
Chat.find({ members: userId })
.then(chats => {
let ids = [];
chats.forEach((chat, i) => {
let guestId = chat.members[1 - chat.members.indexOf(userId)];
ids.push(guestId);
});
return User.find({_id: {$in: ids}}).then(guests => {
let chatsList = [];
guests.forEach(guest => {
chatsList.push(guest.name);
});
return res.json(chatsList);
});
});
});
You may want to additionally validate if every id had a corresponding guest.
You are running into concurrency issues. For example, running chats.forEach, and inside forEach running User.findOne().then: The return statement is already executed before the User.findOne() promise has resolved. That's why your list is empty.
You could get more readable and working code by using async/await:
async function getChatList() {
const chats = await Chat.find({members: userId});
const chatsList = [];
for (const chat of chats) {
let guestId = chat.members[1 - chat.members.indexOf(userId)];
const guest = await User.findOne({_id: guestId});
chatsList.push(guest.name);
}
return chatsList;
}
Then the code to actually send the chat list back to the user:
try {
return res.json(await getChatList());
} catch (err) {
// handle errors;
}
You can try this:
Chat.find({ members: userId }).then(chats => {
let guestHashMap = {};
chats.forEach(chat => {
let guestId = chat.members.filter(id => id != userId)[0];
// depending on if your ID is of type ObjectId('asdada')
// change it to guestHashMap[guestId.toString()] = true;
guestHashMap[guestId] = true;
})
return Promise.all(
// it is going to return unique guests
Object.keys(guestHashMap)
.map(guestId => {
// depending on if your ID is of type ObjectId('asdada')
// change it to User.findOne({ _id: guestHashMap[guestId] })
return User.findOne({ _id: guestId })
}))
})
.then(chats => {
console.log(chats.map(chat => chat.name))
res.json(chats.map(chat => chat.name))
})
.catch(err => {
errors.books = "There are no chats for this user";
res.status(400).json(errors);
})
I'm trying to remove a specific item from an objects array based on the title attribute in the array. I keep running into a problem where I can view the array item, but I'm not able to splice the item out of the array based on the parameters entered in my remove function. I'm just getting the error message back from my else statement in the function.
I've tried using find, forEach, findIndex and match that case in order to test out removing the result based on the index, or the text value of the key 'text'. I commented out all of the functions I tried prior to searching for the answer in the forum recommendations. All of my recipe functions are working, along with my createIngredient function, which adds an object to the recipe array. But the removeIngredient function I've been trying to get to work, isn't because of the problems mentioned above.
let recipes = []
// Read existing recipes from localStorage
const loadRecipes = () => {
const recipesJSON = localStorage.getItem('recipes')
try {
return recipesJSON ? JSON.parse(recipesJSON) : []
} catch (e) {
return []
}
}
// Expose recipes from module
const getRecipes = () => recipes
const createRecipe = () => {
const id = uuidv4()
const timestamp = moment().valueOf()
recipes.push({
id: id,
title: '',
body: '',
createdAt: timestamp,
updatedAt: timestamp,
ingredient: []
})
saveRecipes()
return id
}
// Save the recipes to localStorage
const saveRecipes = () => {
localStorage.setItem('recipes', JSON.stringify(recipes))
}
// Remove a recipe from the list
const removeRecipe = (id) => {
const recipeIndex = recipes.findIndex((recipe) => recipe.id === id)
if (recipeIndex > -1) {
recipes.splice(recipeIndex, 1)
saveRecipes()
}
}
// Remove all recipes from the recipe array
const cleanSlate = () => {
recipes = []
saveRecipes()
}
const updateRecipe = (id, updates) => {
const recipe = recipes.find((recipe) => recipe.id === id)
if (!recipe) {
return
}
if (typeof updates.title === 'string') {
recipe.title = updates.title
recipe.updatedAt = moment().valueOf()
}
if (typeof updates.body === 'string') {
recipe.body = updates.body
recipe.updateAt = moment().valueOf()
}
saveRecipes()
return recipe
}
const createIngredient = (id, text) => {
const recipe = recipes.find((recipe) => recipe.id === id)
const newItem = {
text,
have: false
}
recipe.ingredient.push(newItem)
saveRecipes()
}
const removeIngredient = (id) => {
const ingredient = recipes.find((recipe) => recipe.id === id)
console.log(ingredient)
const allIngredients = ingredient.todo.forEach((ingredient) => console.log(ingredient.text))
// const recipeIndex = recipes.find((recipe) => recipe.id === id)
// for (let text of recipeIndex) {
// console.log(recipdeIndex[text])
// }
// Attempt 3
// if (indexOfIngredient === 0) {
// ingredientIndex.splice(index, 1)
// saveRecipes()
// } else {
// console.log('error')
// }
// Attempt 2
// const recipe = recipes.find((recipe) => recipe.id === id)
// const ingredients = recipe.todo
// // let newItem = ingredients.forEach((item) => item)
// if (ingredients.text === 'breadcrumbs') {
// ingredients.splice(ingredients, 1)
// saveRecipes()
// }
// Attempt 1
// const ingredientName = ingredients.forEach((ingredient, index, array) => console.log(ingredient, index, array))
// console.log(ingredientName)
// const recipeIndex = recipes.findIndex((recipe) => recipe.id === id)
// if (recipeIndex > -1) {
// recipes.splice(recipeIndex, 1)
// saveRecipes()
// }
}
recipes = loadRecipes()
OUTPUT
{id: "ef88e013-9510-4b0e-927f-b9a8fc623450", title: "Spaghetti", body: "", createdAt: 1546878594784, updatedAt: 1546878608896, …}
recipes.js:94 breadcrumbs
recipes.js:94 noodles
recipes.js:94 marinara
recipes.js:94 meat
recipes.js:94 ground beef
recipes.js:94 milk
So I'm able to view the output I printed above and see each item in the ingredients array, but trying to splice the item based on the index number or key is not working for me with the functions I have already tried and the info I have found on Stackoverflow about objects, arrays and the splice method so far.
If I am understanding correctly (after reading the commented out attempts in your code), you are trying to remove the "breadcrumbs" ingredient from the recipe that corresponds to the id passed to the removeIngredient() function.
In that case, perhaps you could take a slightly different approach to removing the ingredient from the recipes todo array, via the Array#filter method?
You could use filter() in the following way to "filter out" (ie remove) the "breadcrumbs" ingredient from the todo array via the following filter logic:
// Keep any ingredients that do not match ingredient (ie if ingredient
// equals "breadcrumbs")
todo.filter(todoIngredient => todoIngredient !== ingredient)
You might consider revising your removeIngredient() function by;
adding an additional ingredient parameter to the function arguments. This allows you to specify the ingredient to be removed from the recipe corresponding to recipeId
and, introducing the filter() idea as described:
const removeIngredient = (recipeId, ingredient) => {
const recipe = recipes.find(recipe => recipe.id === recipeId)
if(recipe) {
// Filter recipe.todo by ingredients that do not match
// ingredient argument, and reassign the filtered array
// back to the recipie object we're working with
recipe.todo = recipe.todo.filter(todoIngredient =>
(todoIngredient !== ingredient));
}
}
Now, when you introduce the "remove" button for each ingredient, you would call the removeIngredient() as follows:
var recipeId = /* got id from somewhere */
var ingredientText = /* got ingredient from somewhere */
removeIngredient( recipeId, ingredientText );
Hope this helps!
infiniteHandler($state) {
var next = db
.collection("posts")
.orderBy("timestamp", "desc")
.startAfter(this.lastVisible)
.limit(3)
next.get().then(documentSnapshots => {
//Get the last visible document
// this.lastVisible =
// documentSnapshots.docs[documentSnapshots.docs.length - 1]
if (documentSnapshots.docs.length == 0) $state.complete()
else {
this.$store.commit(
"modules/posts/updateLastVisible",
documentSnapshots.docs[documentSnapshots.docs.length - 1].data()
.timestamp
)
}
documentSnapshots.forEach(doc => {
var post = doc.data()
post.docID = doc.id
this.$store.commit("modules/posts/pushPost", post)
})
$state.loaded()
})
}
This is my infinite loading handler which fetches new DB Entries once the end of the list is reached. Working fine so far.
This is my first fetch when the page gets loaded
async fetch({ store }){
if (store.state.modules.posts.posts.length < 5) {
let posts = []
await db
.collection("posts")
.orderBy("timestamp", "desc")
.limit(3)
.get()
.then(querySnapshot => {
store.commit(
"modules/posts/updateLastVisible",
querySnapshot.docs[2].data().timestamp
)
querySnapshot.forEach(doc => {
var x = doc.data()
x.docID = doc.id
posts.push(x)
})
})
store.commit("modules/posts/fetchedPosts", posts)
}
}
Basicly the problem is that I get the first 3 entries which I fetch on the page load again when I am fetching in my infinite Loading handler, which leads to the entries being displayed twice, this should not happen because this.lastVisible has the timestamp of the 3rd Element that I fetch on load, so those should be ignored.
After those elements everything is working fine with the .startAfter but the first 3 getting loaded again makes no sense.
I checked the store with the devtools and everything is working fine, this.lastVisible has the correct value when the infiniteLoading Handler is called the first time.
Bounty Edit:
Okay so I still have the problem I tried to play around with it a bit more to find the issue but its still occuring... I will set a bounty now and I hope anyone is able to help.
You do not actually need the first time fetch. The infiniteHandler will be called on its own when it gets mounted. In case if it does not call then you can try using the function
this.$refs.infiniteLoading.attemptLoad(); // 'infiniteLoading' is the component's ref property
That will actually invoke the infiniteHandler function for you.
EDIT: To check if one of the function is currently running. On the handler part
infiniteHandler($state) {
//Check if its currently loading
this.$nextTick(()=>{
if (this.isDocSnapShotLoading){
return;
}
});
//set as currently loading
this.isDocSnapShotLoading = true;
var next = db
.collection("posts")
.orderBy("timestamp", "desc")
.startAfter(this.lastVisible)
.limit(3)
next.get().then(documentSnapshots => {
//Get the last visible document
// this.lastVisible =
// documentSnapshots.docs[documentSnapshots.docs.length - 1]
if (documentSnapshots.docs.length == 0) $state.complete()
else {
this.$store.commit(
"modules/posts/updateLastVisible",
documentSnapshots.docs[documentSnapshots.docs.length - 1].data()
.timestamp
)
}
documentSnapshots.forEach(doc => {
var post = doc.data()
post.docID = doc.id
this.$store.commit("modules/posts/pushPost", post)
})
$state.loaded()
//set completed loading
this.isDocSnapShotLoading = false;
})
}
On the fetch part
async fetch({ store }){
if (store.state.modules.posts.posts.length < 5) {
//check if currently loading
this.$nextTick(()=>{
if (this.isDocSnapShotLoading){
return;
}
});
//set as currently loading
this.isDocSnapShotLoading = true;
let posts = []
await db
.collection("posts")
.orderBy("timestamp", "desc")
.limit(3)
.get()
.then(querySnapshot => {
store.commit(
"modules/posts/updateLastVisible",
querySnapshot.docs[2].data().timestamp
)
querySnapshot.forEach(doc => {
var x = doc.data()
x.docID = doc.id
posts.push(x)
})
//set as completed loading.
this.isDocSnapShotLoading = false;
})
store.commit("modules/posts/fetchedPosts", posts)
}
}
If you wanto be ignore first 3 posts in infiniteHandler then, you can make one post array where you store post id and check whether post id is already loaded or not. I know this should be solved using query but as temporary solution I hope it will work for you.
infiniteHandler($state) {
var next = db
.collection("posts")
.orderBy("timestamp", "desc")
.startAfter(this.lastVisible)
.limit(3)
next.get().then(documentSnapshots => {
//Get the last visible document
// this.lastVisible =
// documentSnapshots.docs[documentSnapshots.docs.length - 1]
if (documentSnapshots.docs.length == 0) $state.complete()
else {
this.$store.commit(
"modules/posts/updateLastVisible",
documentSnapshots.docs[documentSnapshots.docs.length - 1].data()
.timestamp
)
}
documentSnapshots.forEach(doc => {
var check = this.postIdArray.indexOf(doc.id);
if(check == -1){
var post = doc.data()
post.docID = doc.id
this.$store.commit("modules/posts/pushPost", post);
this.postIdArray[] = doc.id;
}
})
$state.loaded()
})
}
async fetch({ store }){
this.postIdArray = [];
if (store.state.modules.posts.posts.length < 5) {
let posts = []
await db
.collection("posts")
.orderBy("timestamp", "desc")
.limit(3)
.get()
.then(querySnapshot => {
store.commit(
"modules/posts/updateLastVisible",
querySnapshot.docs[2].data().timestamp
)
querySnapshot.forEach(doc => {
var x = doc.data()
x.docID = doc.id
this.postIdArray[] = doc.id;
posts.push(x)
})
})
store.commit("modules/posts/fetchedPosts", posts)
}
}
Ok so I found a temporary solution which works for now but is still not pretty:
documentSnapshots.forEach(doc => {
if (
doc.id !== this.posts[0].docID &&
doc.id !== this.posts[1].docID &&
doc.id !== this.posts[2].docID
) {
var post = doc.data()
post.docID = doc.id
this.$store.commit("modules/posts/pushPost", post)
}
})
I also try to make this more efficient with different solutions, thanks so far for your help.