Is there a way to auto render items from the backend? - javascript

I retrieve data from the backend in a method. How can I auto render that data in a component when the app starts?
I tried with mount and beforemount.
Is there any other way?
state() {
return {
FirebaseList: []
}
},
mutation: {
firebaseList(state,items) {
state.FirebaseList.push(items)
},
},
getters: {
getFirebaseList(state) {
return state.FirebaseList
}
}
beforemount() {
return this.dataitems
},
methods: {
dataitems() {
db.collection("Orders")
.get()
.then((querySnapshot) => {
querySnapshot.forEach((doc)=> {
// console.log(doc.id, " => ", doc.data());
var list= doc.data()
this.$store.commit('firebaseList',list)
});
});
},
}
This code is working, but, whenever I re-open the page, it mounts the data again and I see duplicate data on the screen.

I solved it by simply checking that if the list has some items, then don't push the items. Which stopped the list from duplicating items.
if (this.$store.state.FirebaseList.length===0){
// run the fetch code
}
else {
return
}

Related

Cannot read property 'affected_rows' of undefined when trying to run an Hasura mutation

I'm using apollo within my vue.js application, I'm currently trying to remove an object by running a mutation, here is the code :
this.$apollo.mutate({
mutation: require("../graphql/deleteTag.gql"),
variables: {
id: idToDelete,
},
update: (store, { data: { delete_tags } }) => {
if (delete_tags.affected_rows) {
const data = store.readQuery({
query: require("../graphql/fetchDevices.gql"),
});
data.device_id_to_tag_id = data.device_id_to_tag_id.filter((x) => {
return x.id != tag.device_id_to_tag_id.id;
});
store.writeQuery({
query: require("../graphql/fetchDevices.gql"),
data,
});
}
},
});
And my deleteTag.gql file :
mutation delete_tags($id: Int!){
delete_extras_taggeditem(where: { id: { _eq: $id } }) {
affected_rows
}
}
But when I run this the following error appears :
I don't really know what's going on because I followed the Hasura vue.js documentation...
Thanks in advance for your help !
You can specify the name of the returned key in graphql if you want your result data to be called just delete_extras instead of delete_extras_taggeditem:
mutation delete_tags($id: Int!){
delete_extras: delete_extras_taggeditem(where: { id: { _eq: $id } }) {
affected_rows
}
}
but right now, you query do not return you a
I believe you are missing optimisticResponse parameter in mutate. the "update" function takes 2 passes - first with data from optimisticResponse, and then the data from the actual mutation response.
e.g. something like...
this.$apollo.mutate({
mutation: require("../graphql/deleteTag.gql"),
variables: {
id: idToDelete,
},
optimisticResponse: {
delete_extras_taggeditem: {
__typename: 'extras_taggeditem',
id: -1,
affected_rows
}
},
update: (store, { data: { delete_extras_taggeditem } }) => {
if (delete_extras_taggeditem.affected_rows) {
const data = store.readQuery({
query: require("../graphql/fetchDevices.gql"),
});
data.device_id_to_tag_id = data.device_id_to_tag_id.filter((x) => {
return x.id != tag.device_id_to_tag_id.id;
});
store.writeQuery({
query: require("../graphql/fetchDevices.gql"),
data,
});
}
},
});
https://apollo.vuejs.org/guide/apollo/mutations.html#server-side-example
Also, generally speaking I would always return id in your responses back for any level of resource. Apollo relies on __typename + id to maintain and manipulate its cache.

Vuex Getters duplicating array values

