Vue Object not updating with axios POST request - javascript

<template>
<div>
<div class="dropdown d-inline-block ml-2">
<button type="button" class="btn btn-sm btn-dual" id="page-header-notifications-dropdown" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-on:click="MarkAllAsRead($event)">
<i class="si si-bell"></i>
<span class="badge badge-danger badge-pill" v-if="CountUnread(notifications)">{{ CountUnread(notifications) }}</span>
</button>
<div class="dropdown-menu dropdown-menu-lg dropdown-menu-right p-0 border-1 font-size-sm" aria-labelledby="page-header-notifications-dropdown">
<div class="p-2 bg-primary text-center">
<h5 class="dropdown-header text-uppercase text-white">Notifications</h5>
</div>
<ul class="nav-items mb-0" style="overflow-y:scroll; height:500px;">
<!-- notifications should have been updated by axios but they never appear -->
<li v-for="notification of notifications">
<div v-if="notification.length">
<notification v-bind:notification="notification[0]"></notification>
</div>
<div v-else>
<notification v-bind:notification="notification"></notification>
</div>
</li>
<div class="p-2 border-top" v-if="hasMore">
<a class="btn btn-sm btn-light btn-block text-center" href="javascript:void(0)" v-on:click="loadMoreNotifications($event)">
<i class="fa fa-fw fa-arrow-down mr-1"></i> Load More..
</a>
</div>
</ul>
</div>
</div>
</div>
</template>
<script>
export default {
props:[
'user'
],
data: function() {
return{
notificationsIndex:0,
notifications:[],
hasMore:1
}
},
methods: {
loadNotifications: function(){
var data = {
index: this.notificationsIndex
};
axios.post('/notifications/getAll',data).then( response => {
if(response.data){
if(this.notifications.length==0){
this.notifications=response.data;
console.log(this.notifications);//data fetched here successfully
}
else{
this.notifications.push.apply(this.notifications,response.data);
}
}
else{
this.hasMore = 0;
}
});
console.log(this.notifications);//couldn't find data, just observer object
},
loadMoreNotifications: function(event){
event.preventDefault();
this.notificationsIndex++;
this.loadNotifications();
},
CountUnread: function(notifications){
var count=0;
for(var i=0;i<notifications.length;i++){
if(notifications[i].read_at == null){
count++;
}
}
return count;
},
HasNotification: function(notification){
list = this.notifications.filter(item => {
return item.id == notification.id;
});
return list.length
}
},
created: function(){
this.loadNotifications();
window.Echo.private('App.User.' + this.user.id)
.notification((notification) => {
if(this.HasNotification){
this.notifications.unshift(notification);
}
});
}
}
</script>
And notifications object has nothing in html template as well.
Note: This same code works fine on all pages where I have this only instance of vue. On another page (localhost/horizon)(using laravel horizon package and updating it's layout.blade.php ) where there are two instances of vue, one for notifications and other of Laravel/Horizon, this request returns nothing.

The problem here as #cbaconnier mentioned is that you're using an async function and not waiting for the result.
The console.log() that's failing is due to the fact that it's executed before the post request is retrieved (that's why you're receiving an observer).
The same happens in your created method.
try taking all the code that's dependant on the received notifications into the .then() callback of the axios call.

The problem here was, I was using a bootstrap shipped with app theme I was using and when I installed Horizon and updated Horizon layout.blade.php file according to my customisations, it broke this piece of code because it was using some other bootstrap version and once I removed the bootstrap from the horizon require('bootstrap'); in one of it's app.js file, everything worked fine.

Related

Axios making DELETE and GET in the wrong order

I'm working on a small project in vue.js connected to a lumen API (working).
I currently have a list of students ('Etudiants') in which I can click in the list to select one, and delete it via a button in a toolbar.
When a student is deleted I'm reloading the student list (since it's not up to date anymore), therefore I'm doing 2 api calls via axios.
DELETE http://www.url.com/etudiants (param: idEtudiant)
GET http://www.url.com/etudiants (param: page)
The problem is that my API calls are not done in the right order, as shown here on a screenshot of the calls (with watterfall):
This problem involves 3 vue files.
'Etudiants.vue' and its 2 childs: 'ListeEtudiants.vue' (student list) and 'BarreOutilsEtudiant.vue' (toolbar)
This simple image shows the hierarchy of the 3 files and the order of which everything should execute.
In my case (when it's not working) the action number 3, the axios DELETE, happens in last.
Here are the contents of my files:
Etudiants.vue:
<template>
<div id="etudiants" class="container-fluid h-100">
<div class="row">
<div class="col-3 borderR">
<ListeEtudiants ref="list" #idEtudiantChanged="updateIdEtudiant"/>
</div>
<div class="col-9 bg-light">
<BarreOutilsEtudiant v-if="idEtudiant != null" :idEtudiant="idEtudiant" #delEtudiant="delEtudiant"/>
<InfosEtudiant v-if="idEtudiant != null" :idEtudiant="idEtudiant"/>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
import ListeEtudiants from '#/components/ListeEtudiants.vue'
import InfosEtudiant from '#/components/InfosEtudiant.vue'
import BarreOutilsEtudiant from '#/components/BarreOutilsEtudiant.vue'
export default {
components: {
ListeEtudiants,
InfosEtudiant,
BarreOutilsEtudiant
},
data: function(){
return {
idEtudiant: null
}
},
methods:{
updateIdEtudiant(idEtudiant){
this.idEtudiant=idEtudiant;
},
delEtudiant(){
axios
.delete('http://82ab2617.ngrok.io/etudiants', {params: {"idEtudiant" :this.idEtudiant}})
.then(this.$refs.list.loadList())
.catch(error => console.log(error));
}
}
}
</script>
ListeEtudiants.vue:
<template>
<div id="ListeEtudiants">
<div class="row bg-light">
<!-- Trigger Modal Ajout Etudiant -->
<button class="btn btn-light w-100" data-toggle="modal" data-target="#addModal">
<font-awesome-icon icon="plus" size="1x"/>
<span> Ajouter un Etudiant</span>
</button>
</div>
<ul v-if="etudiants != null" id="list" class="row flex-nowrap list-group list-group-flush pr-0">
<button v-for="etudiant in etudiants.data" v-on:click='$emit("idEtudiantChanged",etudiant.idEtudiant)' class="btn btn-light text-left list-group-item pl-5 py-1">{{ etudiant.nom }} {{ etudiant.prenom }}</button>
</ul>
<ul v-else class="row flex-nowrap list-group list-group-flush pr-0">
</ul>
<div class="row bg-light">
<button class="btn btn-light col-3" v-on:click="page -= 1" :disabled="page === 1 || disabled"><font-awesome-icon icon="chevron-left" size="1x"/></button>
<div class="align-middle col-6 my-auto">{{ page }} / {{ maxPage }}</div>
<button class="btn btn-light col-3" v-on:click="page += 1" :disabled="page === maxPage || disabled"><font-awesome-icon icon="chevron-right" size="1x"/></button>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: "ListeEtudiants",
data: function(){
return {
etudiants: null,
maxPage:1,
disabled:false,
page:1,
}
},
methods: {
parseAndDisplay: function(data){
this.etudiants = data;
this.maxPage = data.last_page;
this.page = data.current_page;
this.disabled = false;
},
loadList: function(){
this.disabled = true;
this.etudiants = null;
axios
.get('http://82ab2617.ngrok.io/etudiants', {params: {page:this.page}})
.then(response =>this.parseAndDisplay(response.data))
.catch(error => console.log(error));
}
},
watch: {
'page': function(newVal, oldVal){
if((newVal === 0 && oldVal === 1) || (newVal === this.maxPage+1 && oldVal === this.maxPage)){
this.page = oldVal;
}else{
if(oldVal !== 0 && oldVal !== this.maxPage+1) {
this.loadList();
}
}
}
}
,
mounted() {
this.loadList();
}
}
</script>
BarreOutilsEtudiant.vue:
<template>
<div class="row p-2 navbar-expand navbar-info bg-info">
<button class="btn btn-info mr-5" type="button"><font-awesome-icon icon="download" size="1x"/> Télécharger le Bulletin</button>
<button class="btn btn-info ml-auto" type="button"><font-awesome-icon icon="user-edit" size="1x"/></button>
<button class="btn btn-danger ml-4" v-on:click="$emit('delEtudiant')" type="button"><font-awesome-icon icon="trash-alt" size="1x"/></button>
</div>
</template>
<script>
export default {
name: "BarreOutilsEtudiant"
}
</script>
<style scoped>
</style>
Thank you very much for helping me.
I believe the problem is here:
.then(this.$refs.list.loadList())
That will call loadList immediately and pass the value it returns to then, which isn't what you want.
Instead it should be something like this, wrapping it in a function:
.then(() => this.$refs.list.loadList())

