how to Vue.set with item id instead of index - vue.js

i have realtime application what i am doing get available data from backend and save as array.
when user click its going to unavailable sending index to backend its broadcast to all users who is in current page and hide unavailable data.
var vm = this;
this.$echo.channel("accept-coin").listen("AcceptCoin", response => {
vm.$set(vm.coins, response.indexid, response.coin);
vm.coins[response.indexid].result = response.result;
vm.$auth.fetchUser();
setTimeout(() => {
this.coins[response.indexid].hide = false;
}, 10000);
});
problem is if current array have 5 objects for example and 2 hide unavailable when new user open this page he will have only 3 objects
if user who have 5 objects in array if click 5th object. its working on current user but other who have 3 objects in array getting error.
what i actually want that i want to modify object with item id instead of index

simple solution i found that use findIndex methods
var vm = this;
this.$echo.channel("accept-coin").listen("AcceptCoin", response => {
var indexid = vm.coins.findIndex(f => f.id === response.coin.id); //<-- this find index with coin id so then i can do whatever
vm.$set(vm.coins, indexid, response.coin);
vm.coins[indexid].result = response.result;
vm.$auth.fetchUser();
setTimeout(() => {
this.coins.splice(indexid, 1);
}, 10000);
});
Hope this helps

Related

Vuex model update won't reload computed property

I have the following component to quickly configure stops on a delivery/pickup route and how many items are picked up and dropped
and this is the data model, note the 2 is the one next to 'a' on the previous image.
If a click the + or - button, in the first item, it behaves as expected,
But second item doesn't work as expected
I've already checke a couple of posts on object property update likes this ones
Is it possible to mutate properties from an arbitrarily nested child component in vue.js without having a chain of events in the entire hierarchy?
https://forum.vuejs.org/t/nested-props-mutations-hell-internet-need-clarification/99346
https://forum.vuejs.org/t/is-mutating-object-props-bad-practice/17448
among others, and came up with this code:
ADD_ITEM_TO_SELECTED_STOP(state, payload) {
let count = state.selectedStop.categories[payload.catIndex].items[payload.itemIndex].count;
const selectedCat = state.selectedStop.categories[payload.catIndex];
const currentItem = selectedCat.items[payload.itemIndex];
currentItem.count = count + 1;
selectedCat.items[payload.itemIndex] = currentItem;
Vue.set(state.selectedStop.categories, payload.catIndex, selectedCat);
},
and as the button event:
addToItem(item) {
this.$store.dispatch("addItemToSelectedStop", {
catIndex: item.catIndex,
itemIndex: item.itemIndex
})
},
And finally my computed property code:
items() {
let finalArray = [];
this.selectedStop.categories.forEach(
(cat, catIndex) => {
let selected = cat.items.filter((item) => item.count > 0 );
if (selected.length > 0) {
//here we add the catIndex and itemIndex to have it calling the rigth shit
selected = selected.map(val => {
let itemIndex = cat.items.findIndex( itemToFind => itemToFind.id === val.id);
return {
...val,
catIndex: catIndex,
itemIndex: itemIndex,
}})
finalArray = finalArray.concat(selected);
}
});
return finalArray;
}
What confuses me the most is that I have almost the same code in another component, and there it's working as expected, and although the model is changed, the computed property is only recalculated on the first item,
After reading this gist and taking a look again at the posts describing this kind of issue, I decided to give it a try and just make a copy of the whole stored object not just the property, update it, then set it back on vuex using Vue.set, and that did the trick, everything is now working as expected, this is my final store method.
ADD_ITEM_TO_SELECTED_STOP(state, payload) {
let selectedLocalStop = JSON.parse(JSON.stringify(state.selectedStop));
let count = selectedLocalStop.categories[payload.catIndex].items[payload.itemIndex].count;
selectedLocalStop.categories[payload.catIndex].items[payload.itemIndex].count = count + 1;
Vue.set(state,"selectedStop", selectedLocalStop );
//Now we search for this step on the main list
const stepIndex = state.stops.findIndex(val => val.id === selectedLocalStop.id);
Vue.set(state.stops,stepIndex, selectedLocalStop );
},
I had to add the last bit after updating the whole object, because, originally, the array items were updated when the selected item was changed, I guess some sort of reference, but with the object creation, that relationship no longer works "automatic" so I need to update the array by hand