I have a simple lesson creator where I allow teachers to make selections for various categories, those selections ids are collected and grouped together and I want to return them all at the end as a lesson plan.
However, I am having a strange problem that I can't figure out. My Vuex store shows the selections correctly, however my getter duplicates all of my arrays.
After selections are made, my Vuex store shows something like this through the Vue.js dev-tools plugin:
lesson_store:Object
lesson:Object
selected_event:1
selected_exploration:Array[1]
0:1
selected_extensions:Array[1]
0:1
selected_goals:Array[1]
0:54
selected_lexis:Array[1]
0:2
store.js state and getter:
const state = {
lesson: {
selected_event: '',
selected_exploration: [],
selected_extensions: [],
selected_goals: [],
selected_lexis: [],
}
};
getSelections(state) {
console.log('GETTER SELECTIONS', state.lesson);
return state.lesson
}
My call to getSelections from lesson.vue file:
<template><button #click="saveLesson">Save</button></template>
methods: {
saveLesson () {
console.log('GET RETURN OF SELECTIONS',this.$store.getters["getSelections"]);
},
}
Now my console output is:
lesson_store:Object
lesson:Object
selected_event:1
selected_exploration:Array[2]
0:1
0:1
selected_extensions:Array[2]
0:1
0:1
selected_goals:Array[2]
0:54
0:54
selected_lexis:Array[2]
0:2
0:2
The thing is, none of my other getters behave this way. This getter is super basic.
When I check out store and getSelections getter in the Vue.js dev-tools the values are correct and there are no duplicates.
Any advice or direction you can provide would be much appreciated.
UPDATE::::::
Actions and Mutations for Lesson_Store
// create mutations
const mutations = {
setSelectedEvent(state, payload) {
// state.lesson.selected_event = payload
if (state.lesson.selected_event === payload) {
state.lesson.selected_event = '';
} else {
state.lesson.selected_event = payload
}
},
setSelectedReading(state, payload) {
if (state.lesson.selected_reading === payload) {
state.lesson.selected_reading = '';
} else {
state.lesson.selected_reading = payload
}
},
setSelectedLexis(state, payload) {
// if event is already in array, then remove it with filter
// otherwise push it to the array
if (state.lesson.selected_lexis.includes(payload)) {
state.lesson.selected_lexis = state.lesson.selected_lexis.filter(function (item) {
return item !== payload;
});
} else {
state.lesson.selected_lexis.push(payload);
}
// state.lesson.selected_lexis = payload
},
setSelectedExplorations(state, payload) {
// if event is already in array, then remove it with filter
// otherwise push it to the array
if (state.lesson.selected_exploration.includes(payload)) {
state.lesson.selected_exploration = state.lesson.selected_exploration.filter(function (item) {
return item !== payload;
});
} else {
state.lesson.selected_exploration.push(payload);
}
// state.lesson.selected_lexis = payload
},
setSelectedQuestions(state, payload) {
// if event is already in array, then remove it with filter
// otherwise push it to the array
if (state.lesson.selected_questions.includes(payload)) {
state.lesson.selected_questions = state.lesson.selected_questions.filter(function (item) {
return item !== payload;
});
} else {
state.lesson.selected_questions.push(payload);
}
// state.lesson.selected_lexis = payload
},
setSelectedPerformances(state, payload) {
// if event is already in array, then remove it with filter
// otherwise push it to the array
if (state.lesson.selected_performances.includes(payload)) {
state.lesson.selected_performances = state.lesson.selected_performances.filter(function (item) {
return item !== payload;
});
} else {
state.lesson.selected_performances.push(payload);
}
},
setSelectedExtensions(state, payload) {
// if event is already in array, then remove it with filter
// otherwise push it to the array
if (state.lesson.selected_extensions.includes(payload)) {
state.lesson.selected_extensions = state.lesson.selected_extensions.filter(function (item) {
return item !== payload;
});
} else {
state.lesson.selected_extensions.push(payload);
}
},
setSelectedGoals(state, payload) {
// if event is already in array, then remove it with filter
// otherwise push it to the array
if (state.lesson.selected_goals.includes(payload)) {
state.lesson.selected_goals = state.lesson.selected_goals.filter(function (item) {
return item !== payload;
});
} else {
state.lesson.selected_goals.push(payload);
}
},
};
// create actions
const actions = {
setSelectedEvent({commit}, payload) {
commit('setSelectedEvent', payload);
},
setSelectedReading({commit}, payload) {
commit('setSelectedReading', payload);
},
setSelectedLexis({commit}, payload) {
commit('setSelectedLexis', payload);
},
setSelectedExplorations({commit}, payload) {
commit('setSelectedExplorations', payload);
},
setSelectedQuestions({commit}, payload) {
commit('setSelectedQuestions', payload);
},
setSelectedPerformances({commit}, payload) {
commit('setSelectedPerformances', payload);
},
setSelectedExtensions({commit}, payload) {
commit('setSelectedExtensions', payload);
},
setSelectedGoals({commit}, payload) {
commit('setSelectedGoals', payload);
},
};
All of these appear to be working correctly because my vuejs dev tools display all of the selection id's properly.
To anyone having similar issues where your dev tools store does not match your actual store values output, it is probably due to your code not updating the store values formally through the actions and mutations approach.
if this store value is ever updated directly without actions and mutations the value in the store will change, however, those updated values will not be detected by vuejs dev tools and your actual store data and dev tools data values will not match.