Error on saving to local storage for Vue.js

In my Vue JS app I am trying to save data I get back from a rest api into a list component but I am getting the following error:
Error in mounted hook: "SyntaxError: Unexpected token u in JSON at position 0"
I can't see what could be causing this?
Component:
<template>
<div class="mb-5 mt-5 container">
<div class="list-group">
<a href="#" class="list-group-item list-group-item-action active">
My List
<span class="badge badge-light">{{List.length}}</span>
</a>
<a
href="#"
class="list-group-item list-group-item-action"
v-for="(result, index) in List"
:key="index"
>
{{result.collectionName}}
<br>
<b>{{result.artistName}}</b>
<br>
<div class="row">
<div class="col-md-6">
<button
class="btn btn-success btn-sm btn-block"
v-on:click="removeElement(index)"
>Remove</button>
</div>
<div class="col-md-6">
<button
class="btn btn-info btn-sm btn-block"
#click="toiTunesAlbum(result)"
>View on iTunes</button>
</div>
</div>
</a>
</div>
<button class="btn btn-info mt-5 btn-lg btn-block" #click="saveList()">Save</button>
</div>
</template>
<script>
export default {
name: "List",
props: ["List"],
mounted() {
if(localStorage.result) this.result = JSON.parse(this.result);
},
methods: {
saveList() {
localStorage.result = this.result;
localStorage.setItem('result', JSON.stringify(this.result));
/* eslint-disable no-console */
console.log(this.result + 'List Saved');
/* eslint-disable no-console */
},
removeElement: function(index) {
this.List.splice(index, 1);
},
resizeArtworkUrl(result) {
return result.artworkUrl100.replace("100x100", "160x160");
},
toiTunesAlbum(result) {
window.open(result.collectionViewUrl, "_blank");
}
}
};
</script>
<style scoped>
.album-cover {
width: 100%;
height: auto;
background-color: aqua;
}
.album-container {
height: 350px;
}
</style>
Any ideas on how I can save to local storage the rest api result that would be great. Should I be using the List array or result?
I think your problem is that you are only initializing this.result if localStorage.result has a value. If localStorage.result doesn’t have a value the first time you call saveList this.result is still undefined. Try setting a default value or skip trying to save if the value is still undefined. I am also not sure of the relationship between this.List and this.result, since List seems to be a list of results?