How I can increase the url number in load more data in Vue

I am implment show more data button that's data came from API (backend)
and the api url have number when this number (visable) change the data change
.post(`/account/api/auth/user/${userName}/posts/more/${visible}/`)
I created function that increse number by 3 but it seems not to work and the api had same number without change
$('#show_more_posts_button').on('click', ()=>{
visible += 3
console.log(visible)
this.showMore()
})
This the function of load more button
async showMore() {
var visible = 3
this.buttonText = 'Loading more images...';
const userName = this.$route.params.username
await axios
.post(`/account/api/auth/user/${userName}/posts/more/${visible}/`).then(response => {
this.more = response.data.data
});
this.buttonText = 'Show More';
},
The button of show more data
<button class="show_more_button" id="show_more_posts_button" ><p class="Show_more_profile">Show More <i class="fas fa-chevron-down" ></i></p></button>
#Ahmed it's hard to tell how all the pieces fit together from the amount of code you've shared, but within your showMore function you're instantiating a new variable called visibile and assigning it's value to 3 each time. So within this function it will always be 3.
This part here
async showMore() {
var visible = 3
...etc...
How about alternatively, from within the below on click listener, you instead pass in the newly updated visible variable as an argument to showMore. ie
$('#show_more_posts_button').on('click', ()=>{
visible += 3
console.log(visible)
this.showMore(visible)
})
which would change your showMore function to look like
async showMore(visible) {
this.buttonText = 'Loading more images...';
const userName = this.$route.params.username
await axios.post(`/account/api/auth/user/${userName}/posts/more/${visible}/`).then(response => {
this.more = response.data.data
});
this.buttonText = 'Show More';
},

How to map some data to the data fetched from API before showing it on screen?

I am new to React Native. I am trying to make an app of my own to try out the different things that I learnt and also get to know new things and one such thing that I came across and is giving me a hard time is the following issue:
I have an API which gives me certain data about an item. The properties of the item are listed in the API like "sizeofitem" , "nameofitem" or "itemacategory". Now there are multiple items for different items and not all properties are present in each item. What I was trying to achieve is to somehow map these properties in the following manner:
If let's say "sizeofitem", should become "Size of Item", "nameofitem" should become "Name of Item". Now these properties are different of all the items so for example, sizeofitem might be in one item detail list but might not be in another, but I have all the properties that are can be there. Can someone help me how to do this?
Till now I have the following:
const [itemDtl , setItemDtl] = useState([]);
const getItemInfo = async (id) => {
try{
const response = await api.get(`myAPI/${id}`);
setItemDtl(response.data.obj.itemutils);
}catch(err){
console.log(err);
}
}
let arr = [];
for(let i in itemDtl){
arr.push(itemDtl[i].util_type);
}
console.log(arr);
useEffect(() => {
getItemInfo(id);
})
arr array has whatever the properties where listed for the item in the API i.e. [sizeofitem, nameofitem , etc].
I want an array to have [Size of Item, Name of Item , etc].
Basically just, to sum up, I want to rename the list of properties that can be there for when whatever property comes up is then stored in an array with the mapped string I have given, so for example if an item has 'sizeofitem : 50', I want it to be stored as "Size of item" so that I can show that on the screen. And there are like a total of 5 properties that can exist for an item so I can code it somewhere maybe like sizeofitem : 'Size Of Item' so that when sizeofitem property is top be shown on the screen I can use this and show Size of Item on the screen.
try this:
const [itemDtl , setItemDtl] = useState([]);
const getItemInfo = async (id) => {
try{
const response = await api.get(`myAPI/${id}`);
let arr = [];
for(let i in itemDtl){
arr.push(itemDtl[i].util_type);
}
setItemDtl(arr);
}catch(err){
console.log(err);
}
}
useEffect(() => {
getItemInfo(id);
})

How to solve duplicate page in ion-infinite-scroll ionic 4

I am using ionic 4 and I am doing pagination using ion-infinite-scroll. My problem is I always get the duplicate page problem. Can I know how to solve this duplicate problem? Here is my code in home.page.ts:
doInfinite(event) {
this.userService.getData().then(res => {
event.target.complete();
});
}
loadData(event) {
console.log('Load more data');
this.userService.getData().then(res => {
event.target.complete();
});
}
Here is home.html
<ion-infinite-scroll (ionInfinite)="loadData($event)">
<ion-infinite-scroll-content
loadingSpinner="bubbles"
loadingText="loading ...">
</ion-infinite-scroll-content>
</ion-infinite-scroll>
It depends what your userService.getData() looks like.
It doesn't look like you are telling it to start at an offset.
Each time you pull data down, you should assign that list data to some local on-page variable, let's say dataList.
Then use this.dataList.length as the starting index for your next data request.
So some pseudo-code for how this might work would be:
let dataFeed = [];
let startAtRecord = 0;
constructor() {
this.userService.getData(startAtRecord).then(res => {
this.dataFeed = res;
this.startAtRecord = this.dataService.length;
});
}
loadData(event) {
// ask for a batch of records, starting at `startAtRecord`
this.userService.getData(startAtRecord).then(res => {
// add the new res data to the existing dataFeed
this.dataFeed = [...this.dataFeed, ...res];
// keep track of the number of records loaded
this.startAtRecord = this.dataService.length;
event.target.complete();
});
}
Do you see what I'm saying? The data service has to load the next page of data so you don't get the same one back, so it needs to track where its starting the list from.

Vuexfire bindFirebaseRef with pagination infinite scroll

Question: How can I add pagination (infinite scroll) to my binded Firestore VuexFire reference without re-querying previously retrieved (and binded) results?
Background:
I am currently using VuexFire firestore binding to fill a timeline for most upvoted posts, as an action, in my Vuex store like this:
fillTimeLine: firebaseAction(
({ bindFirebaseRef }) => {
bindFirebaseRef(
'timelineResults',
db
.collection('POSTS')
.orderBy('combined_vote_score', 'desc')
.limit(30)
)
})
This will retrieve the top 30 highest rated posts in my firestore database to my vuex state variable timelineResults.
To add pagination I have found a non-VuexFire example like this:
How to paginate or infinite scroll by number of items in firestore?
var first = db.collection("....").orderBy("price", "desc").limitTo(20);
return first.get().then(function (documentSnapshots) {
// Get the last visible document
var lastVisible = documentSnapshots.docs[documentSnapshots.docs.length-1];
console.log("last", lastVisible);
// Construct a new query starting at this document,
// get the next 25 cities.
var next = db.collection("....")
.orderBy("price", "desc")
.startAfter(lastVisible)
.limit(20);
});
Is there a way to combine the two examples and append results to a binded reference?
You could create a more generic action, just like this:
bindRef: firestoreAction(({ bindFirestoreRef }, { name, ref }) => {
bindFirestoreRef(name, ref);
}),
And then using it like:
this.bindRef({
name: 'timelineResults',
ref: db
.collection('POSTS')
.orderBy('combined_vote_score', 'desc')
.limit(30),
});
There you can change the ref according to your needs. In this case, when you detect the scroll limit:
// lastVisible: using the array position from the previous binding
// since with vuex's bound data you cannot get the snapshots
this.bindRef({
name: 'timelineResults',
ref: db
.collection('POSTS')
.orderBy('combined_vote_score', 'desc')
.startAfter(lastVisible)
.limit(20),
});