Deleting an item in the store Vuex - vue.js

I would like to delete an item that is located in my store.
I saw this solution https://stackoverflow.com/a/59686164/11984242 which seems to be what I'm looking for BUT I have another "level" in my object so I don't know how to delete an element from a sub-level.
The structure of my data.
{
id: 1,
name: 'My customer one',
projects: [
{
id: 1,
name: 'name',
tests: [
{
id: 1,
name: 'name test'
},
{
id: 2,
name: 'name test 2'
}
]
},
{
id: 2,
name: 'other name'
}
]
}
So I see how to delete a project with the link above. But how to delete one row of tests ?
I do this in my vue :
this.$store.commit("deleteTest", this.idProject, e)
And this in my store :
deleteTest(state, {data, e}) {
const allTestsOfProject = state.listProjects.projects.find(p => p.id === data)
console.log("all", allTestsOfProject)
const oneTest = allTestsOfProject.studies.find(q => q.id === e)
console.log("one test", oneTest)
//state.listProjects.projects.splice(oneTest, 1)
}
Thanks a lot for help

You Have two problem with your code:
First Problem
commit function takes only 2 params:
commit name ( as String )
Payload Pbject
so if you want to pass multiple props you should pass them as an object
this.$store.commit("deleteTest", this.idProject, e) // WON'T WORK
this.$store.commit("deleteTest", {data: this.idProject, e}) // WILL WORK
now you can call deleteTest(state, {data, e})
Second Problem
you should get the INDEX of tests object not the OBJECT itself
const oneTest = allTestsOfProject.studies.find(q => q.id === e) // WON'T WORK
const oneTest = allTestsOfProject.studies.findIndex(q => q.id === e) // WILL WORK
now you can call: allTestsOfProject.studies.findIndex (q => q.id === e) to delete your test

Related

Error: Exception in HostFunction: Attempting to create an object of type 'sets' with an existing primary key value '6' in react native

I'm trying to store history of workout in realm, my addHistory function looks like this
export function addHistory(workout, exercise, sets, _id) {
console.log({
workout,
exercise,
sets,
_id,
});
if (
_id !== undefined &&
workout !== undefined &&
exercise !== undefined &&
sets !== undefined
) {
// return console.log("HISTORY ", { workout, exercise, sets, _id });
return realm.write(() => {
return realm.create("workoutData", {
_id: _id,
exercise,
workout,
sets,
workoutDate: new Date(Date.now()),
});
});
} else {
alert("History is incomplete");
}
}
Schema of the workoutData is as follows:
exports.workoutData = {
name: "workoutData",
primaryKey: "_id",
properties: {
_id: "int",
workout: "workouts",
exercise: "exercise",
workoutDate: "date",
sets: "sets[]",
},
};
Now when I add sets and click on finishWorkoutHandler the logic works fine before the addHistory function but when addHistory is executed it throws the error as stated in the question.
//finish workout handler
const finishWorkoutHandler = () => {
if (sets.length == 0) {
return;
}
let setsFromRealm = realm.objects("sets");
let workoutData = realm.objects("workoutData");
let setsArray = [];
exercises.forEach((exercise) => {
sets
.filter((items) => items.exercise._id == exercise._id)
.forEach((sets) => {
let _id = 0;
if (setsFromRealm.length > 0) {
_id = realm.objects("sets").max("_id") + 1;
}
addSet(
sets.name,
parseInt(sets.weight),
parseInt(sets.reps),
parseInt(sets.rmValue),
sets.isHeighest,
sets.exercise,
_id,
sets.profile,
sets.failedSet,
sets.warmupSet,
sets.notes
);
let indiSet = {
name: sets.name,
weight: parseInt(sets.weight),
reps: parseInt(sets.reps),
rmValue: parseInt(sets.rmValue),
isHeighest: sets.isHeighest,
_id: _id,
profile: sets.profile,
failedSet: sets.failedSet,
warmupSet: sets.warmupSet,
notes: sets.notes,
createdDate: new Date(Date.now()),
};
setsArray.push(indiSet);
});
let workoutDataId = 0;
let setsArrcopy = setsArray;
console.log("SETS ", realm.objects("sets"));
console.log("SETS ", setsArrcopy);
if (workoutData.length > 0) {
workoutDataId = realm.objects("workoutData").max("_id") + 1;
}
**WORKING AS EXPECTED TILL HERE**
// problem lies here
addHistory(params.workout, exercise, setsArrcopy, workoutDataId);
});
dispatch(setsEx([]));
goBack();
};
the structure of setsArrCopy containing sets is as follows
[
({
_id: 6,
createdDate: 2022-09-29T16:27:06.128Z,
failedSet: false,
isHeighest: false,
name: "Thai",
notes: "",
profile: [Object],
reps: 12,
rmValue: 64,
warmupSet: false,
weight: 56,
},
{
_id: 7,
createdDate: 2022-09-29T16:27:06.151Z,
failedSet: false,
isHeighest: false,
name: "Thsi 3",
notes: "",
profile: [Object],
reps: 10,
rmValue: 75,
warmupSet: false,
weight: 66,
})
];
the logic is also working fine in terms of assigning new ids to the sets being added in a loop. But somehow its throwing error when passing setArrCopy to addHistory function. Although its an array of sets not a single object?