Array Splice always delete an item from last?

I am facing a problem in deleting item from an array. Array splice supposed to work but its not working like I want. Its always delete the item from last. I am using Vue.js . I am pushing item dynamically to an array. But after click remove its delete from the last. why I am facing this. I am attaching the codes.
<template>
<div>
<span class="badge badge-pill mb-10 px-10 py-5 btn-add" :class="btnClass" #click="addBtn"><i class="fa fa-plus mr-5"></i>Button</span>
<div class="block-content block-content-full block-content-sm bg-body-light font-size-sm" v-if="buttons.length > 0">
<div v-for="(item, index) in buttons">
<div class="field-button">
<div class="delete_btn"><i #click="remove(index)" class="fa fa-trash-o"></i></div>
<flow-button v-model="item.title" :showLabel="false" className="btn btn-block min-width-125 mb-10 btn-border" mainWrapperClass="mb-0" wrapperClass="pt-0" placeholder="Button Title"></flow-button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import flowButton from '../assets/flow-button'
export default {
name: "textArea",
props:{
index : Number
},
data() {
return {
buttons : [],
btnClass : 'badge-primary',
}
}
components : {
flowButton
},
methods : {
addBtn () {
if(this.buttons.length >= 2) {
this.btnClass = 'btn-secondary'
}
if(this.buttons.length < 3) {
this.buttons.push({
title : ''
});
}
},
remove(index) {
this.buttons.splice(index, 1)
}
}
}
</script>
This must be because of your flow-button I have tried to replicate your error but endup to this code. I just replaced the flow-button with input and it works. Try the code below.
Use v-bind:key="index", When Vue is updating a list of elements rendered with v-for, by default it uses an “in-place patch” strategy. If the order of the data items has changed, instead of moving the DOM elements to match the order of the items, Vue will patch each element in-place and make sure it reflects what should be rendered at that particular index. This is similar to the behavior of track-by="$index"
You have missing comma between data and components, I remove the component here it won't cause any error now, and more tips don't mixed double quotes with single qoutes.
<template>
<div>
<span class="badge badge-pill mb-10 px-10 py-5 btn-add" :class="btnClass" #click="addBtn"><i class="fa fa-plus mr-5"></i>Button</span>
<div class="block-content block-content-full block-content-sm bg-body-light font-size-sm" v-if="buttons.length > 0">
<div v-for="(item, index) in buttons" v-bind:key="index">
<div class="field-button">
<div class="delete_btn"><i #click="remove(index)" class="fa fa-trash-o">sdfsdff</i></div>
<input type="text" v-model="item.title" :showLabel="false" className="btn btn-block min-width-125 mb-10 btn-border" mainWrapperClass="mb-0" wrapperClass="pt-0" placeholder="Button Title"/>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'textArea',
props: {
index: Number
},
data () {
return {
buttons: [],
btnClass: 'badge-primary'
}
},
methods: {
addBtn () {
if (this.buttons.length >= 2) {
this.btnClass = 'btn-secondary'
}
if (this.buttons.length < 3) {
this.buttons.push({
title: ''
})
}
},
remove (index) {
this.buttons.splice(index, 1)
}
}
}
</script>
I think that you may be facing a conflict with the index prop of your component. Try to use a different name for the index of your v-for loop:
<div v-for="(item, ind) in buttons">
<div class="field-button">
<div class="delete_btn"><i #click="remove(ind)" class="fa fa-trash-o"></i></div>
<flow-button v-model="item.title" :showLabel="false" className="btn btn-block min-width-125 mb-10 btn-border" mainWrapperClass="mb-0" wrapperClass="pt-0" placeholder="Button Title"></flow-button>
</div>
</div>
Try this. Removing an item correctly using this.
<div v-for="(item, ind) in buttons" :key="JSON.stringify(item)">

