I am trying to push a newly created object to an array. The array is defined as
clientengagements: []
The Object is
engagement: []
So I am using a v-for to iterate through each engagement belonging to a client in my clientengagements array. Everything works fine until I submit a new engagement. It changes my clientengagements: [] array to only show the new object. Now if i refresh the page, the clientengagements: [] will go back to the array with the newly added object plus the other objects that already existed which is what I want it to do..
This is the AddEngagement component script that I use to dispatch to the store
methods: {
...mapActions(['addEngagement']),
addNewEngagement() {
if(!this.engagement.return_type || !this.engagement.year ) return;
this.addEngagement({
id: this.idForEngagement,
client_id: this.client.id,
return_type: this.engagement.return_type,
year: this.engagement.year,
assigned_to: this.engagement.assigned_to,
status: this.engagement.status,
})
.then(() => {
this.engagement = ""
this.idForEngagement++
this.$router.go(-1);
})
},
},
The action in the store is defined like below
addEngagement(context, engagement) {
axios.post(('/engagements'), {
client_id: engagement.client_id,
return_type: engagement.return_type,
year: engagement.year,
assigned_to: engagement.assigned_to,
status: engagement.status,
done: false
})
.then(response => {
context.commit('getClientEngagements', response.data)
})
.catch(error => {
console.log(error)
})
},
from there it should commit to the getClientEngagements() mutation which is where I believe I am running into my issue but I have not figured out how to resolve. Here is the code
getClientEngagements(state, clientengagements) {
state.clientengagements = clientengagements;
},
I have been recommended to use Vue.set() but I do not know how to apply it.. any help would be greatly appreciated!!!
So I have set the mutation getClientEngagements to use this now
getClientEngagements(state, engagement) {
state.clientengagements.push(engagement)
},
But by changing the mutation to this it has placed the already existing objects into a deeper nested array. see below
So the response.data for the new engagement only sends back from the backend that newly added engagement on the addEngagement action. is this a problem?
Thanks for all the help, but I actually ended up needing to add a extra mutation and use that as the commit, because I am defining to seperate lists of engagements. One for the client and one for all clients. here is the new mutation
addClientEngagement(state, engagement) {
state.clientengagements.push(engagement)
},
which I then use in my action here
addEngagement(context, engagement) {
axios.post(('/engagements'), {
client_id: engagement.client_id,
return_type: engagement.return_type,
year: engagement.year,
assigned_to: engagement.assigned_to,
status: engagement.status,
done: false
})
.then(response => {
context.commit('addClientEngagement', response.data)
})
.catch(error => {
console.log(error)
})
},
and then this mutation happens
getClientEngagements(state, clientengagements) {
state.clientengagements = clientengagements
},
Previously it was using this mutation to add the engagement to the array which was why it was replacing the array with the new object. see below for old mutation
addEngagement(state, engagement) {
state.engagements.push ({
id: engagement.id,
client_id: engagement.client_id,
return_type: engagement.return_type,
year: engagement.year,
assigned_to: engagement.assigned_to,
status: engagement.status,
done: false
})
},
Hopefully I am making sense in my explanation
.then(response => {
...// data transaction
context.commit('getClientEngagements', response.data)
})
Maybe you can do with the response data at first and then store it after that.
If the response data is an object, and you only want to store the value,
you can do something similar to this:
for( p in object ){
context.commit('getClientEngagements', object[p])
}
If the response data is an array, then you can try to use for to get the value you want and then push in the state.
You should do the transaction of data according to your needs at first for your case.
Related
How do you make a relationship in firebase and use the fields? I'm picking up here. I'm working with firestore here and I made relationships like this:
Where I have the collection of answers and it has serviceId collaboratorId and tokenId each of these there is a foreign key of the other collections that you can see in the image. I don't know if this is right, but assuming it's right anyway, how do you use this in React? When it was all string I could add it, now that it's changed to a reference it doesn't work anymore :(
To add I was doing this:
async function handleSubmitResult(e) {
e.preventDefault();
await createFormSchema
.validate({
name,
service,
collaborator,
})
.then(() => {
addDoc(answersCollectionRef, {
name,
service,
collaborator,
answer1,
answer2,
answer3,
answer4,
comment,
createdAt: new Date().toString(),
});
console.log("SALVO NO FIREBASE! 🔥");
// A temporary fix to clear fields of the form
document.getElementById("form-rating").reset();
setName("");
setService("");
setCollaborator("");
setAnswer1(0);
setAnswer2(0);
setAnswer3(0);
setAnswer4(0);
setComment("");
navigate("/thanks");
})
.catch((error) => {
toast.error(error.message, {
theme: "colored",
});
});
}
This works perfectly when it was a string, but now that the fields have changed to the references of the collections I mentioned, I can no longer save it the way I want it in the firestore.
---- After edit my question ------
And when I edit my save method to:
async function handleSubmitResult(e) {
e.preventDefault();
await createFormSchema
.validate({
name,
service,
collaborator,
})
.then(() => {
addDoc(answersCollectionRef, {
name,
serviceId: service,
collaboratorId: collaborator,
answer1,
answer2,
answer3,
answer4,
comment,
createdAt: new Date().toString(),
tokenId: token,
});
console.log("SALVO NO FIREBASE! 🔥");
// A temporary fix to clear fields of the form
document.getElementById("form-rating").reset();
setName("");
setService("");
setCollaborator("");
setAnswer1(0);
setAnswer2(0);
setAnswer3(0);
setAnswer4(0);
setComment("");
navigate("/thanks");
})
.catch((error) => {
toast.error(error.message, {
theme: "colored",
});
});
}
It doesn't show any errors, it now is saving, but it saves as a string. Wouldn't it be correct to save the ID, given that I'm doing a one-to-many relationship? That's what I can't understand...
My firebase looks like this after saving:
------ Add ID of the document Service
Here is my Service, how get id document?
If you want to store a reference to another document in the database, make sure that the value you pass in your code is a DocumentReference object to that document.
E.g. say that you want the name field to be a DocumentReference to the Users collection, that'd be something like:
addDoc(answersCollectionRef, {
name: new DocumentReference(db, "Users", "idOfUserDocument"),
...
});
Im using next.js and Stripe webhooks to insert checkout sessions to Supabase that will create a customer's order history. I'm able to get the information about the whole order written to a table called 'orders', but am wondering what the best way to add individual items within each checkout session to another table called 'order_items' is. This way I can map through main orders and then the children items. Appreciate any help provided. Here is what I have for getting orders associated with a customer:
const upsertOrderRecord = async (session: Stripe.Checkout.Session, customerId: string) => {
const { data: customerData, error: noCustomerError } = await supabaseAdmin
.from<Customer>('customers')
.select('id')
.eq('stripe_customer_id', customerId)
.single();
if (noCustomerError) throw noCustomerError;
const { id: uuid } = customerData || {};
const sessionData: Session = {
id: session.id,
amount_total: session.amount_total ?? undefined,
user_id: uuid ?? undefined
};
const { error } = await supabaseAdmin.from<Session>('orders').insert([sessionData], { upsert: true });
if (error) throw error;
console.log(`Product inserted/updated: ${session.id}`);
};
The Checkout Session object contains a line_items field which is a list of each item included in the purchase.
However this field is not included in the object by default, and therefore won't be a part of your webhook payload. Instead you'll need to make an API call in your webhook handle to retrieve the Checkout Session object, passing the expand parameter to include the line_items field:
const session = await stripe.checkout.sessions.retrieve('cs_test_xxx', {
expand: ['line_items']
});
I am trying to update setState in a for loop, but for some reason state isn't being copied it's just being replaced. There should be 2 clients, instead I am getting one. Can anyone tell me why this is happening? The console.log is returning both clients.
const handleViewClients = () => {
for (let i = 0; i < clients.length; i++) {
console.log(clients[i].clientid);
fetch("http://localhost:3005/all-clients/" + clients[i].clientid)
.then((response) => response.json())
.then((result) => {
console.log(result);
setBarbersClient({
...barbersClient,
client: result,
});
});
}
};
I have also tried this... The console.log is returning what I need
Promise.all(
clients.map((client) =>
fetch("http://localhost:3005/all-clients/" + client.clientid)
)
)
.then((resp) => resp.json())
.then((result) => {
console.log(result.username)
setBarbersClient({
...barbersClient,
client: result,
});
});
Here is the route from the server side
app.get("/all-clients/:clientid", (req, res) => {
db.NewClientsx.findOne({
where: {
id: req.params.clientid,
},
}).then((response) => {
res.json(response);
});
});
There some fundamental concepts of sync vs. async code that you aren't accounting for here. State changing (and fetching) is asynchronous, so it won't run until after this synchronous loop has finished being executed (during which the state value will remain unchanged). Also, it's a bad idea to change state in a loop, for this reason and others.
Fetch all the clients, then do one state change at the end with all the fetched data. You can utilise things like Promise.all and Promise.spread to achieve this. Here's an example of doing multiple fetches then dealing with the results in one batch: How can I fetch an array of URLs with Promise.all?
You're making two distinct mistakes of which either is enough to cause the behaviour you're seeing.
1. You're overwriting the client property.
Every time you call the setter function you're overwriting the previous value of the client property. You'll need some data structure that supports multiple values like a map:
setBarbersClient({
...barbersClient,
clients: {
...barbersClient.clients,
[result.id]: result
},
});
You will need to change your render logic somewhat to accomodate the new data structure.
2. You're using a stale reference.
When you access barbersClient its setter may have already been called with a different value and your reference to it still refers to the value of the previous run of the render function. You can make sure your reference is fresh by using a set state action callback.
setBarbersClient(previousValue => {
...previousValue,
clients: {
...previousValue.clients,
[result.id]: result
},
});
previousValue will never be stale inside the set state action function body.
I'm calling a get service in componentWillMount(). Service response me a object in the form of JSON I which mentioned below. But I need the values of service objects into my variables/objects so I made a loop when I'm getting the data from service and it works fine but the issue is that it takes too much time to pass the data into my objects/variables. It take at least 45 to 50 seconds to pass the service data into my objects. If I removed the loop so service respond me perfectly but I need the values of service into my objects. Please provide me the best solution for it. Thanks
Service JSON Response
{
"Capital": 0,
"Code": "BOL",
"Code2": "",
"Continent": "",
"GNP": 0,
"Name": "Bolivia",
}
I need the Code into key variable and Name into label variable
axios.get('http://apiurl.com/api/user/GetCountries')
.then((response) => {
for (var i = 0; i < response.data.length; i++) {
var joined = this.state.countriesModel.concat({ key: response.data[i].Code, label: response.data[i].Name });
this.setState({ countriesModel: joined });
}
this.hideLoader();
}).catch((err) => {
console.log(err)
});
I made array outside state and it works perfectly. setState is taking too much time to set the data.
countiesOptions = [];
componentDidMount(){
var joined = this.countiesOptions.concat({ key: response.data[i].Code, label: response.data[i].Name });
this.countiesOptions = joined;
}
EDIT: Since I wasn't able to find a correct solution, I changed the
application's structure a bit and posted another question:
Mongoose - find documents not in a list
I have a MEAN app with three models: User, Task, and for keeping track of which task is assigned to which user I have UserTask, which looks like this:
const mongoose = require("mongoose");
const autopopulate = require("mongoose-autopopulate");
const UserTaskSchema = mongoose.Schema({
completed: { type: Boolean, default: false },
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
autopopulate: true
},
taskId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Task",
autopopulate: true
}
});
UserTaskSchema.plugin(autopopulate);
module.exports = mongoose.model("UserTask", UserTaskSchema);
In my frontend app I have AngularJS services and I already have functions for getting all users, all tasks, and tasks which are assigned to a particular user (by getting all UserTasks with given userId. For example:
// user-task.service.js
function getAllUserTasksForUser(userId) {
return $http
.get("http://localhost:3333/userTasks/byUserId/" + userId)
.then(function(response) {
return response.data;
});
}
// task-service.js
function getAllTasks() {
return $http.get("http://localhost:3333/tasks").then(function(response) {
return response.data;
});
}
Then I'm using this data in my controllers like this:
userTaskService
.getAllUserTasksForUser($routeParams.id)
.then(data => (vm.userTasks = data));
...and because of autopopulate plugin I have complete User and Task objects inside the UserTasks that I get. So far, so good.
Now I need to get all Tasks which are not assigned to a particular User. I guess I should first get all Tasks, then all UserTasks for a given userId, and then make some kind of difference, with some "where-not-in" kind of filter.
I'm still a newbie for all the MEAN components, I'm not familiar with all those then()s and promises and stuff... and I'm really not sure how to do this. I tried using multiple then()s but with no success. Can anyone give me a hint?
You can do at server/API side that will more efficient.
In client side, if you want to do then try below
var userid = $routeParams.id;
userTaskService
.getAllTasks()
.then((data) => {
vm.userTasks = data.filter(task => task.userId !== userid)
});