Vue.js: mutation for deleting a comment - javascript

I have been working on the feature of comment deleting and came across a question regarding a mutation for an action.
Here is my client:
delete_post_comment({post_id, comment_id} = {}) {
// DELETE /api/posts/:post_id/comments/:id
return this._delete_request({
path: document.apiBasicUrl + '/posts/' + post_id + '/comments/' + comment_id,
});
}
Here is my store:
import Client from '../client/client';
import ClientAlert from '../client/client_alert';
import S_Helper from '../helpers/store_helper';
const state = {
comment: {
id: 0,
body: '',
deleted: false,
},
comments: [],
};
const actions = {
deletePostComment({ params }) {
// DELETE /api/posts/:post_id/comments/:id
document.client
.delete_post_comment({ params })
.then(ca => {
S_Helper.cmt_data(ca, 'delete_comment', this);
})
.catch(error => {
ClientAlert.std_fail_with_err(error);
});
},
};
delete_comment(context, id) {
context.comment = comment.map(comment => {
if (!!comment.id && comment.id === id) {
comment.deleted = true;
comment.body = '';
}
});
},
};
export default {
state,
actions,
mutations,
getters,
};
I am not quite sure if I wrote my mutation correctly. So far, when I am calling the action via on-click inside the component, nothing is happening.

Guessing you are using vuex the flow should be:
according to this flow, on the component template
#click="buttonAction(someParams)"
vm instance, methods object:
buttonAction(someParams) {
this.$store.dispatch('triggerActionMethod', { 'something_else': someParams })
}
vuex actions - Use actions for the logic, ajax call ecc.
triggerActionMethod: ({commit}, params) => {
commit('SOME_TRANSATION_NAME', params)
}
vuex mutations - Use mutation to make the changes into your state
'SOME_TRANSATION_NAME' (state, data) { state.SOME_ARG = data }

Related

Vuex getters has undefined data when I try to load data from API and use it in multiple components

I have a page component where I am making api call and storing the data in Vuex store through actions. This data has to be used at multiple places but everywhere I'm initially getting undefined data which loads after a few seconds asynchronously from the API. How should I use vuex getters asynchronously ?
Here's the code for my vuex store module :
import axios from 'axios';
const state = {
all_pokemon: {},
pokemon_details: {}
};
const getters = {
getAllPokemon: function(state) {
return state.all_pokemon;
},
getPokemonDetails: function(state) {
return state.pokemon_details;
}
};
const mutations = {
setAllPokemon: function(state, payload) {
return state.all_pokemon = payload;
},
setPokemon: function(state, payload) {
console.log('Pokemon details set with payload ', payload);
return state.pokemon_details = payload;
}
};
const actions = {
setPokemonAction: function({ commit }, passed_pokemon) {
axios.get('https://pokeapi.co/api/v2/pokemon/' + passed_pokemon)
.then((response) => {
console.log('Response data is : ', response.data);
});
commit('setAllPokemon', response.data);
},
setPokemonDetailAction: function({ commit }, passed_pokemon) {
console.log('Action method called..', passed_pokemon);
axios.get('https://pokeapi.co/api/v2/pokemon/' + passed_pokemon)
.then((response) => {
commit('setPokemon', response.data);
});
}
};
export default {
state,
getters,
mutations,
actions,
};
And code for the component where I want to get this data and pass it to other components :
<script>
import { mapGetters, mapActions } from 'vuex'
import axios from 'axios'
// Imported other components here
export default {
name: 'pokemon_detail_page',
data() {
return {
current_pokemon: this.$route.params.pokemon,
isLoading: false,
childDataLoaded: false,
search_pokemon: '',
sprites: [],
childData: 'False',
isAdded: false,
pokemon_added: 'none_display',
show: false
}
},
methods: {
...mapActions([
'setPokemonDetailAction',
'removePokemon'
]),
},
computed: {
...mapGetters([
'getPokemonDetails',
'getTeam'
])
},
components: {
Game_index,
PulseLoader,
PokemonComponent,
},
filters: {
},
watch: {
getTeam: function (val) {
},
getPokemonDetails: function(val) {
}
},
created() {
setTimeout(() => {
this.show = true;
}, 2000);
this.$store.dispatch('setPokemonDetailAction', this.current_pokemon)
.then(() => {
// const { abilities, name, order, species, } = {...this.getPokemonDetails};
})
},
mounted() {
},
}
</script>
And here's is the code for the template where I'm passing this data to multiple components :
<div v-if="show" class="pokemon_stats_container" :key="childData">
<ability-component
:abilities="getPokemonDetails.abilities"
>
</ability-component>
<sprites-component
:sprites="sprites"
>
</sprites-component>
<location-component
:location_area="getPokemonDetails.location_area_encounters"
:id="getPokemonDetails.id"
>
</location-component>
<stats-component
:stats="getPokemonDetails.stats"
>
</stats-component>
<game_index
:game_indices="getPokemonDetails.game_indices"
/>
<moves-component
:moves="getPokemonDetails.moves"
:pokemon_name="getPokemonDetails.name"
>
</moves-component>
</div>
As of now, I've adopted a roundabout way of doing this through setTimeout and setting a variable after 2 seconds so that data is available for other components to use. But, there has to be a more elegant way of handling vuex asynchronous data. Someone please help me in this.
Your first commit is not in the promise
commit('setAllPokemon', response.data);
make this :
axios.get('https://pokeapi.co/api/v2/pokemon/' + passed_pokemon)
.then((response) => {
console.log('Response data is : ', response.data);
commit('setAllPokemon', response.data);
});
try to use in your vue component
$forceUpdate()
when your request is end for reload the data