Meteor template not updating on changed data

I'm currently building a nifty little 'talent point distributor' view, similar to what popular RPG games offer. I didn't want a huge wall of HTML code for all the buttons and textboxes, so I created a template to which I pass two parameters:
the name of the stat I want to alter
the initial value of the stat
The template renders correctly, and I notice that when I log the results to the console, the variable seems to be changed correctly. However, the displayed value does not change and will always stay at 0.
Here is the template itself:
<template name="attributeStepper">
<div class="row" style="margin: 1em;">
<div class="col-sm-3 col-md-2">
<h4>{{toUpper attribute}}</h4>
</div>
<div class="col-sm-6 col-md-4">
<div class="btn-group" role="group">
<button type="button" class="btn btn-default btn-value-dec">
<span class="glyphicon glyphicon-chevron-down"></span>
</button>
<button type="button" class="btn btn-default disabled">{{attributeValue}}</button>
<button type="button" class="btn btn-default btn-value-inc">
<span class="glyphicon glyphicon-chevron-up"></span>
</button>
</div>
</div>
</div>
</template>
Here is the helper I defined for the template:
Template.attributeStepper.helpers({
toUpper : function(str) {
return str.substring(0, 1).toUpperCase() + str.substring(1);
}
})
Template.attributeStepper.events({
'click .btn-value-inc' : function(event, tmpl) {
tmpl.data.attributeValue ++;
},
'click .btn-value-dec' : function(event, tmpl) {
tmpl.data.attributeValue --;
}
});
And this is how I call the templates from the actual view:
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Attributes</h3>
</div>
{{ >attributeStepper attribute="strength" attributeValue="0"}}
{{ >attributeStepper attribute="courage" attributeValue="0"}}
{{ >attributeStepper attribute="intelligence" attributeValue="0"}}
{{ >attributeStepper attribute="agility" attributeValue="0"}}
{{ >attributeStepper attribute="dexterity" attributeValue="0"}}
{{ >attributeStepper attribute="intuition" attributeValue="0"}}
{{ >attributeStepper attribute="charisma" attributeValue="0"}}
</div>
I hope you can make any sense out of this and tell me what I'm doing wrong, because I feel like I'm not following the mindset behind Meteor correctly yet.
Cheers!
There is nothing wrong but also nothing reactive in your code. For the attributeValue you should use a template based ReactiveVar which is created at the onCreate Event
Template.attributeStepper.onCreated(function() {
if (! _.isUndefined(this.data.startingValue))
this.attributeValue = new ReactiveVar(Number(this.data.startingValue));
else
this.attributeValue = new ReactiveVar(0);
})
You can use some initialValue from Template as you like
See complete example at the MeteorPad I created for you.
http://meteorpad.com/pad/Zw7YnnW57uuGKcu3Q/MultipleTemplateUsage
This should solve your question
Cheers
Tom
Do you have idea about reactive-var in meteor (Meteor Doc) or you can also use Session instead of reactive-var (ReactiveVar is similar to a Session variable)
Have a look at changes as per your code.
Here is the template(.html)
<template name="attributeStepper">
<div class="row" style="margin: 1em;">
<div class="col-sm-3 col-md-2">
<h4>{{toUpper attribute}}</h4>
</div>
<div class="col-sm-6 col-md-4">
<div class="btn-group" role="group">
<button type="button" class="btn btn-default btn-value-dec">
<span class="glyphicon glyphicon-chevron-down"></span>
</button>
<button type="button" class="btn btn-default disabled">{{getAttributeValue}}</button>
<button type="button" class="btn btn-default btn-value-inc">
<span class="glyphicon glyphicon-chevron-up"></span>
</button>
</div>
</div>
</div>
</template>
Here is helpers for your template(.js)
Template.attributeStepper.created = function(){
this.attributeValue = new ReactiveVar(parseInt(this.data.attributeValue));
}
Template.attributeStepper.helpers({
toUpper : function(str) {
return str.substring(0, 1).toUpperCase() + str.substring(1);
},
getAttributeValue : function(){
return Template.instance().attributeValue.get();
}
});
Template.attributeStepper.events({
'click .btn-value-inc' : function(event, tmpl) {
tmpl.attributeValue.set(tmpl.attributeValue.get()+1)
},
'click .btn-value-dec' : function(event, tmpl) {
tmpl.attributeValue.set(tmpl.attributeValue.get()-1)
}
});
Template.attributeStepper.created = function(){...} method called before your template's logic is evaluated for the first time.