Sequelize query with a where clause on an include of an include

I'm struggling to create a query with sequelize.
Some context
I have the following models:
A Manifestation can have [0..n] Event
An Event belongs to one Manifestation (an Event cannot exist without a Manifestation)
A Place can have [0..n] Event
An Event belongs to one Place (an Event cannot exist without a Place)
A Manifestation can have [1..n] Place
A Place can have [0..n] Manifestation
I model the relations as the following:
Manifestation.hasMany(Event, { onDelete: 'CASCADE', hooks: true })
Event.belongsTo(Manifestation)
Place.hasMany(Event, { onDelete: 'CASCADE', hooks: true })
Event.belongsTo(Place)
Manifestation.belongsToMany(Place, { through: 'manifestation_place' })
Place.belongsToMany(Manifestation, { through: 'manifestation_place' })
For me it seems rather correct, but don't hesitate if you have remarks.
The question
I'm trying to query the Place in order to get all Manifestation and Event happening in a given Place. But for the Event ones, I want to include them within their Manifestation even if the Manifestation doesn't happen in the given Place.
Below is the "JSON" structure I'm trying to achieve:
{
id: 1,
name: "Place Name",
address: "Place address",
latitude: 47.00000,
longitude: -1.540000,
manifestations: [
{
id: 10,
title: "Manifestation one",
placeId: 1,
events: []
},
{
id: 11,
title: "Manifestation two",
placeId: 3,
events: [
id: 5,
title: "3333",
manifestationId: 11,
placeId: 1
]
}
]
}
So I want to include the Manifestation with id: 11, because one of its Event occurs in the given Place (with id: 1)
Update (04/06/20): For now I rely on javascript to get the expected result
I figured out it would be nice if I posted my current solution before asking.
router.get('/test', async (req, res) => {
try {
const placesPromise = place.findAll()
const manifestationsPromise = manifestation.findAll({
include: [
{ model: event },
{
model: place,
attributes: ['id'],
},
],
})
const [places, untransformedManifestations] = await Promise.all([
placesPromise,
manifestationsPromise,
])
const manifestations = untransformedManifestations.map(m => {
const values = m.toJSON()
const places = values.places.map(p => p.id)
return { ...values, places }
})
const result = places
.map(p => {
const values = p.toJSON()
const relatedManifestations = manifestations
.filter(m => {
const eventsPlaceId = m.events.map(e => e.placeId)
return (
m.places.includes(values.id) ||
eventsPlaceId.includes(values.id)
)
})
.map(m => {
const filteredEvents = m.events.filter(
e => e.placeId === values.id
)
return { ...m, events: filteredEvents }
})
return { ...values, manifestations: relatedManifestations }
})
.filter(p => p.manifestations.length)
return res.status(200).json(result)
} catch (err) {
console.log(err)
return res.status(500).send()
}
})
But I'm pretty sure I could do that directly with sequelize. Any ideas or recommendations ?
Thanks
This is not optimum. But you can try it out:
const findPlace = (id) => {
return new Promise(resolve => {
db.Place.findOne({
where: {
id: id
}
}).then(place => {
db.Manefestation.findAll({
include: [{
model: db.Event,
where: {
placeId: id
}
}]
}).then(manifestations => {
const out = Object.assign({}, {
id: place.id,
name: place.name,
address: place.address,
latitude: place.latitude,
longitude: place.longitude,
manifestations: manifestations.reduce((res, manifestation) => {
if (manifestation.placeId === place.id || manifestation.Event.length > 0) {
res.push({
id: manifestation.id,
title: manifestation.id,
placeId: manifestation.placeId,
events: manifestation.Event
})
}
return res;
}, [])
})
})
resolve(out);
})
})
}
From this, you get all manifestations that assigned to place or have any event that assigns. All included events in the manefestations are assigned to the place.
Edit :
You will be able to use the following one too:
const findPlace = (id) => {
return new Promise(resolve => {
db.Place.findOne({
include: [{
model: db.Manefestation,
include: [{
model: db.Event,
where: {
placeId: id
}
}]
}],
where: {
id: id
}
}).then(place => {
db.Manefestation.findAll({
include: [{
model: db.Event,
where: {
placeId: id
}
}],
where: {
placeId: {
$not: id
}
}
}).then(manifestations => {
place.Manefestation = place.Manefestation.concat(manifestations.filter(m=>m.Event.length>0))
resolve(place);// or you can rename, reassign keys here
})
})
})
}
Here I take only direct manifestations in the first query. Then, manifestations that not included and concatenate.
I do not know if you figure it out by now. But the solution is provided below.
Search with Sequelize could get funny :). You have to include inside another include. If the query gets slow use separate:true.
Place.findAll({
include: [
{
model: Manifestation,
attributes: ['id'],
include: [{
model: Event ,
attributes: ['id']
}]
},
],
})
I tried to complete it in a single query but you will still need JavaScript to be able to get the type of output that you want.
(Note: đź’ˇ You need manifestation which is not connected to places but should be included if a event is present of that place. The only SQL way to get that starts by doing a CROSS JOIN between all tables and then filtering out the results which will be a very hefty query)
I came up with this code(tried & executed) which doesn't need you to execute 2 findAll that fetches all data as what you are currently using. Instead it fetched only the data needed for final output in 1 query.
const places = await Place.findAll({
include: [{
model: Manifestation,
// attributes: ['id']
through: {
attributes: [], // this helps not get keys/data of join table
},
}, {
model: Event,
include: [{
model: Manifestation,
// attributes: ['id']
}],
}
],
});
console.log('original output places:', JSON.stringify(places, null, 2));
const result = places.map(p => {
// destructuring to separate out place, manifestation, event object keys
const {
manifestations,
events,
...placeData
} = p.toJSON();
// building modified manifestation with events array
const _manifestations = manifestations.map(m => {
return ({ ...m, events: [] })
});
// going through places->events to push them to respective manifestation events array
// + add manifestation which is not directly associated to place but event is of that manifestation
events.map(e => {
const {
manifestation: e_manifestation, // renaming variable
...eventData
} = e;
const mIndex = _manifestations.findIndex(m1 => m1.id === e.manifestationId)
if (mIndex === -1) { // if manifestation not found add it with the events array
_manifestations.push({ ...e_manifestation, events: [eventData] });
} else { // if found push it into events array
_manifestations[mIndex].events.push(eventData);
}
});
// returning a place object with manifestations array that contains events array
return ({ ...placeData, manifestations: _manifestations });
})
// filter `.filter(p => p.manifestations.length)` as used in your question
console.log('modified places', JSON.stringify(result, null, 2));