How to prevent Vue from loading the data multiple times?

I'm currently practicing Vue with Vuex and the REST API architecture (with Django). I wanna retrieve the data from by database and store the data in my global state in order to access it throughout my Vue components. It works fine so far but there is one weird thing I do not understand.
When I start my application I have the homepage where currently nothing is displayed. I click on the menu item "Contacts" and the contacts component loads (router-view) (API GET call is executed) and displays the table of all the created contacts and shows it properly (source of truth is now my global state). I can edit, delete and view a contact.The contacts are now stored in the global state as well.
The problem: Everytime I load the contact component the mounted() lifecycle gets called (which makes sense) and loads the contacts from the API inside the state so the whole lists gets dupblicated over and over. I just want Vue to make a GET request only once and then access the state's data (where to contacts are stored now).
Another scenario is when I update a contact and click back to the contacts menu item the list contains the old contact and the updated one but when I refresh the page it is fine.
Thanks!
MY CODE
state.js
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
import { apiService } from "../utils/api.service.js";
export const store = new Vuex.Store({
state: {
contacts: []
},
mutations: {
initContacts_MU: (state, data) => {
state.contacts.push(...data);
},
updateContact_MU: (state, data) => {
let getContact = state.contacts.filter(contact => contact.id === data.id);
let getContactIndex = state.contacts.indexOf(getContact);
state.contacts.splice(getContactIndex, 1, data);
},
deleteContact_MU: (state, data) => {
let getContact = state.contacts.filter(contact => contact.id === data.id);
let getContactIndex = state.contacts.indexOf(getContact);
state.contacts.splice(getContactIndex, 1);
},
createContact_MU: (state, data) => {
state.contacts.unshift(data);
}
},
actions: {
initContacts_AC({ commit }) {
let endpoint = "api/contacts/";
apiService(endpoint).then(data => {
commit("initContacts_MU", data);
});
},
updateContact_AC({ commit }, contact) {
let endpoint = "/api/contacts/" + contact.slug + "/";
apiService(endpoint, "PUT", contact).then(contact => {
commit("updateContact_MU", contact);
});
},
deleteContact_AC({ commit }, contact) {
let endpoint = "/api/contacts/" + contact.slug + "/";
apiService(endpoint, "DELETE", contact).then(contact => {
commit("deleteContact_MU", contact);
});
},
createContact_AC({ commit }, contact) {
let endpoint = "/api/contacts/";
apiService(endpoint, "POST", contact).then(contact => {
commit("createContact_MU", contact);
});
}
},
getters: {
contacts: state => {
return state.contacts;
}
}
});
ContactList.vue
<script>
import Contact from "../../components/Contacts/Contact.vue";
import { mapGetters, mapActions } from "vuex";
export default {
name: "ContactList",
components: {
Contact
},
computed: {
...mapGetters(["contacts"])
},
methods: {
...mapActions(["initContacts_AC"])
},
mounted() {
this.initContacts_AC();
}
};
</script>
Just check if there're contacts which are already retrieved from backend.
computed: {
...mapGetters(["contacts"])
},
methods: {
...mapActions(["initContacts_AC"])
},
mounted() {
if (this.contacts && this.contacts.length > 0) return; // already fetched.
this.initContacts_AC();
}
EDIT:
updateContact_MU: (state, data) => {
const contactIndex = state.contacts.findIndex(contact => contact.id === data.id);
if (contactIndex < 0) return;
state.contacts.splice(contactIndex, 1, data);
}

