Vue.js updating data from created functions in a component - javascript

I cant update the data properties from created() functions. I tried using 'this' too but i just seem out of scope. Any help?
Anyways a sibling component is emitting info on click, which this component should recieve and display as data, very simple, but i when i try to update the main properties of data, they just always stay the same. Im new to vue2 so any help would be appreciated.
const singleAc = Vue.component('singleAc', {
template: `<div class="helper_text">
<div> Aircraft with ID : {{ $route.params.aircraftId }} </div>
<div><img class="airline_logo" src="//logo.clearbit.com/Ryanair.com"></div>
<div> Model : {{modelName}} </div>
<div v-if="fromAp"> From: {{fromAp}} </div>
<div v-if="toAp"> To: {{toAp}} </div>
</div>`,
data: function() {
return {
company: null,
modelName: null,
fromAp: null,
toAp: null
}
},
created() {
bus.$on('op', function(op) {
singleAc.company = op;
console.log(op)
})
bus.$on('model', function(model) {
singleAc.modelName = model;
console.log(model)
})
bus.$on('from', function(from) {
singleAc.fromAp = from;
console.log(from)
})
bus.$on('to', function(to) {
singleAc.toAp = to;
console.log(to)
})
}
});

singleAc is a Vue component and not a Vue instance. That's why changing data like singleAc.company won't work
You still gotta use this
Solution 1: use arrow functions so that this can be used
const singleAc = Vue.component("singleAc", {
created() {
bus.$on("op", op => {
this.company = op;
console.log(op);
});
}
});
Solution 2: store this in a variable
const singleAc = Vue.component("singleAc", {
created() {
var _t = this;
bus.$on("op", op => {
_t.company = op;
console.log(op);
});
}
});
Hope this helps.

binding this actually solved the problem
bus.$on('to', function(to) {
this.toAp = to;
}.bind(this))

Forget about global events for now, try passing your aircraft's data with props
then your component should access aircraft data by adding:
props: ['aircraft']
Don't forget to point to the aircraft data model. It should look somewhere like this:
`<div :aircraft="aircraft" class="helper_text">
<div> Aircraft with ID : {{ aircraft.id }} </div>
<div><img class="airline_logo" src="//logo.clearbit.com/Ryanair.com"></div>
<div> Model : {{aircraft.modelName}} </div>
<div v-if="fromAp"> From: {{fromAp}} </div>
<div v-if="toAp"> To: {{toAp}} </div>
</div>`
Hope it helps.

Related

Nuxtjs pages are not updated even if the vuex store is updated

Here my code :
~/store/state.js
export default () => ({
selectLanguage: 'fr'
})
~/store/actions.js
export default {
switchToFr (context) {
context.commit('switchToFr')
},
switchToEn (context) {
context.commit('switchToEn')
}
}
~/store/mutations.js
export default {
switchToFr (state) {
state.selectLanguage = 'fr'
},
switchToEn (state) {
state.selectLanguage = 'en'
}
}
~/layouts/inside.js
<b-dropdown-item :value="'fr'" #click="$store.dispatch('switchToFr')" aria-role="listitem">
<div class="media">
<img width='30px' height='30px' src="~/assets/img/icons8-france-48.png"/>
<div class="media-content">
<h3>Français</h3>
</div>
</div>
</b-dropdown-item>
<b-dropdown-item :value="'en'" #click="$store.dispatch('switchToEn')" aria-role="listitem">
<div class="media">
<img width='30px' height='30px' src="~/assets/img/icons8-great-britain-48.png"/>
<img width='30px' height='30px' src="~/assets/img/icons8-usa-48.png"/>
<div class="media-content">
<h3>English</h3>
</div>
</div>
</b-dropdown-item>
data () {
return {
activeLanguage: this.$store.state.selectLanguage,
}
},
watch: {
activeLanguage: function() {
console.log(this.activeLanguage)
}
},
~/pages/projects.js
data () {
return {
activeLanguage: this.$store.state.selectLanguage,
}
},
watch: {
activeLanguage: function() {
console.log(this.activeLanguage)
}
},
The problem :
In layout.js, when i switch language, the data activeLanguage change and the watch do a console.log of the new value.
-> it's okay
In project.js, it does not work, i have to change the page and come back to it to have the new store value in my data.
-> it's bad
Anyone know how to do with project.js to have the same comportment that layout.js ?
Thank's !
I'm surprised that activeLanguage did change for you in layout.js. The data function only gets run once when the component gets created and strings are immutable, so I wouldn't have expected activeLanguage in layout.js to pick up when that the selectLanguage value in the store changed.
You should be getting state values from a computed function instead as recommended by the Vuex docs.
Something like this should do the trick:
computed: {
activeLanguage () {
return this.$store.state.selectLanguage
}
}
For a short version, look at mapState.

