vuejs props are undefined after refresh - javascript

App.vue
template:
<ResponsiveNavigation
:nav-links="navLinks"
/>
script
data: () => ({
navLinks: []
}),
created: function() {
this.getSocialNetworks();
},
methods: {
getSocialNetworks() {
var self = this;
axios
.get(MY_API_URL)
.then(function(res) {
var fb_url = res.data.data.filter(obj => {
return obj.key === "Social_Facebook";
});
self.navLinks.fb = fb_url[0].defaultValue;
//
var ig_url = res.data.data.filter(obj => {
return obj.key === "Social_Instagram";
});
self.navLinks.ig = ig_url[0].defaultValue;
//
})
.catch(function(error) {
console.log("Error", error);
});
}
}
ResponsiveNavigation.vue:
<a :href="$props.navLinks.fb"></a>
if I console.log the $props.navLinks I have everything stored.
however in the href doesn't work after the FIRST load.

I am fairly sure that this is due to the reactive nature and UNreactive of arrays.
You're not really using an array, but an object
data: () => ({
navLinks: []
}),
to
data: () => ({
navLinks: {
fb:'',
ig:''}
}),
and I think it would setup the reactive props more suitably.
If you need an array, then use array.push() so it can react accordingly. I may also consider moving it to the mounted() method. Finally, you put $props in your code, do you have other props you've not shown us which may be conflicting?

Related

Edit function not saving changes to state data in React

