Why updating object in array is not working in VUE? - vuex

I'm trying to make an update and I managed to do it in the firebase, but in the store is not updating. Here is my code
editCar() {
let result = this.balmUI.validate(this.formData);
let { valid, message } = result;
this.message = message;
console.log(`Vrei sa editezi masina: ${this.formData.vehicle}`);
console.log(utils.url);
if (valid) {
let data = {
vehicle: this.formData.vehicle,
color: this.formData.color,
fuel: this.formData.fuel,
status: this.formData.status,
price: this.formData.price,
};
let requestParameters = { ...utils.globalRequestParameters };
let token = window.localStorage.getItem("token");
requestParameters.headers.Authorization = "Bearer " + token;
requestParameters.method = "PUT";
requestParameters.body = JSON.stringify(data);
fetch(utils.url + "cars/" + this.formData.id, requestParameters)
.then((res) => res.json())
.then((res) => {
console.log(res.message);
if (
res.message === "Decoding error!" ||
res.message === "Your token expired!"
) {
console.log("nu ai voie!");
} else {
data.id = res.id;
this.$store.dispatch("editCar", data);
this.$router.push("/");
}
});
}
This is the index from store, which contais my mutation and action. Anything else is working properly
import { createStore } from 'vuex'
export default createStore({
state: {
cars: [],
isAuthentif: false
},
getters: {
cars: state => {
return state.cars
}
},
mutations: {
SET_AUTH: (state, status) => {
state.isAuthentif = status
},
SET_CARS: (state, cars) => {
state.cars = cars
},
ADD_CAR: (state, car) => {
state.cars.push(car)
},
DELETE_CAR: (state, id) => {
var index = state.cars.findIndex(car => car.id == id)
state.cars.splice(index, 1);
},
EDIT_CAR: (state, car) => {
state.cars.forEach(c => {
if(c.id === car.id) {
c = car;
}
})
}
},
actions: {
login: ({ commit }, payload) => {
commit('SET_AUTH', payload)
},
fetchCars: ({ commit }, payload) => {
commit('SET_CARS', payload)
},
addCar: ({ commit }, payload) => {
commit('ADD_CAR', payload)
},
deleteCar: ({ commit }, payload) => {
commit('DELETE_CAR', payload)
},
editCar: ({ commit }, payload) => {
commit('EDIT_CAR', payload)
}
},
modules: {
}
})
EDIT_CAR is the problem, I think.
What is wrong? Why it is not updating on the screen.
I've also tried to use this https://vuex.vuejs.org/guide/mutations.html#object-style-commit
like this c = {...c, car} but is not working

Your problem is not in the mutation. The problem is in your editCar() in here this.$store.dispatch("editCar", data);
You put data, and with vehicle, color, fuel, status and price and then in your mutation you verify ID. You didn't pass any id. You cand make a new object if you don't want you id, like this:
editCar() {
let result = this.balmUI.validate(this.formData);
let { valid, message } = result;
this.message = message;
console.log(`Vrei sa editezi masina: ${this.formData.vehicle}`);
console.log(utils.url);
if (valid) {
let data = {
vehicle: this.formData.vehicle,
color: this.formData.color,
fuel: this.formData.fuel,
status: this.formData.status,
price: this.formData.price,
};
let requestParameters = { ...utils.globalRequestParameters };
let token = window.localStorage.getItem("token");
requestParameters.headers.Authorization = "Bearer " + token;
requestParameters.method = "PUT";
requestParameters.body = JSON.stringify(data);
fetch(utils.url + "cars/" + this.formData.id, requestParameters)
.then((res) => res.json())
.then((res) => {
console.log(res.message);
if (
res.message === "Decoding error!" ||
res.message === "Your token expired!"
) {
console.log("nu ai voie!");
} else {
let newData = {
id: this.formData.id,
vehicle: data.vehicle,
color: data.color,
fuel: data.fuel,
status: data.status,
price: data.price,
};
this.$store.dispatch("editCar", newData);
this.$router.push("/");
}
});
}
},
Also in your mutation cand make something like this:
EDIT_CAR: (state, car) => {
Object.assign(state.cars[state.cars.findIndex(c => c.id === car.id)], car);
}

Can you try to change your EDIT_CAR mutation to:
const index = state.cars.findIndex(x => x.id === car.id)
state.cars.splice(index, 1, car)
If you haven't done already, place a console.log(car) at the beginning of the mutation so you make sure it gets called and the car payload is what you expect.

Related

100 requests sent in 500ms unnecessarily using Cypress. Why?

I am intercepting a request to http://localhost:18545 in Cypress.
The cy.intercept() is within a beforeEach. After the test passes, the requests skyrocket to 100 requests in 500ms. Why?
This is the code that can be condensed but still working on a few things:
import { startServer } from '../../cypress/support/e2e'
describe('visit and interact with home page', () => {
const serverUrl = `http://testurl.invalidtld`
const ethProvider = `http://localhost:18545`
Cypress.on('uncaught:exception', (err, runnable) => {
return false
})
beforeEach(() => {
// deploy unirep and unirep social contract
const context = startServer()
Object.assign(context)
cy.intercept('GET', `${serverUrl}/api/post?*`, {
body: {
posts: 'string',
},
}).as('getApiContent')
cy.intercept('GET', `${serverUrl}/api/config`, {
body: {
unirepAddress: '0x41afd703a36b19D9BB94E3083baA5E4F70f5adD6',
unirepSocialAddress:
'0x903Ae15BfbddFAD6cd44B4cC1CF01EEBa0742456',
},
}).as('getApiConfig')
cy.intercept(`${ethProvider}*`, (req) => {
// conditonal logic to return different responses based on the request url
// console.log(req.body)
const { method, params, id } = req.body
if (method === 'eth_chainId') {
req.reply({
body: {
id: 1,
jsonrpc: '2.0',
result: '0x111111',
},
})
} else if (
method === 'eth_call' &&
params[0]?.data === '0x79502c55'
) {
req.reply({
body: {
id,
jsonrpc: '2.0',
result:
'0x' +
Array(10 * 64)
.fill(0)
.map((_, i) => (i % 64 === 63 ? 1 : 0))
.join(''),
},
})
} else if (method === 'eth_call') {
// other uint256 retrievals
req.reply({
body: {
id,
jsonrpc: '2.0',
result: '0x0000000000000000000000000000000000000000000000000000000000000001',
},
})
} else {
req.reply({
body: { test: 'test' },
})
}
}).as('ethProvider')
cy.intercept('GET', `${serverUrl}/api/genInvitationCode/*`, {
fixture: 'genInvitationCode.json',
}).as('genInvitationCode')
cy.intercept('GET', `${serverUrl}/api/signup*`, {
fixture: 'signup.json',
}).as('signup')
})
it('navigate to the signup page and signup a user', () => {
cy.visit('/')
cy.findByText('Join').click()
cy.findByRole('textbox').type('testprivatekey')
cy.findByText('Let me in').click()
})
})
I can add more context to the startServer logic within the e2e directory if requested.

Join 3 res.data vue.js

I want to join three res.data (carsIn,PositionUsed,position),this res.data I get it by axios.get
carsIn(id,username,useraddress,userphone,plate)
PositionUsed(id_pos,id_car,enterdate)
position(id_pos,name)
I tried this solution but I need to refresh 3 time to get data in array mergedd
any solution ?
I want to get mergedd (username,useraddress,userphone,plate,enterdate,name)
export default {
name: "Courses",
data() {
return {
carsIn: [],
PositionUsed:[],
merged:[],
positions:[],
mergedd:[],
message: "",
INSTRUCTOR: "in28minutes"
};
},
computed: {
currentUser() {
return this.$store.state.auth.user;
}
},
mounted() {
if (!this.currentUser) {
this.$router.push('/login');
}
},
methods: {
refreshCourses() {
clientService.retrieveAllCarsIn(this.INSTRUCTOR)
.then((res) => {
this.carsIn= res.data;
});
clientService.retrieveAllPositionOcp(this.INSTRUCTOR)
.then((res) => {
this.PositionUsed= res.data;
for(let i=0; i<this.carsIn.length; i++) {
this.merged.push({
...this.carsIn[i],
...(this.PositionUsed.find((itmInner) =>
itmInner.id_car === this.carsIn[i].plate))}
);
}
});
clientService.retrieveAllPositions(this.INSTRUCTOR)
.then((res) => {
this.positions = res.data;
for(let i=0; i<this.merged.length; i++) {
this.mergedd.push({
...this.merged[i],
...(this.positions.find((itmInner) => itmInner.id_pos
=== this.merged[i].id_pos))}
);
}
});
}
},
created() {
this.refreshCourses();
}
}
This is the best solution but it will only work if clientService.retrieveAllCarsIn , clientService.retrieveAllPositionOcp and clientService.retrieveAllPositions are promises
refreshCourses() {
Promise.all([clientService.retrieveAllCarsIn(this.INSTRUCTOR) , clientService.retrieveAllPositionOcp(this.INSTRUCTOR)] , clientService.retrieveAllPositions(this.INSTRUCTOR)).then((response) =>{
this.carsIn= response[0].data;
this.PositionUsed= response[1].data;
this.positions = response[2].data;
}).catch((err) =>{
//error handler
}).finally(() =>{
})
}
Seems like the you can rewrite your function something like this:
refreshCourses() {
// Get data for all 3 requests at the same time
const [courses, pos, positions] = Promise.all([clientService.retrieveAllCarsIn(this.INSTRUCTOR), clientService.retrieveAllPositionOcp(this.INSTRUCTOR), clientService.retrieveAllPositions(this.INSTRUCTOR)]);
// Now iterate over courses
this.mergedd = courses.map(({
id,
namecourse,
desc,
plate
}) => {
// Get date from pos array
const {
date,
id_position
} = pos.find(p => p.id_car === plate); // Or p.id_course === id
// Get nameposition from positions array
const {
nameposition
} = positions.find(p => p.id === id_position);
const out = {
id,
namecourse,
desc,
date,
nameposition
}
});
}

How to place a component's logic outside the file .vue itself

I'm building a webapp in Nuxt.js and it's growing quite a bit.
I have a page which does two things: one when i'm creating a task and one when managing that task.
This page has a lot of methods, divided for when i create the task and when i manage the task.
How can i split these modules in two files and then import then only when I need them?
These methods need also to access the component's state and other function Nuxt imports such as axios.
async create() {
if (this.isSondaggioReady()) {
try {
await this.getImagesPath()
const o = { ...this.sondaggio }
delete o.id
o.questions = o.questions.map((question) => {
delete question.garbageCollector
if (question.type !== 'checkbox' && question.type !== 'radio') {
delete question.answers
delete question.hasAltro
} else {
question.answers = question.answers.map((answer) => {
delete answer._id
delete answer.file
delete answer.error
if (answer.type !== 'image') delete answer.caption
return answer
})
}
if (question.hasAltro) {
question.answers.push({
type: 'altro',
value: ''
})
}
return question
})
console.log('TO SEND', JSON.stringify(o, null, 2))
this.$store.commit('temp/showBottomLoader', {
show: true,
message: 'Crezione del sondaggio in corso'
})
const { data } = await this.$axios.post('/sondaggi/admin/create', o)
this.sondaggio.id = data
const s = {
_id: data,
author: this.$auth.user.email.slice(0, -13),
title: this.sondaggio.title
}
this.$store.commit('temp/pushHome', { key: 'sondaggi', attr: 'data', data: [...this.$store.state.temp.home.sondaggi.data, s] })
this.$store.dispatch('temp/showToast', 'Sondaggio creato correttamente')
this.$router.replace('/')
} catch (e) {
console.log(e)
this.$store.dispatch('temp/showToast', this.$getErrorMessage(e))
} finally {
this.$store.commit('temp/showBottomLoader', {
show: false,
message: 'Crezione del sondaggio in corso'
})
}
}
},
Here there's an example of what a method does. It calls an async functions which relies on HTTP axios requests:
async getImagesPath() {
this.sondaggio.questions.forEach((question, i) => {
question.answers.forEach((answer, j) => {
if (answer.file instanceof File || answer.value.includes('data:image')) {
this.uploadingImages.push({
coords: [i, j],
percentage: 0,
file: answer.file || answer.value
})
}
})
})
const requests = []
this.uploadingImages.forEach((img) => {
const temp = new FormData()
temp.append('img', img.file)
const req = this.$axios.post('/sondaggi/admin/images/add/' + this.sondaggio.title.replace(/\s+/g, ''), temp, {
onUploadProgress: function (progressEvent) {
img.percentage = Math.round(((progressEvent.loaded * 100) / progressEvent.total) * 90 / 100)
},
onDownloadProgress: function (progressEvent) {
img.percentage = 90 + Math.round(((progressEvent.loaded * 100) / progressEvent.total) * 10 / 100)
},
headers: { 'Content-Type': 'multipart/form-data' }
})
.catch((err) => {
console.log(err)
img.percentage = 150
})
requests.push(req)
})
try {
const response = await Promise.all(requests)
response.forEach(({ data }, i) => {
this.sondaggio.questions[this.uploadingImages[i].coords[0]].answers[this.uploadingImages[i].coords[1]].value = data[0]
})
this.$set(this.sondaggio, 'hasImages', this.uploadingImages.length > 0)
this.uploadingImages = []
await Promise.resolve()
} catch (e) {
console.log('handling gloval err', e)
await Promise.reject(e)
}
},
As you can see axios requests modify the component's state

Is it possible create a moleculer service with many Validator instances?

I would like to have more than one Validator instance on my service to handle different languages. Is there any way to implement that?
Something like that:
{
en: new Validator({ messages: { ... }}),
de: new Validator({ messages: { ... }})
// ...
}
It is not available. You should create a custom multi-validators. Here is a quick example:
"use strict";
const _ = require("lodash");
const { ServiceBroker } = require("moleculer");
const BaseValidator = require("moleculer").Validators.Base;
const Validator = require("fastest-validator");
const DefaultMessages = require("fastest-validator/lib/messages");
const { ValidationError } = require("moleculer").Errors;
// --- I18N VALIDATOR CLASS ---
class I18NValidator extends BaseValidator {
constructor(messages) {
super();
0;
this.validators = {};
Object.keys(messages).forEach(lang => {
this.validators[lang] = new Validator();
this.validators[lang].messages = Object.assign({}, DefaultMessages, messages[lang]);
});
}
compile(schema) {
this.checks = {};
Object.keys(this.validators).forEach(lang => {
this.checks[lang] = this.validators[lang].compile(schema);
});
return this.checks;
}
middleware() {
return function I18NValidator(handler, action) {
// Wrap a param validator
if (action.params && typeof action.params === "object") {
const checks = this.compile(action.params);
return function validateContextParams(ctx) {
const check = checks[ctx.meta.lang] || checks["en"];
const res = check(ctx.params);
if (res === true)
return handler(ctx);
else
return Promise.reject(new ValidationError("Parameters validation error!", null, res));
};
}
return handler;
}.bind(this);
}
}
let broker = new ServiceBroker({
logger: true,
validation: true,
validator: new I18NValidator({
"en": {
"string": "The '{field}' field must be a string!"
},
"hu": {
"string": "A '{field}' mezőnek szövegnek kell lennie!"
}
})
});
// --- TEST BROKER ---
broker.createService({
name: "greeter",
actions: {
hello: {
params: {
name: { type: "string", min: 4 }
},
handler(ctx) {
return `Hello ${ctx.params.name}`;
}
}
}
});
broker.start()
// No meta lang
.then(() => broker.call("greeter.hello", { name: 100 }).then(res => broker.logger.info(res)))
.catch(err => broker.logger.error(err.message, err.data))
// "hu" lang
.then(() => broker.call("greeter.hello", { name: 100 }, { meta: { lang: "hu" }}).then(res => broker.logger.info(res)))
.catch(err => broker.logger.error(err.message, err.data))
// "en" lang
.then(() => broker.call("greeter.hello", { name: 100 }, { meta: { lang: "en" }}).then(res => broker.logger.info(res)))
.catch(err => broker.logger.error(err.message, err.data));
It reads the lang from the ctx.meta.lang but you can change it for your case.

Mutation for notifications in Vue.js

I have a store for notifications in my application. I can load all notifications and mark one notification as read. I wrote actions and mutations for each case. The last action is for marking all notifications as read but I am struggling with writing a mutation for this (commented part).
const actions = {
notifications(context) {
document.client
.get_notifications()
.then(ca => {
S_Helper.cmt_data(ca, "load_notifications", this);
})
.catch(error => {
ClientAlert.std_fail_with_err(error);
});
},
readNotification(id, params) {
document.client
.read_notification({ id, params })
.then(ca => {
if (ca.is_success()) {
context.commit("mark_notification", id);
} else {
ClientAlert.std_fail();
}
})
.catch(error => {
ClientAlert.std_fail_with_err(error);
});
},
readAllNotifications(context, params) {
params = params || {};
document.client
.read_all_notifications(params)
.then(ca => {
if (ca.is_success()) {
context.commit("mark_all_notifications", this);
} else {
ClientAlert.std_fail();
}
})
.catch(error => {
ClientAlert.std_fail_with_err(error);
});
}
};
const mutations = {
load_notifications(context, data) {
context.notifications = data;
},
mark_notification(state, id) {
let new_data = state.notifications.map(function(element) {
if (!!element.id && element.id === id) {
element.viewed = true;
}
return element;
});
state.notifications = new_data;
}
//mark_all_notifications(context, data) {
//}
};
mark_all_notifications(state, data) {
state.notifications = state.notifications.map(notification => {
notification.viewed = true
return notification
})
}
A simple map should work.