Laravel 5 vue.js: Property or method "comment" is not defined on the instance but referenced during render

I am creating commenting system using vue.js and laravel5.8.
I have done with models and seeding, so I have now 10 comments to one post (id is 51).
But I got this error,
Property or method "comment" is not defined on the instance but
referenced during render
and
Cannot read property 'user' of undefined
I have problems with fetching data.
I created a new endpoint for a comment function.
web.php
Route::get('results/{post}', 'ResultsController#show')->name('posts.show');
Route::get('results/{post}/comments', 'CommentsController#index');
I want to show comments in show.blade.php.
ResultsController.php
public function show(Post $post)
{
$recommended_posts = Post::latest()
->whereDate('date','>',date('Y-m-d'))
->where('category_id','=',$post->category_id)
->where('id','!=',$post->id)
->limit(7)
->get();
$posts['particular_post'] = $post;
$posts['recommended_posts'] = $recommended_posts;
$post->comments()->with('user')->get();
return view('posts.show',compact('posts'));
}
show.blade.php
<comments-component :post="{{ $posts['particular_post']->comments }}"></comments-component>
comments.vue
<div class="reply-comment" :v-for="comment in comments">
<div class="user-comment" >
<div class="user">
<!--<img src="" alt="" >-->
<avatar :username="comment.user.name" :size="30" ></avatar>
</div>
<div class="user-name">
<span class="comment-name">{{ comment.user.name }}</span>
<p> {{ comment.body }} </p>
</div>
</div>
<div class="reply">
<div class="seemorecomments">
see more
</div>
<button class="reply-button">
<i class="fas fa-reply"></i>
</button>
</div>
</div>
<script>
import Avatar from 'vue-avatar'
export default {
props: ['post'],
components: {
Avatar
},
mounted() {
this.fetchComments()
},
data: () => ({
comments: {
data: []
}
}),
methods: {
fetchComments() {
axios.get(`/results/${this.post.id}/comments`).then(({data}) => {
this.comments = data
})
}
}
}
CommentsController.php
public function index(Post $post)
{
return $post->comments()->paginate(5);
$post->comments()->with('user')->get();
}
comment.php
protected $with = ['user'];
I cannot get data object here.
Within axios, you may need to access data from the response that is returned (see console.log examples here), try the following within your comments component:
methods: {
fetchComments() {
axios.get(`/results/${this.post.id}/comments`).then((response) => {
this.comments = response.data.data
})
}
}
Note response.data.data is used.
I assume returning the ->paginate() will put the results within a data key in the returned array. If not, then just use response.data.
Also, in the controller getting the comments change to the following:
public function index(Post $post)
{
return $post->comments()->with('user')->paginate(5);
}
This will eager load the users with the queried comments.

Issue displaying updated data in Vue component after axios POST

