Duplicate items in list after an API update - vue.js

I'm learning vuejs and I'm doing a weather app, the goal is to rank cities with an index (humidex). I fetch weather information by API (axios) in order to collect data from several cities. I want to auto update data every x minutes, problem : some of my results are duplicated (the new data don't replace the old one).
I tried to set an unique key (based on latitude and longitude) for each item, it works for several results but not for all.
data () {
return {
items:[],
show: false,
cities: cities,
newCity:''
}
},
components: {
Item
},
computed: {
sortHumidex() {
return this.items.slice().sort((a,b) => {
return this.getHumidex(b) - this.getHumidex(a) || b.current.temp_c - a.current.temp_c
})
}
},
methods: {
addCity() {
if (this.newCity.trim().length == 0) {
return
}
this.cities.push(this.newCity)
this.newCity = ''
},
getHumidex: (el) => {
const e = 6.112 * Math.pow(10,(7.5*el.current.temp_c/(237.7+el.current.temp_c)))
*(el.current.humidity/100)
return Math.round(el.current.temp_c + 5/9 * (e-10))
},
indexGeo: (e) => {
const lat = Math.round(Math.abs(e.location.lat))
const lon = Math.round(Math.abs(e.location.lon))
return lat.toString() + lon.toString()
},
getApi: function () {
const promises = [];
this.cities.forEach(function(element){
const myUrl = apiUrl+element;
promises.push(axios.get(myUrl))
});
let self = this;
axios
.all(promises)
.then(axios.spread((...responses) => {
responses.forEach(res => self.items.push(res.data))
}))
.catch(error => console.log(error));
}
},
created() {
this.getApi()
this.show = true
}
}
The render when I update API :

By pushing to the existing array of items, you have to deal with the possibility of duplicates. This can be eliminated simply by replacing items every time the API call is made.
Replace:
responses.forEach(res => self.items.push(res.data))
with:
self.items = responses.map(res => res.data)

Related

How to search inside an array property of an index

I have to retrieve a list of apps that have certain string inside of an array property:
const users = algoliaClient.initIndex('apps')
return users.search('myId123', { hitsPerPage: 50 });
The apps objects are just like this:
{categories: ['an', 'interesting', 'category']}
How can I search the index for items with a categories property that contains any item in an array of strings?
const search = ["cat1", "cat2", "cat3"];
const apps = [
{
name: "app1",
categories: ["cat1", "cat5"],
},
{
name: "app2",
categories: ["cat2", "cat4"],
},
];
is that what you mean ? if so :
var myIndexes = [];
apps.forEach((app, index) => {
search.every((search) => {
if (app.categories.includes(search)) {
myIndexes.push(index);
return false; // return false here is for stoping the EVERY loop
} else {
return true; // keep running the EVERY loop
}
});
});

Reordering Array for Todos in MST Mobx State Tree

I would like to reorder arrays when using mobx state tree.
Say I have this example taken from the example page.
How do I get to reorder my ToDos in the TodoStore.
As a simplified example, say my todos are ['todo1, todo2'], how do I change them so that the new array is ['todo2, todo1']?
const Todo = types
.model({
text: types.string,
completed: false,
id: types.identifierNumber
})
.actions((self) => ({
remove() {
getRoot(self).removeTodo(self)
},
edit(text) {
if (!text.length) self.remove()
else self.text = text
},
toggle() {
self.completed = !self.completed
}
}))
const TodoStore = types
.model({
todos: types.array(Todo),
filter: types.optional(filterType, SHOW_ALL)
})
.views((self) => ({
get completedCount() {
return self.todos.filter((todo) => todo.completed).length
},
}))
.actions((self) => ({
addTodo(text) {
const id = self.todos.reduce((maxId, todo) => Math.max(todo.id, maxId), -1) + 1
self.todos.unshift({ id, text })
},
removeTodo(todo) {
destroy(todo)
},
}))
export default TodoStore
Thanks a lot!
If you want move the second todo to the first index in the array you could create a new action and splice the second todo out and then unshift it back in:
swapFirstTwoTodos() {
const secondTodo = self.todos.splice(1, 1)[0];
self.todos.unshift(secondTodo);
}

TypeError: Cannot read properties of undefined (reading 'products')