show element in template if owner meteor js

I am writing a messaging app which has a delete / edit function for a user for a given message that is submitted. What I would like to do is write something like:
{{#if currentUser._id === this._id}}
<!-- Show -->
{{/if}}
But this is probably wrong I have a template written for the message record:
<template name="message">
<div class="row message-row">
<div class="col-md-12">
<div class="message-container">
<div class="message-avatar">
<img src="{{userAvatar}}">
</div>
<p>{{message}}</p>
<div class="message-time">{{prettifyDate time}}</div>
<!-- this is the div to hide / show based on the conditional -->
<div class="message-controls">
<button class="btn btn-link btn-xs" type="button" id="deleteMessage"><i class="fa fa-trash-o"></i></button>
<button class="btn btn-link btn-xs" type="button" id="editMessage"><i class="fa fa-edit"></i></button>
</div>
<!-- end -->
</div>
</div>
</div>
</template>
And I am using the following in my client.js
Template.messages.messages = function() {
return Messages.find({}, { sort: {time: -1} });
}
But at this point I am stuck
Assuming your message documents have a userId field, you can simply do this:
Template.message.helpers({
isOwner: function() {
return this.userId === Meteor.userId();
}
});
and:
{{#if isOwner}}
<!-- controls -->
{{/if}}
You could also make a more flexible, reusable global helper for this:
Template.registerHelper('isCurrentUser', function(userId) {
return userId === Meteor.userId();
});
<!-- in this example, the document might have an `ownerId` field rather than `userId` -->
{{#if isCurrentUser ownerId}}{{/if}}
Of course, you also need to validate the updates on the server with the allow/deny API or with custom methods.

Categories

Resources