React Redux store changes sometimes rerender and sometimes dont

So i am using the fetch API with react / redux to perform API calls to Blockv in my actions and store a returned array of objects in my redux store using a reducer. My array of objects only contains one level of objects (i.e. {id: 9798234982739847, name: 220398402343, etc..}). My table that SHOULD rerender everytime the array is replaced - and I use replace as i want to completely replace the array in the store with the new - only rerenders sometimes and i can see the changes reflected in the store.
I am using thunk middleware. Here is what im working with:
store.js
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import { createLogger } from 'redux-logger'
import urbanArcadeReducer from '../reducers/reducers'
import { loadTokens } from '../localStorage'
import { checkAuth } from '../actions/check-auth'
const persistedTokens = loadTokens();
const loggerMiddleware = createLogger();
// Store for Urban Arcade Application
const store = createStore(
urbanArcadeReducer,
persistedTokens,
applyMiddleware(
thunk, // lets us dispatch() functions
loggerMiddleware // neat middleware that logs actions
)
)
// Check for automatic login on page load
store.dispatch(checkAuth(store.getState().access_token))
export default store
reducers.js
function atoms(state = {
receivedAt: null,
atoms: []
}, action) {
switch (action.type) {
case UPDATE_CURR_ATOMS:
var mappedAtoms = action.atoms.map(atom => {
atom = {
id: atom.id,
gameTitle: atom.private.GameTitle,
highscore: atom.private.HighScore,
highscoreOwner: atom.private.HighScoreOwner,
locationName: atom.private.LocationName,
scoreHistory: atom.private.ScoreHistory,
unpublished: atom.unpublished,
author: atom['vAtom::vAtomType'].author,
description: atom['vAtom::vAtomType'].description,
dropped: atom['vAtom::vAtomType'].dropped,
lat: atom['vAtom::vAtomType'].geo_pos.coordinates[1],
lng: atom['vAtom::vAtomType'].geo_pos.coordinates[0],
owner: atom['vAtom::vAtomType'].owner,
template: atom['vAtom::vAtomType'].template,
template_variation: atom['vAtom::vAtomType'].template_variation,
title: atom['vAtom::vAtomType'].title,
when_created: atom.when_created,
when_modified: atom.when_modified
}
return atom
})
return {
...state,
receivedAt: action.receivedAt,
atoms: mappedAtoms
}
default:
return state
}
}
actions.js
import {
fetchRequest,
fetchFailure,
fetchSuccess,
updateNotifier,
updateCurrAtoms } from './action-creators'
import { bringToLogin } from './bring-to-login'
export const UPDATE_CURR_ATOMS = 'UPDATE_CURR_ATOMS'
export function updateCurrAtoms(atoms) {
return { type: UPDATE_CURR_ATOMS, atoms: atoms.atoms, receivedAt: atoms.receivedAt }
}
/**
* Submits request to get all arcade games using Blockv Discover Endpoint(vAtoms)
*
* #returns list of arcade cabinets (vAtoms)
*/
export function getAtoms(params) {
var access_token = params.access_token
var from_refresh = params.from_refresh
var responseCode = ''
var method = 'POST'
var url = 'https://api.blockv.io/v1/vatom/discover'
var headers = {
'Content-Type': 'application/json',
'App-Id': '<App ID>',
'Authorization': 'Bearer ' + access_token
}
var requestBody = {
"scope": {
"key": "vAtom::vAtomType.template",
"value": "<publisher_fqdn>"
},
"filters": [
{
"filter_elems": [
{
"field": "vAtom::vAtomType.template",
"filter_op": "Match",
"value": "<publisher_fqdn>"
}
]
}
],
"return": {
"type": "*",
}
}
var requestBodyJSON = JSON.stringify(requestBody)
// Thunk middleware knows how to handle functions.
// It passes the dispatch method as an argument to the function,
// thus making it able to dispatch actions itself.
return function(dispatch) {
// First dispatch: the app state is updated to inform
// that the API call is starting.
dispatch(fetchRequest())
console.log('Sending get atoms request to Blockv...');
fetch(url, {
method: method,
body: requestBodyJSON,
headers: headers
}).then(response => {
responseCode = response.status
console.log(responseCode)
return response.json()
}).then(data => {
if (responseCode === 401) {
dispatch(bringToLogin("We've logged you out. Please reauthenticate"))
dispatch(fetchFailure('Failed to get atoms'))
} else if (responseCode === 200) {
var atoms = data.payload.results
dispatch(fetchSuccess('Retrieved atoms!')) // Array of template variations
if (from_refresh) {
dispatch(updateNotifier({
isOpen: true,
message: 'Successfully retrieved games!'
}))
}
dispatch(updateCurrAtoms({
atoms: atoms,
receivedAt: Date.now()
}))
}
}).catch(err => {
console.log(err)
dispatch(fetchFailure('Failed to get atoms'))
});
}
}
MyComponent.js
...
class GameStatsModal extends Component {
getDataAtoms = () => {
return this.props.atoms
}
/**
* Props for each row in table
*
* #memberof GameStatsModal
*/
setTrProps = (state, rowInfo, column, instance) => {
return {
style: {
marginBottom: 15
}
}
}
render () {
return (
<OuterContainer>
<StatsContainer>
<InnerContainer>
<img
src={RefreshIcon}
alt="Refresh List"
id="refreshGames"
className='refreshButton'
onClick={
() => {
store.dispatch(getAtoms({
access_token: store.getState().access_token,
from_refresh: true
}))
}
}
/>
<ReactTable
data={this.getDataAtoms()}
className='-highlight -striped gamesTable'
noDataText="Click Refresh to Load Games"
columns={columnsAtoms}
defaultSorted={[{id: 'created', desc: true}]}
getTrProps={this.setTrProps}
getTdProps={this.setTdProps}
pageSize={this.getDataAtoms().length}
showPagination={false}
resizable={false}
/>
</InnerContainer>
</StatsContainer>
</OuterContainer>
)
}
}
const mapStateToProps = state => ({
atoms: state.atoms.atoms
})
export default withRouter(connect(mapStateToProps)(GameStatsModal))
Again i know the updates are being made to store, so my question is am i somehow mutating the previous state somewhere? If not is it possible that since i have multiple dispatch calls being executed in actions, could they be interfering with each other and/or rerendering? not sure where else to look.
Looking forward to any suggestions, thank you!
i think you are mutating the state directly
try this one
switch (action.type) {
case UPDATE_CURR_ATOMS:
var mappedAtoms = action.atoms.map(atom => {
// directly returning the new object
return {
id: atom.id,
gameTitle: atom.private.GameTitle,
highscore: atom.private.HighScore,
highscoreOwner: atom.private.HighScoreOwner,
locationName: atom.private.LocationName,
scoreHistory: atom.private.ScoreHistory,
unpublished: atom.unpublished,
author: atom['vAtom::vAtomType'].author,
description: atom['vAtom::vAtomType'].description,
dropped: atom['vAtom::vAtomType'].dropped,
lat: atom['vAtom::vAtomType'].geo_pos.coordinates[1],
lng: atom['vAtom::vAtomType'].geo_pos.coordinates[0],
owner: atom['vAtom::vAtomType'].owner,
template: atom['vAtom::vAtomType'].template,
template_variation: atom['vAtom::vAtomType'].template_variation,
title: atom['vAtom::vAtomType'].title,
when_created: atom.when_created,
when_modified: atom.when_modified
}
})
return {
...state,
receivedAt: action.receivedAt,
atoms: mappedAtoms
}

Conditional get request in vue for rendering a subcomponent scoped

When I click a profile (of an author) component, I can't figure out how it should render a scoped sub-component, listing the main entities of the app, so-called fabmoments (containers for 3D print information).
My current solution looks like this:
export default {
name: 'Multipe',
props: [
'author'
],
data () {
return {
// search: '',
localAuthor: '',
fabmoments: []
}
},
created () {
this.localAuthor = this.author
if (typeof localAuthor !== 'undefined') {
this.$http.get(`/users/${this.$route.params.id}/fabmoments`)
.then(request => this.buildFabmomentList(request.data))
.catch(() => { alert('Couldn\'t fetch faboments!') })
} else {
this.$http.get('/fabmoments')
.then(request => this.buildFabmomentList(request.data))
.catch(() => { alert('Couldn\'t fetch faboments!') })
}
},
methods: {
buildFabmomentList (data) {
this.fabmoments = data
}
},
components: {
// Box
}
}
This renders all in the profile, where it should render a list scoped to the current profile's author.
And it renders nothing in the home (without receiving the prop), where it should render all.
I am not much of star in JavaScript. What am I doing wrong?
UPDATE
This works as a solution, though not very elegant.
export default {
name: 'Multipe',
props: [
'author'
],
data () {
return {
fabmoments: []
}
},
created () {
if (this.author.id >= 0) {
this.$http.get(`/users/${this.$route.params.id}/fabmoments`)
.then(request => this.buildFabmomentList(request.data))
.catch(() => { alert('Couldn\'t fetch faboments!') })
} else {
this.$http.get('/fabmoments')
.then(request => this.buildFabmomentList(request.data))
.catch(() => { alert('Couldn\'t fetch faboments!') })
}
},
methods: {
buildFabmomentList (data) {
this.fabmoments = data
}
},
components: {
// Box
}
}
Not sure which part is wrong, but you may definitely debug your code to find out why fabmoments is empty array assuming there is no error occurred yet.
There are three parts to debug:
http response -- to check if data is properly returned
this -- to check if this pointer still points at the component
template -- to check if fabmoments are correctly bind to the element
At last, it would be better to separate your http request logics from your components.
Good luck!

Vuex how to pass state/getter in data prop

I retrieved my data from database using axios under my vuex
const state = {
giveaways: null
}
const actions = {
getGiveAways : ({commit}) =>{
axios({
url : '/prod/api/thresholds_settings',
method: 'post',
data : {
},
config: 'JOSN'
})
.then(response=>{
if(response.status == 200){
//console.log(response.data.total_giveaways);
commit('SET_GIVEAWAYS', response.data.total_giveaways)
}
})
.catch(error=>{
if(error.response){
console.log('something happened')
}
});
}
}
const mutations = {
SET_GIVEAWAYS : (state, obj)=>{
state.giveaways = obj
}
}
const getters = {
doneGiveAways(state){
return state.giveaways
}
}
In my Dashboard.vue I have
import {mapState,mapGetters} from 'vuex'
export default{
data: () => ({
cards: [],
giveaways: ''
}),
computed:{
...mapState({
Giveaway: state => state.Threshold.giveaways
}),
doneGiveAways(){
return this.$store.getters.doneGiveAways
}
},
ready(){
//giveaways: this.Giveaways
//console.log(this.Giveaways);
},
created(){
const self = this
this.cards[0].result_val = 2
this.cards[2].result_val = 2;
},
mounted(){
this.$store.dispatch('getGiveAways');
console.log(this.giveaways);
}
}
My problem is I need to pass the value from the mapState Giveaway to my returning data giveaways: '' so when page fires I can get the response value using this.giveaways. If I just call {{ Giveaway }} in my html it shows the value. But I need to make something like this.giveaways = this.$store.state.Thresholds.giveaways
I would use Stephan-v's recommendation and delete the local copy of giveaways. But I don't know what your specific reason is for declaring an extra copy of giveaways, so here is a solution that will work:
In your Dashboard.vue add:
export default {
...
watch: {
Giveaway(value) {
this.giveaways = value
}
},
...
}
Just delete the giveaways property from your data Object and rename the computed doneGiveAways to giveaways and you are done.
There is no need for a local component giveaway data property in this scenario.

Categories

Resources