I have event duplication after action was moved in store object

In my laravel 5.8 / vue 2.5.17 / vuex^3.1.0 I have a problem that with dialog opened I have event duplication.
I have an event for item deletion :
In my vue file:
...
mounted() {
bus.$on('dialog_confirmed', (paramsArray) => {
if (paramsArray.key == this.deleteFromUserListsKey(paramsArray.user_list_id)) {
this.runDeleteFromUserLists(paramsArray.user_list_id, paramsArray.index);
}
})
bus.$on('onUserListDeleteSuccess', (response) => {
this.is_page_updating = false
this.showPopupMessage("User lists", 'User\'s list was successfully deleted!', 'success');
})
bus.$on('onUserListDeleteFailure', (error) => {
this.$setLaravelValidationErrorsFromResponse(error.message);
this.is_page_updating = false
this.showRunTimeError(error, this);
this.showPopupMessage("User lists", 'Error adding user\'s list !', 'error');
})
}, // mounted() {
methods: {
confirmDeleteUserList(user_list_id, user_list_title, index) {
this.confirmMsg("Do you want to exclude '" + user_list_title + "' user list ?", {
key: this.deleteFromUserListsKey(user_list_id), user_list_id: user_list_id, index: index
}, 'Confirm', bus);
}, //confirmDeleteUserList(id, user_list_title, index) {
deleteFromUserListsKey(user_list_id) {
return 'user_list__remove_' + user_list_id;
},
runDeleteFromUserLists(user_list_id, index) {
this.$store.dispatch('userListDelete', { logged_user_id : this.currentLoggedUser.id, user_list_id : user_list_id } );
}, // runDeleteFromUserLists() {
and in resources/js/store.js :
state : {
...
userLists: [],
...
actions : {
userListDelete(context, paramsArray ) {
axios({
method: ( 'delete' ),
url: this.getters.apiUrl + '/personal/user-lists/' + paramsArray.user_list_id,
}).then((response) => {
let L = this.getters.userLists.length
for (var I = 0; I < L; I++) {
if (response.data.id == this.getters.userLists[I].id) {
this.getters.userLists.splice(this.getters.userLists.indexOf(this.getters.userLists[I]), 1)
context.commit('refreshUserLists', this.getters.userLists);
break;
}
}
bus.$emit( 'onUserListDeleteSuccess', response );
}).catch((error) => {
bus.$emit('onUserListDeleteFailure', error);
});
}, // userListDelete(context, paramsArray ) {
confirmMsg (based on https://github.com/euvl/vue-js-modal )is defined in my mixing :
confirmMsg: function (question, paramsArray, title, bus) {
this.$modal.show('dialog', {
title: title,
text: question,
buttons: [
{
title: 'Yes',
default: true, // Will be triggered by default if 'Enter' pressed.
handler: () => {
bus.$emit('dialog_confirmed', paramsArray);
this.$modal.hide('dialog')
}
},
{
title: '', // Button title
handler: () => {
} // Button click handler
},
{
title: 'Cancel'
}
]
})
},
it worked ok, until I moved userListDelete method from my vue file into store.js.
As a result on 1st event item is deleted ok, the the second item raise error that item was not found and I do not know event is doubled...
How to fix it ?
UPDATED BLOCK :
I still search for valid decision :
I uploaded live demo at :
http://178.128.145.48/login
demo#demo.com wdemo
http://178.128.145.48/websites-blogs will be opened.
Please, try to go to “User's lists” by link at top left menu https://prnt.sc/nq4qiy
and back several times. When on “User's lists” page I try to delete 1 user list it is deleted, but I got several messages
and url in “network” section of my browser : https://imgur.com/a/4ubFB0g
Looks like events are duplicated. And looks like that is move between pages number of guplications is raised.
Why and how to fix it ?
I use #click.prevent in triggering the event to show confirm delete message.
There is “ Add Demo Data” to add more demo rows.
Thanks!
Well, it is quite obvious.
Take a closer look at the Vue component lifecycle diagram.
Your component is mounted each time you enter a route.
So, bus.$on inside your mounted block executed each time you enter this route.
I suggest you move bus event handlers to some other location. For example app.js/ App.vue mounted hook or directly into the store. Since all you do inside handler is calling store actions.

Vue.js with iView Table - accessing Elasticsearch search response object

I am using the iView UI kit table in my Vue.js application that consumes an Elasticsearch API with axios. My problem is that I just can't seem to get to access the nested search response object, which is an array list object. I only get to access the 1st level fields, but not the nested ones. I don't know how to set the table row key in the iView table.
This is how my axios call and mapper methods look like:
listObjects(pageNumber){
const self = this
self.$Loading.start()
self.axios.get("/api/elasticsearch/")
.then(response => {
self.ajaxTableData = self.mapObjectToArray(response.data);
self.dataCount = self.ajaxTableData.length;
if(self.ajaxTableData.length < self.pageSize){
self.tableData = self.ajaxTableData;
} else {
self.tableData = self.ajaxTableData.slice(0,self.pageSize);
}
self.$Loading.finish()
})
.catch(e => {
self.tableData = []
self.$Loading.error()
self.errors.push(e)
})
},
mapObjectToArray(data){
var mappedData = Object.keys(data).map(key => {
return data[key];
})
return mappedData
},
The iView table columns look like this:
tableColumns: [
{
title: 'Study Date',
key: 'patientStudy.studyDate',
width: 140,
sortable: true,
sortType: 'desc'
},
{
title: 'Modality',
key: "generalSeries.modality",
width: 140,
sortable: true
},
...
]
The (raw) Elasticsearch documents look like this:
[
{ "score":1, "id":"3a710fa2c1b3f6125fc168c9308531b59e21d6b3",
"type":"dicom", "nestedIdentity":null, "version":-1, "fields":{
"highlightFields":{
},
"sortValues":[
],
"matchedQueries":[
],
"explanation":null,
"shard":null,
"index":"dicomdata",
"clusterAlias":null,
"sourceAsMap":{
"generalSeries":[
{
"seriesInstanceUid":"999.999.2.19960619.163000.1",
"modality":"MR",
"studyInstanceUid":"999.999.2.19960619.163000",
"seriesNumber":"1"
}
],
"patientStudy":[
{
"studyDate":"19990608"
}
]
}
}
]
And this is how the consumed object looks like:
As you can see, the fields I need to access are within the "sourceAsMap" object, and then nested in arrays.
How can I provide the iView table cell key to access them?
UPDATE:
I now "remapped" my Elasticsearch object before displaying it in the Vue.js table, and it works now. However, I don't think that the way I did it is very elegant or clean....maybe you can help me to do it in a better way. This is my method to remap the object:
getData(data){
let jsonMapped = []
for(let i = 0; i < data.length; i++){
let id = {}
id['id'] = data[i].id
let generalData = data[i]['sourceAsMap']['generalData'][0]
let generalSeries = data[i]['sourceAsMap']['generalSeries'][0]
let generalImage = data[i]['sourceAsMap']['generalImage'][0]
let generalEquipment = data[i]['sourceAsMap']['generalEquipment'][0]
let patient = data[i]['sourceAsMap']['patient'][0]
let patientStudy = data[i]['sourceAsMap']['patientStudy'][0]
let contrastBolus = data[i]['sourceAsMap']['contrastBolus'][0]
let specimen = data[i]['sourceAsMap']['specimen'][0]
jsonMapped[i] = Object.assign({}, id, generalData, generalSeries, generalImage, generalEquipment, patient,
patientStudy, contrastBolus, specimen)
}
return jsonMapped
},
The result is this:
Even though it now works, but how can I optimize this code?
A few functions can help you with your situation
let data = [{
key1: ['k1'],
key2: ['k2'],
key3: [{
subKey1: 'sk1',
subKey2: ['sk2'],
subObject: [{ name: 'John', surname: 'Doe' }],
items: [1, 2, 3, 5, 7]
}]
}];
function mapIt(data) {
if (isSingletonArray(data)) {
data = data[0];
}
for(const key in data) {
if (isSingletonArray(data[key])) {
data[key] = mapIt(data[key][0]);
} else {
data[key] = data[key];
}
}
return data;
}
function isSingletonArray(obj) {
return typeof obj === 'object' && Array.isArray(obj) && obj.length === 1;
}
console.log(mapIt(data));
Outputs:
{
key1: 'k1',
key2: 'k2',
key3: {
subKey1: 'sk1',
subKey2: 'sk2',
subObject: { name: 'John', surname: 'Doe' },
items: [ 1, 2, 3, 5, 7 ]
}
}
You can check it in your browser. mapIt unwraps the singleton arrays into objects or primitives.
But I recommend you to watch out special elastic client libraries for javascript. It could save you from writing extra code.

Redux Update a state with an array of objects

I have a state like this:
this.state ={
photos: [
{ description: 'someDescription', id: 1 },
{ description: 'someDescription', id: 2 },
{ description: 'someDescription', id: 3 }
]
}
How can I update one of the descriptions only?
Or I have to do something like
this.setState({ photos: newArrayOfObjectsWithOnlyOneUpdatedDescription })
You can create a funtion to do it for you like this:
const updatePhoto = (id, desc) =>
this.state.photos.map((obj) =>
obj.id === id ? Object.assign(obj, { description: desc }) : obj)
map function will return a new array, so you won't need to do a manual state cloning stuff.
Then reuse it as you need it:
this.setState({ photos: updatePhoto(2, 'new desc') })
You can create copy of state, then update copied state and setState
let stateCopy = Object.assign({}, this.state); // get the current state copy
stateCopy.photos = stateCopy.photos.slice(); // get photos object
stateCopy.photos[key] = Object.assign({}, stateCopy.photos[key]);
stateCopy.photos[key].description = 'new decription'; // update description of specific key
this.setState(stateCopy); // set state