I am trying to provide functionality in my webpage for editing state data.
Here is the state structure
state = {
eventList:[
{
name: "Coachella"
list: [
{
id: 1,
name: "Eminem"
type: "rap"
}
{
id: 2,
name: "Kendrick Lamar"
type: "rap"
}
]
}
]
}
I want to be able to edit the list arrays specifically the id, name, and type properties but my function doesn't seem to edit them? I currently pass data I want to override id name and type with in variable eventData and an id value specifying which row is selected in the table which outputs the state data.
Here is the function code:
editPickEvent = (eventData, id) => {
const eventListNew = this.state.eventList;
eventListNew.map((event) => {
event.list.map((single) => {
if (single.id == id) {
single = eventData;
}
});
});
this.setState({
eventList: eventListNew,
});
};
When I run the code the function doesn't alter the single map variable and I can't seem to pinpoint the reason why. Any help would be great
edit:
Implementing Captain Mhmdrz_A's solution
editPickEvent = (eventData, id) => {
const eventListNew = this.state.eventList.map((event) => {
event.list.map((single) => {
if (single.id == id) {
single = eventData;
}
});
});
this.setState({
eventList: eventListNew,
});
};
I get a new error saying Cannot read property list of undefined in another file that uses the map function to render the state data to the table?
This is the part of the other file causing the error:
render() {
const EventsList = this.props.eventList.map((event) => {
return event.list.map((single) => {
return (
map() return a new array every time, but you are not assigning it to anything;
editPickEvent = (eventData, id) => {
const eventListNew = this.state.eventList.map((event) => {
event.list.forEach((single) => {
if (single.id == id) {
single = eventData;
}
});
return event
});
this.setState({
eventList: eventListNew,
});
};
const editPickEvent = (eventData, id) => {
const updatedEventList = this.state.eventList.map(event => {
const updatedList = event.list.map(single => {
if (single.id === id) {
return eventData;
}
return single;
});
return {...event, list: updatedList};
});
this.setState({
eventList: updatedEventList,
});
}
Example Link: https://codesandbox.io/s/crazy-lake-2q6ez
Note: You may need to add more checks in between for handling cases when values could be null or undefined.
Also, it would be good if you can add something similar to the original data source or an example link.
Turns out primitive values are pass by value in javascript, which I didn't know and why the assignment wasn't working in some of the previous suggested answers. Here is the code that got it working for me:
editEvent = (EventData, id) => {
const eventListNew = this.state.eventList.map((event) => {
const newList = event.list.map((single) => {
return single.id == id ? EventData : single;
});
return { ...event, list: newList };
});
this.setState({
eventList: eventListNew,
});
};

multiple this.setState(this.state) not re-rendering page

In my render, I have a function that updates the properties. I have listed the functions that get called between, but I think only the last one matters since it is the one that updates the data I use.
<button
onClick={() =>
this.handleUpdateProperty()
}>
Update Properties
</button>
which calls:
handleUpdateProperty = () => {
this.getDataBC();
this.setState(this.state);
//db.inventory.find( { status: "D" } )
}
That in turns calls:
getDataBC = () => {
var rentals = scFunctions.getRents();
console.log(web3.toAscii(rentals[1][0]));
for(let i = 0; i < rentals[0].length; i++){
let currentProp = {
status: rentals[0][i].toNumber(),
location: web3.toUtf8(rentals[1][i]).replace(/\s+/g,''),
company: web3.toUtf8(rentals[2][i]).replace(/\s+/g,''),
price: rentals[3][i].toNumber(),
start: rentals[4][i].toNumber(),
end: rentals[5][i].toNumber(),
help: "haha"
}
console.log(currentProp)
this.updateDB(currentProp);
}
this.getDataFromDb();
this.setState(this.state);
};
That in turn calls:
getDataFromDb = () => {
fetch("http://localhost:3001/api/property")
.then(property => property.json())
.then(res => this.setState({ data: res.data }))
.then(this.setState(this.state))
};
The last function does the:
`.then(res => this.setState({ data: res.data }))`
which updates the data I use to render my page. However, it doesn't update the page right away, I have to refresh the page to see the results from pressing the button. I thought
.then(res => this.setState({ data: res.data }))
would rerender the page?
Thank you so much
edit:
The constructor is as follows:
constructor(props) {
super(props);
this.state = {
data: [],
show: false, // show of the rental modal
company: "Homeaway",
id: 0,
message: null,
intervalIsSet: false,
idToDelete: null,
idToUpdate: null,
objectToUpdate: null,
rentProperty: "DongFang",
startDate: new Date(),
endDate: new Date(),
firstName: "Ludwig",
showConflict: true,
lastName: "Wittgenstein"
};
this.handleCompanySubmit = this.handleCompanySubmit.bind(this);
}
This is what uses the "data" from state. So I want this function to rerun and update the page when I setState...:
renderProperties = data => {
var properties = []
var propRow = []
data.forEach((property,index) => {
propRow.push(<Col xs={{ size:3, offset: .5}}>
<Jumbotron>
<Image src={require("./images/1.jpg")} fluid rounded />
<b> {property.location} </b>
<h1> Price: {property.price} </h1>
<div>
{this.renderStatusButton(property)}
</div>
</Jumbotron>
</Col>)
if((index+1)%3 == 0){ // if first in the row
properties.push(<Row>{ propRow }</Row>)
propRow = []
}
})
return (
<Container>
{properties}
</Container>
)
}
And this is in the render:
{this.renderProperties(data)}
I am going to bed. Thank you all for your help so far. If it doesn't get fixed, it is fine. It is not pivotal.
If I'm correct, you just want to refresh the page once the fetch in getDataFromDb() has finished, is that correct?
If so, you don't need all those setState() calls, you just need one in getDataFromDb(), which should be written as follow:
getDataFromDb = () => {
fetch("http://localhost:3001/api/property")
.then(property => property.json())
.then(res => this.setState({ data: res.data }))
};
That is, you don't need the last setState() call you wrote neither.
Anyways, in getDataBC() I see two functions (getRent() and updateDB) that I don't know what they do, so maybe there are some problems in those functions too.

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!

Vue.js: mutation for deleting a comment

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 }

$emit an event from child to parent component Vue 2

I am new to JS and Vue, so please bear with me :)
I have a table that is rendered using two Vue components which are a parent (the table - orders) and child (the row - order).
There is a button that can be pressed on each row of the table that carries out an AJAX against that row, but I also need to have the table (parent) refresh when the action is carried out so it has the updated data.
I think I need to use $emit in the child to pass the action on to the parent, but I can't get it to work. Here is the code (sorry its long, I removed everything non-essential);
const order = {
template: `
...// table content
<td><button class="btn btn-default btn-sm" #click="assignAdvisor(id,
selectedOption)">Set Advisor</button></td>
`,
methods: {
// following is the method that is run when the button is pressed
assignAdvisor(id, selectedOption) {
axios.post('url').then(response => {
..// show response message
orders.$emit('refreshAfterUpdate'); // also tried
// this.$parent.$emit(...)
})
},
};
const orders = {
components: { order, },
props: {
orders: {
type: Object,
},
},
mounted() {
// this is basically the code that I need to re-run when button is pressed,
// which I have repeated below in a method
var refresh = () => {
axios.get('/admin/ajax/unassigned-orders')
.then(response => {
this.ordersData = response.data;
setTimeout(refresh, 5000);
});
}
refresh();
},
methods: {
refreshAfterUpdate() {
axios.get('/admin/ajax/unassigned-orders')
.then(response => {
this.ordersData = response.data;
console.log(response);
});
},
}
};
new Vue({
render(createElement) {
const props = {
orders: {
type: Object,
},
};
return createElement(orders, { props });
},
}).$mount('#unassignedOrders');
I don't get any error message or anything - it just doesn't work.
Thanks
OK so thanks to #Patrick Steele I have figured it out.
I was not using $on - oops.
Added code to the mounted() section and it now works:
const order = {
template: `
...// table content
<td><button class="btn btn-default btn-sm" #click="assignAdvisor(id,
selectedOption)">Set Advisor</button></td>
`,
methods: {
// following is the method that is run when the button is pressed
assignAdvisor(id, selectedOption) {
axios.post('url').then(response => {
..// show response message
orders.$emit('refreshAfterUpdate'); // also tried
// this.$parent.$emit(...)
})
},
};
const orders = {
components: { order, },
props: {
orders: {
type: Object,
},
},
mounted() {
// this is basically the code that I need to re-run when button is pressed,
// which I have repeated below in a method
var refresh = () => {
axios.get('/admin/ajax/unassigned-orders')
.then(response => {
this.ordersData = response.data;
setTimeout(refresh, 5000);
});
}
refresh();
$this.on('refreshAfterUpdate', () => {
axios.get('/admin/ajax/unassigned-orders')
.then(response => {
this.ordersData = response.data;
console.log(response);
});
},
},
},
};
new Vue({
render(createElement) {
const props = {
orders: {
type: Object,
},
};
return createElement(orders, { props });
},
}).$mount('#unassignedOrders');

Categories

Resources