I'm stuck on a problem and was hoping that a Javascript Jedi could help point me in the right direction.
Scope of the problem:
I'm passing a Laravel collection to my Vue component. Inside the component, I'm iterating through the collection and submitting a form via axios. The form submits, the data is updated in the database, but __I'm not clear on how to show the updated value without a page refresh.__
Expected Outcome:
The updated data is reflected in the {{ collection.value }} inside the Vue template after form submission
What's going wrong:
The data is being updated in the database, but the {{ collection.value }} remains the same until page is reloaded.
Web.php:
Route::post('/updateQty', 'MyController#update');
MyController:
public function update(Request $request)
{
$product = Product::where('id', $request->productId)
->update([ 'qty' => $request->qty ]);
return response()->json($product);
}
public function index()
{
$collection = DB::table('products')->get();
return view('my-blade', [
'collections' => $collection,
]);
}
Structure of $collection as stored in the database:
'qty' => decimal(8,2),
'class' => varchar(255),
'description' => varchar(255),
'value' => decimal(8,2),
'productId' => int(11)
my-blade:
<my-component :collections="{{ $collections }}"></my-component>
MyComponent.vue:
<template>
<div class="container">
<div class="row">
<div class="col-lg-12">
<table class="table table-sm">
<div v-if="collections.length">
<tr v-for="collection in collections" v-bind:key="collection.id">
<td>
<form #submit="updateQty">
<input type="hidden" id="productId" :value="collection.productId" name="productId">
<select class="form-control" name="qty" id="qty" #change="updateQty">
<option :value="collection.qty">{{ collection.qty }}</option>
<option v-for="(x, index) in 200" v-bind:key="index" :value="index">{{ index }}</option>
</select>
</form>
</td>
<td>{{ collection.value }}</td>
</tr>
</div>
</table>
</div>
</div>
</div>
</template>
<script>
export default {
props: ['collections'],
data() {
return {
qty: '',
}
}
mounted() {
console.log('MyComponent.vue mounted successfully');
},
methods: {
updateQty(e) {
e.preventDefault();
let currentObj = this;
let url = '/updateQty';
axios.post(url, {
qty: qty.value,
})
.then(function (response) {
currentObj.value = (response.data);
let collections = response.data;
})
},
}
}
</script>
App.js
Vue.component('my-component', require('./components/MyComponent.vue'));
I'm sure it's something simple, but for the life of me I can't wrap my head around it. Thank you very much in advance!
You just need to change up your script a bit.
First, save the collections property to a data property, or Vue will scream when you try to update it. To do this, I would rename the incoming prop as something like collections_prop. Then save it to the collections data property.
Then change let collections = to this.collections = in your update response.
EDIT: I changed the .then function to ES6 syntax as you may have trouble accessing the this variable otherwise. No need for the currentObj stuff.
export default {
props: ['collections_prop'],
mounted() {
console.log('MyComponent.vue mounted successfully');
},
data() {
return {
collections: this.collections_prop;
}
},
methods: {
updateQty(e) {
e.preventDefault();
let url = '/updateQty';
// not sure where qty is coming from
// but you said that's all worked out
// on your end
axios.post(url, {
qty: qty.value,
})
.then(response => {
this.collections = response.data;
})
},
}
}
And finally, don't forget to update the prop in your view.
<my-component :collections_prop="{{ $collections }}"></my-component>
Or if you want to later specify prop type as JSON:
<my-component :collections_prop='#json($collections)'></my-component>

VueJs watching deep changes in object