Vue.js: Data is not reactive and not correctly updated within a method

I have a custom component which receives a list of filters in order to display just the doctors that the user has selected:
<DoctorsSidebarFilter #update-view='showFilteredDoctors'></DoctorsSidebarFilter>
Next, in my main component, I'm using this to display the doctors:
<v-flex
v-for="doctor in allDoctors"
:key="doctor.first_name"
xs12
sm6
md4
>
And here's my data:
export default {
data: () => ({
allDoctors:[],
}),
methods: {
fetchDoctors(){
//Retrieve doctors
this.$store.dispatch(RETRIEVE_DOCTORS)
.then(
response => {
this.allDoctors = response;
}
)//TODO-me: Handle the error properly!
.catch(error => {
console.log(error);
});
},
showFilteredDoctors(filters){
let result = [];
this.fetchDoctors();
console.log('1:' + " " + JSON.stringify(this.allDoctors));
if (filters.length > 0) { // If Array is not empty then apply the filters
console.log('2');
this.allDoctors.forEach(function(e) {
if(filters.some(s => s.specialty === e.specialty || s.city === e.city)) {
result.push(e);
}
});
console.log('3:' + " " + JSON.stringify(result));
this.allDoctors = [...result];
console.log('4:' + " " + JSON.stringify(this.allDoctors));
}
}
},
mounted() {
this.fetchDoctors();
}
}
The problem is that eventhough my filtering works correctly and I can see from console.log('4:' + " " + JSON.stringify(this.allDoctors)); that this.allDoctors contains the new, filtered list; this is never displayed on screen.
Instead I see the default list of doctors that I've fetched from my API. Using vue devtools I can see that the this.allDoctors is momentarily updated with the correct values but then it goes back to the default ones.
As #user1521685 has already explained, the call to fetchDoctors is asynchronous so it'll complete after you've performed the filtering.
Typically you'd do something like this using a computed property instead and only make the server call once.
export default {
data: () => ({
allDoctors: [],
filters: []
}),
computed: {
filteredDoctors() {
const allDoctors = this.allDoctors;
const filters = this.filters;
if (filters.length === 0) {
return allDoctors;
}
return allDoctors.filter(doctor => {
return filters.some(filter => filter.specialty === doctor.specialty || filter.city === doctor.city);
});
}
},
methods: {
fetchDoctors(){
//Retrieve doctors
this.$store.dispatch(RETRIEVE_DOCTORS)
.then(
response => {
this.allDoctors = response;
}
)//TODO-me: Handle the error properly!
.catch(error => {
console.log(error);
});
},
showFilteredDoctors(filters){
this.filters = filters;
}
},
mounted() {
this.fetchDoctors();
}
}
In your template you'd then use:
v-for="doctor in filteredDoctors"
fetchDoctors is async, so in showFilteredDoctors you fetch the doctors, then set the filtered array and then the thenable in fetchDoctors kicks in and overrides the doctors again: this.allDoctors = response.
You'd have to return the Promise in fetchDoctors and use it in showFilteredDoctors like so:
this.fetchDoctors().then(() => /* do the filtering */)
EDIT: Return the Promise like this:
return this.$store.dispatch(RETRIEVE_DOCTORS).then().catch()

Vuejs - vuex computed property, DOM not updating