I am trying to get products, then check if the product pId is in an array, and filter if it is.
I get an error when i soft refresh of 'TypeError: Cannot read properties of undefined' (reading 'products'), almost like my 'this.products' isnt populated yet when computed is trying to get the data. Tried adding some if statements to check data is there but no luck.
<script>
export default {
data() {
return {
popular_products: [],
products: [],
}
},
computed: {
bestsellers() {
const keywords = this.popular_products
let array = []
for (var index = 0; index < keywords.length; index++) {
const keyword = this.products.data.products.product.filter(
(product) => product.pId == keywords[index].ProductNumber
)
array = array.concat(keyword)
}
return array
},
},
mounted() {
axios
.get(
'https://myurl/admin/api/collections/get/popularproducts?token=account-9306f9192049d3c442e565f2de5372'
)
.then((response) => (this.popular_products = response.data.entries))
axios
.get('https://myurl/products.json')
.then((response) => (this.products = response))
},
}
</script>
The problem is with this line:
let keyword = this.products.data.products.product.filter(product => product.pId == keywords[index].ProductNumber);
more specific with this read: data.products.
You see, computed property bestsellers is evaluated before your axios calls are finished.
Because of that, Vue can't find products in data because your this.products doesn't have data key.
The best solution would be to change this assignment:
- .then(response => (this.products = response)); // delete this line
+ .then(response => (this.products = response.data.products)); // add this line
Update After comment.
if (this.products.product) {
return this.products.product.filter(...)
} else {
return []
}

How to refresh view when vuex store changes?

I get the data from the store like so
computed: {
notes() {
var data = this.$store.getters.getNotes;
var key = this.$store.getters.getTitleFilter;
if (key === "all") return data;
return data.filter((note) => {
var filteredNote = note.category.some(({ name }) => name === key);
if (filteredNote) return filteredNote;
});
},
},
When the note array changes (an item is removed, getNotes should reflect that. In other instances (where the data is returned without filtering) this used to do the trick:
watch: {
notes(newval) {
return newval;
},
},
I there a way to get the filtered array to update?

Wait until API fully loads before running next function -- async/await -- will this work?

I am a beginner with Javascript with a bit of knowledge of VueJs. I have an array called tickets. I also have a data api returning two different data objects (tickets and user profiles).
The tickets have user ids and the user profiles has the ids with names.
I needed to create a method that looks at both of that data, loops through it, and assigns the full name of the user to the view.
I was having an issue where my tickets object were not finished loading and it was sometimes causing an error like firstname is undefined. So, i thought I'd try and write an async/await approach to wait until the tickets have fully loaded.
Although my code works, it just doesn't "feel right" and I am not sure how reliable it will be once the application gets larger.
Can I get another set of eyes as to confirmation that my current approach is OK? Thanks!
data() {
return {
isBusy: true,
tickets: [],
userProfiles: [],
}
},
created() {
this.getUserProfiles()
this.getTickets()
},
methods: {
getUserProfiles: function() {
ApiService.getUserProfiles().then(response => {
this.userProfiles = response.data
})
},
getTickets() {
ApiService.getTickets().then(response => {
this.tickets = response.data
this.assignNames(this.tickets)
this.isBusy = false
})
},
// lets wait until the issues are loaded before showing names;
async assignNames() {
let tickets = await this.tickets
var i
for (i = 0; i < this.tickets.length; i++) {
if (tickets[i].assigned_to !== null) {
const result = this.userProfiles.filter(profile => {
return profile.uid == tickets[i].assigned_to
})
tickets[i].assigned_to = result[0].firstname + ' ' + result[0].lastname
}
}
}
}
}
</script>
There are several ways you could do this. Here is the one I prefer without async/await:
created() {
this.load();
},
methods: {
getUserProfiles: function() {
return ApiService.getUserProfiles().then(response => {
this.userProfiles = response.data
})
},
getTickets() {
return ApiService.getTickets().then(response => {
this.tickets = response.data
})
},
load() {
Promise.all([
this.getUserProfiles(),
this.getTickets()
]).then(data => {
this.assignNames();
this.isBusy = false;
});
},
assignNames(){
const tickets = this.tickets;
for (let i = 0; i < this.tickets.length; i++) {
if (tickets[i].assigned_to !== null) {
const result = this.userProfiles.filter(profile => {
return profile.uid == tickets[i].assigned_to
})
tickets[i].assigned_to = result[0].firstname + ' ' + result[0].lastname
}
}
}
}