I have this 3 components in VueJS. The problem i want to solve is: When i click at vehicle component, it needs to be selected (selected = true) and other vehicles unselected.
What i need to do for two-way data binding? Because i'm changing this selected property in VehiclesList.vue component and it also need to be changed in Monit.vue (which is a parent) and 'Vehicle.vue' need to watch this property for change class.
Also problem is with updating vehicles. In Monit.vue i do not update full object like this.vehicles = response.vehicles, but i do each by each one, and changing only monit property.
Maybe easier would be use a store for this. But i want to do this in components.
EDITED:Data sctructure
{
"m":[
{
"id":"v19",
"regno":"ATECH DOBLO",
"dt":"2017-10-09 13:19:01",
"lon":17.96442604,
"lat":50.66988373,
"v":0,
"th":0,
"r":0,
"g":28,
"s":"3",
"pow":1
},
{
"id":"v20",
"regno":"ATECH DUCATO_2",
"dt":"2017-10-10 01:00:03",
"lon":17.96442604,
"lat":50.6698494,
"v":0,
"th":0,
"r":0,
"g":20,
"s":"3"
},
]
}
Monit.vue
<template>
<div class="module-container">
<div class="module-container-widgets">
<vehicles-list :vehicles="vehicles"></vehicles-list>
</div>
</div>
</template>
<script>
import VehiclesList from '#/components/modules/monit/VehiclesList.vue';
export default {
name: "Monit",
data (){
return {
vehicles: null
}
},
components: {
VehiclesList
},
methods: {
getMonitData(opt){
let self = this;
if (this.getMonitDataTimer) clearTimeout(this.getMonitDataTimer);
this.axios({
url:'/monit',
})
.then(res => {
let data = res.data;
console.log(data);
if (!data.err){
self.updateVehicles(data.m);
}
self.getMonitDataTimer = setTimeout(()=>{
self.getMonitData();
}, self.getMonitDataDelay);
})
.catch(error => {
})
},
updateVehicles(data){
let self = this;
if (!this.vehicles){
this.vehicles = {};
data.forEach((v,id) => {
self.vehicles[v.id] = {
monit: v,
no: Object.keys(self.vehicles).length + 1
}
});
} else {
data.forEach((v,id) => {
if (self.vehicles[v.id]) {
self.vehicles[v.id].monit = v;
} else {
self.vehicles[v.id] = {
monit: v,
no: Object.keys(self.vehicles).length + 1
}
}
});
}
},
},
mounted: function(){
this.getMonitData();
}
};
</script>
VehiclesList.vue
<template>
<div class="vehicles-list" :class="{'vehicles-list--short': isShort}">
<ul>
<vehicle
v-for="v in vehicles"
:key="v.id"
:data="v"
#click.native="select(v)"
></vehicle>
</ul>
</div>
</template>
<script>
import Vehicle from '#/components/modules/monit/VehiclesListItem.vue';
export default {
data: function(){
return {
isShort: true
}
},
props:{
vehicles: {}
},
methods:{
select(vehicle){
let id = vehicle.monit.id;
console.log("Select vehicle: " + id);
_.forEach((v, id) => {
v.selected = false;
});
this.vehicles[id].selected = true;
}
},
components:{
Vehicle
}
}
</script>
Vehicle.vue
<template>
<li class="vehicle" :id="data.id" :class="classes">
<div class="vehicle-info">
<div class="vehicle-info--regno font-weight-bold"><span class="vehicle-info--no">{{data.no}}.</span> {{ data.monit.regno }}</div>
</div>
<div class="vehicle-stats">
<div v-if="data.monit.v !== 'undefined'" class="vehicle-stat--speed" data-name="speed"><i class="mdi mdi-speedometer"></i>{{ data.monit.v }} km/h</div>
</div>
</li>
</template>
<script>
export default {
props:{
data: Object
},
computed:{
classes (){
return {
'vehicle--selected': this.data.selected
}
}
}
}
</script>
Two-way component data binding was deprecated in VueJS 2.0 for a more event-driven model: https://v2.vuejs.org/v2/guide/components.html#One-Way-Data-Flow
This means, that changes made in the parent are still propagated to the child component (one-way). Changes you make inside the child component need to be explicitly send back to the parent via custom events: https://v2.vuejs.org/v2/guide/components.html#Custom-Events or in 2.3.0+ the sync keyword: https://v2.vuejs.org/v2/guide/components.html#sync-Modifier
EDIT Alternative (maybe better) approach:
Monit.vue:
<template>
<div class="module-container">
<div class="module-container-widgets">
<vehicles-list :vehicles="vehicles" v-on:vehicleSelected="onVehicleSelected"></vehicles-list>
</div>
</div>
</template>
<script>
import VehiclesList from '#/components/modules/monit/VehiclesList.vue';
export default {
name: "Monit",
data (){
return {
vehicles: null
}
},
components: {
VehiclesList
},
methods: {
onVehicleSelected: function (id) {
_.forEach((v, id) => {
v.selected = false;
});
this.vehicles[id].selected = true;
}
...other methods
},
mounted: function(){
this.getMonitData();
}
};
</script>
VehicleList.vue:
methods:{
select(vehicle){
this.$emit('vehicleSelected', vehicle.monit.id)
}
},
Original post:
For your example this would probably mean that you need to emit changes inside the select method and you need to use some sort of mutable object inside the VehicleList.vue:
export default {
data: function(){
return {
isShort: true,
mutableVehicles: {}
}
},
props:{
vehicles: {}
},
methods:{
select(vehicle){
let id = vehicle.monit.id;
console.log("Select vehicle: " + id);
_.forEach((v, id) => {
v.selected = false;
});
this.mutableVehicles[id].selected = true;
this.$emit('update:vehicles', this.mutableVehicles);
},
vehilcesLoaded () {
// Call this function from the parent once the data was loaded from the api.
// This ensures that we don't overwrite the child data with data from the parent when something changes.
// But still have the up-to-date data from the api
this.mutableVehilces = this.vehicles
}
},
components:{
Vehicle
}
}
Monit.vue
<template>
<div class="module-container">
<div class="module-container-widgets">
<vehicles-list :vehicles.sync="vehicles"></vehicles-list>
</div>
</div>
</template>
<script>
You still should maybe think more about responsibilities. Shouldn't the VehicleList.vue component be responsible for loading and managing the vehicles? This probably would make thinks a bit easier.
EDIT 2:
Try to $set the inner object and see if this helps:
self.$set(self.vehicles, v.id, {
monit: v,
no: Object.keys(self.vehicles).length + 1,
selected: false
});