So I have the following code in one of my components:
export default {
name: 'section-details',
components: {
Loading
},
mounted() {
if (!this.lists.length || !this.section_types.length) {
this.$store.dispatch('section/fetch_section_form_data', () => {
if (this.section) {
this.populate_form();
}
});
}
else if (this.section) {
this.populate_form();
}
},
computed: {
section_types() {
return this.$store.state.section.section_types;
},
lists() {
return this.$store.state.list.lists;
},
loading() {
console.log(this.$store.state.section.loading);
this.$store.state.section.loading;
}
},
.
.
.
}
As you can see I have a computed property for "loading" that retrieves the attribute from my vuex store for when doing an ajax request.
in my section vuex module i have this:
fetch_section_form_data({ commit }, callback) {
commit("isLoading", true);
sectionService
.fetch_form_data()
.then((data) => {
commit("isLoading", false);
commit("fetch_section_types_success", data.section_types);
commit("list/fetch_lists_success", data.lists, { root: true});
if (callback) {
callback();
}
})
.catch((err) => {
commit("isLoading", false);
})
;
}
then in my mutations for the module i have the following code:
mutations: {
isLoading(state, status) {
state.loading = status;
},
}
Finally in my component where I store the loading property I have this:
<Loading v-if="loading"></Loading>
Anyways, for some reason the Loading component isn't showing up. the console.log in the loading() method however, is returning true for this.$store.state.section.loading. So for some reason Vue isn't picking up that loading == true in the actual DOM. Any help would be appreciated.
You need to return the value from the computed property method:
loading() {
return this.$store.state.section.loading;
}

How to update values in table with this.state?

I make a component, which show information from database in table. But this information with filters.
Filtering can be by event type and by participant (id: integer type).
When I click the button, I call handleShowClick(). In this function I check: if value of type event isn't null, I get from database events with this type. if value of type event is null, I get all events.
After this I check a participant value. If value isn't null, I call function, which search which events are include this participant. Data from this.state.event show in table in another component.
I haven't problems with event type. But I have problem with participant. When I choose one of participant, table shows correct data for a split second. After this return to prev state (without filter by participants).
How can I fix this issue? I set state to event only in this component
class TestPage extends Component {
constructor(props) {
super(props);
this.state = {
event: [],
searchByType: null,
searchByParticipant: null,
participantToEvent: []
};
this.handleShowClick = this.handleShowClick.bind(this);
this.onHandleEventByTypeFetch = this.onHandleEventByTypeFetch.bind(this);
this.handleParticipantSearch = this.handleParticipantSearch.bind(this);
this.onHandleEventFetch = this.onHandleEventFetch.bind(this);
}
handleShowClick() { // onClick
if (this.state.searchByType !== null) {
this.onHandleEventByTypeFetch(); // select * from ... where type=...
} else {
this.onHandleEventFetch(); // select * from ...
}
if (this.state.searchByParticipant !== null) {
this.handleParticipantSearch();
}
}
handleParticipantSearch() {
const list = [];
this.state.participantToEvent.map(itemP => { // participantToEvent is binding table
if (itemP.parid === this.state.searchByParticipant) {
this.state.event.map(itemEvent => {
if (itemEvent.id === itemP.eventid) {
list.push(itemEvent);
}
});
}
});
console.log(list); // here I see array with correct result
this.setState({ event: list });
}
onHandleEventFetch() {
fetch( ... , {
method: 'GET'
})
.then((response) => {
if (response.status >= 400) {
throw new Error('Bad response from server');
}
return response.json();
})
.then(data => {
if (data.length === 0) {
alert('nothing');
} else {
this.setState({
event: data
});
}
});
}
onHandleEventByTypeFetch() {
fetch( ... , {
method: 'GET'
})
.then((response) => {
if (response.status >= 400) {
throw new Error('Bad response from server');
}
return response.json();
})
.then(data => {
if (data.length === 0) {
alert('nothing');
} else {
this.setState({
event: data
});
}
});
...
}
}
Structure of this.state.event:
[{id: 1, name: 'New event', participant: 5, type: 10}, ...]
Structure of this.state.participantToEvent:
[{id: 1, idparticipant: 5, idevent: 1}, ...]
this.setState(...this.state,{ event: list });
I think this would solve your problem. Because you clear every item except for {event:list} by not copying the previous state.
Edit:
You should put
...this.state
to onHandleEventByeTypeFetch and onHandleEventFetch. Without them when you click handleShowClick one of those two functions always work and clears searchByParticipant data from the state by not copying the previous state.
The reason for you see the correct data for a short time is all about async nature of the state.

Categories