Removing item in Firebase with React, re-render returns item undefined

I am building an app to learn React, and am using Firebase as my data storage. I have all the items in my firebase rendering out, and am trying to enable removal of individual items. However, when I try to remove, I get Uncaught TypeError: Cannot read property 'name' of null after clicking on the remove button, and it is referring to this line of code in the renderExpenditure function:
<strong>{details.name}</strong>, <strong>{h.formatPrice(details.amount)}</strong>, {details.category}, {details.type}, {details.date}
The state is set up as follows:
getInitialState: function() {
return {
cashbook: {
expenditure: {},
income: {}
},
totals: {},
available: {}
}
}
And the functions which render out the items and try to remove them are as follows:
(Can anyone see what I am doing wrong, or is this too little code to work out what is going on?)
Thanks in advance.
Within App
render: function() {
return(
<div className="cashbook">
<div className="expenditure">
<ul className="items">
{Object.keys(this.state.cashbook.expenditure).map(this.renderExpenditure)}
</ul>
</div>
</div>
);
}
renderExpenditure: function(key) {
var details = this.state.cashbook.expenditure[key];
return(
<li className="item" key={key}>
<strong>{details.name}</strong>, <strong>{h.formatPrice(details.amount)}</strong>, {details.category}, {details.type}, {details.date}
<button className="remove-item" onClick={this.removeExpenditure.bind(null, key)}>Remove</button>
</li>
);
},
removeExpenditure: function(key) {
this.state.cashbook.expenditure[key] = null;
this.setState({
expenditure: this.state.cashbook.expenditure
});
},
You are setting the wrong value in setState. expenditure doesn't exist in the root state, so you must overwrite the parent that contains it. It should be:
this.state.cashbook.expenditure[key] = null;
this.setState({
cashbook: this.state.cashbook
});

Categories