vuejs refresh template after ajax return - ajax

I'm trying to make my VueJS template refresh after an AJAX request.
At the page first loading my datas are correctly updated en the template is OK.
in my template i have this section that i want to be updated each time one of the task has changed :
<li v-for="(task,index) in tasks" #click="setCurrentTask(index)" :class="{'active' : currentTask.id == task.id}">
<span><i class="far fa-dot-circle"></i></span>{{ task.attributes.name }}
<a class="ui mini label" :class="task.attributes.state.attributes.name">{{ task.attributes.state.attributes.name }}</a><br>
<div class="details">
{{ currentTask.attributes.estimatedStart }}<br>
<i class="fas fa-play" #click="startTask(index)"></i>
</div>
</li>
Then i have a request that says to change the state of a specific task to start it then it returns the task with the status updated :
export default {
name: "fdr",
data(){
return {
fdr:'',
tasks:'',
currentTask:null,
currentIndex:null,
}
},
methods: {
setCurrentTask: function (index) {
this.currentTask = this.tasks[index]
this.currentIndex = index;
if(this.currentTask.time != null){
$('.time').html( this.currentTask.timer.getTimeValues().toString());
$('#startDate').calendar();
}
},
startTask(index){
axios.get(this.$env_uri+'/api/task/'+this.tasks[index].id+"/start").then(function (resource) {
this.tasks[index] = resource.body
}.bind(this))
},
},
created() {
this.$http.get(this.$env_uri+'/api/fdr/' + this.$route.params.id)
.catch(response => {
return this.$router.push('/404')
})
.then(function (resource) {
this.fdr = resource.body
this.tasks = this.fdr.relationship.tasks
this.setCurrentTask(0);
}
);
},
When this.task is updated VueJs is not refreshing my template to display the new status of the task. I can't find a way to do it... do you have any ideas ?

Totally misunderstod the issue first.
The problem is updating updating an index in an array. This is something that has to be done with Array.$set(index, value) since Vue don't have a way to tell if an index in an array is changed.
You can read more about it here

Related

using axios data from Laravel pagination array

ok I am stumped and I know its going to be something stupid. So let me explain more in detail. The code above is what I believe should work for pagination from a Laravel controller to a vue component. I get that I am not fully accounting for pagination and I can handle that later but the first results are not available for use when I do this and I do not understand where my syntax is wrong.
Vue:
<ul role="list" class="space-y-4">
<li
v-for="item in allActivities"
:key="item.id"
class="bg-gray-800 px-4 py-6 shadow sm:p-6 sm:rounded-lg"
>
{{ item.description }}
</li>
</ul>
Mounted:
mounted() {
axios
.get("/activity")
.then((response) => (this.allActivities = response.data));
},
Controller
public function index()
{
$activity = Activity::paginate(10);
return $activity;
}
If in the v-if I change it to allActivities.data it refreshes to show the data until I reload the page and get id not found.
If I change the axios to response.data.data it works, but I lose pagination.
IM stuck
response.data is the result from laravel
response.data.data if the data key in the result which is a list of your models
response.data will contain these keys if using
current_page
data
first_page_url
from
last_page
last_page_url
links
next_page_url
path
per_page
prev_page_url
to
total
I did not fully understand the problem. If you want to change page, send the value of the page you want to display to the fetchActivities() method.
EDIT!
<script>
export default {
data() {
return {
allActivities: {
data: [],
total: null
}
}
},
methods: {
async fetchActivities(page = 1){
const {data:activities} = await axios.get(`/activity?page=${page}`)
this.allActivities.data.push(activities.data)
this.allActivities.total = activities.total
}
},
created() {
this.fetchActivities()
}
}
</script>
<template>
<div v-if="allActivities.data.length > 0">
<ul role="list" class="space-y-4">
<li
v-for="(item, index) in allActivities.data"
:key="index"
class="bg-gray-800 px-4 py-6 shadow sm:p-6 sm:rounded-lg"
>
{{ item.description }}
</li>
</ul>
</div>
</template>

Updates the data in vue with laravel without refreshing the page or overlapping the div

I have a problem in sight.
I just started to learn, but I'm confused about something, why when I update the data I send with axios.post and update them, the devil disappears for 2 seconds and refreshes the page.
Look how I thought
<li v-for="question in questions" :key="question.id" class="list-group-item">
{{ question.questiontype }}
<br>
<button v-on:click="addVote(question.id)">Add 1</button>
<br>
<span class="badge badge-primary"><strong>{{ question.votes }}</strong></span>
</li>
data() {
return {
success: false,
questions:[],
percentages:[],
}
},
created() {
this.getQuestions(),
this.getPercentages()
},
methods: {
getQuestions(){
axios.get('/api/questionlist')
.then((response)=>{
this.questions = response.data.question
})
},
getPercentages(){
axios.get('/api/getpercentage')
.then((response)=>{
this.percentages = response.data.percentage
})
},
addVote(id) {
axios.post('api/vote/' + id)
.then((response) => {
this.questions = response.data.question
console.log(response.data)
this.getQuestions()
})
}
So I'm interested in the data I sent with the post.
I would like the page to update without refresh or to overlap the div for 2 seconds.
What exactly am I doing wrong?
I tried a few ways on stackoverflow but they didn't help
If your button is inside a form, you probably want to add type="button" attribute to it or prevent the form submission action.
<button type="button" v-on:click="addVote(question.id)">Add 1</button>
OR:
<button v-on:click.prevent="addVote(question.id)">Add 1</button>
I'm guessing that's probably why you're seeing the page refresh. If you want to display the updated data, you either need to make another axios call to fetch or you need to return the new entry from your API endpoint and push/add it to your list.
So modify your code as follows:
addVote(id) {
axios.post('api/vote/' + id)
.then((response) => this.getQuestions());
}

Difficult to get associate table's record in Vuejs from Laravel collection

I'm using Vuejs and Laravel building a live feed app, but i found that it is difficult to get associate data from collection in Vuejs, is there anyway to get those data easily?
Here is my attempt:
<div class="sl-item" v-for="post, index in posts">
<a href="javascript:void(0)">
<div class="sl-content">
{{post}}
<br>
------------------------
<br>
{{getUser(post.user_id)}}
</div>
</a>
</div>
Here is my methods that i intend to retrieve user record:
methods: {
getUser (id)
{
return axios.get("/user/getUser/" + id)
.then(response => {
console.log(response.data);
return response.data;
});
}
},
But this is what i got:
I can log what i get in console, but i can't display or access the thing i return from my method.
Is there any other easier way to achieve this?
You fetching all the data from collection. that's why you also getting unwanted data in your object
in your current situation (without changing in your controller script) you can access those data as like:
public function index(Request $request)
{
if ($request->ajax()) {
return Post::with(['user' => function($query){
$query->select(['id', 'name'])
}])
->select(['id', 'title', 'description'])
->get;
}
}
if you do this in your controller, then in your vue file,
data:function(){
return{
posts: [],
};
},
methods: {
getPosts ()
{
return axios.get("/posts")
.then(response => {
this.posts = response.data;
});
}
},
and in component:
<div class="sl-item" v-for="post, index in posts">
<a href="javascript:void(0)">
<div class="sl-content">
{{post.title}}
<br>
------------------------
<br>
{{ post.user.name }}
</div>
</a>
</div>

Find a matching value in Vue component

I have passed this collection (postFavourite) to my vue component via props.
[{"id":1,"user_id":1,"post_id":2,"created_at":"2018-07-24 09:11:52","updated_at":"2018-07-24 09:11:52"}]
How do I then check if any instance of user_id in the collection is equal to userId which is the current logged in user (also sent via props).
Tried
let pf = _.find(this.postFavourite, { "user_id": this.userId})
Keep getting undefined as the value of the pf variable even though this.userID is equal to 1.
New to JS and Vue.js so any help would be great.
Here is the vue component code.
<template>
<div>
<i v-show="this.toggle" #click="onClick" style="color: red" class="fas fa-heart"></i>
<i v-show="!(this.toggle)" #click="onClick" style="color: white" class="fas fa-heart"></i>
</div>
</template>
<script>
export default {
data() {
return {
toggle: 0,
}
},
props: ['postData', 'postFavourite', 'userId'],
mounted() {
console.log("Post is :"+ this.postData)
console.log("User id is: "+ this.userId)
console.log("Favourite Object is :" +this.postFavourite);
console.log(this.postFavourite.find(pf => pf.user_id == this.userId));
},
methods: {
onClick() {
console.log(this.postData);
this.toggle = this.toggle ? 0 : 1;
}
}
}
</script>
This is how I passed the props to vue
<div id="app">
<favorite :post-data="'{{ $post->id }}'" :post-favourite="'{{Auth::user()->favourite }}'" :user-id="'{{ $post->user->id }}'"></favorite>
</div>
I gave up on lodash and find and just messed around with the data in the chrome console to work out how to check the value I wanted.
Then I built a loop to check for the value.
If it found it toggle the like heart on of not leave it off.
This will not be the best way to solve this problem but I'm just pleased I got my first real vue component working.
<template>
<div>
<i v-show="this.toggle" #click="onClick" style="color: red" class="fas fa-heart"></i>
<i v-show="!(this.toggle)" #click="onClick" style="color: white" class="fas fa-heart"></i>
</div>
</template>
<script>
export default {
props: ['postData', 'postFavourite', 'userId']
,
data() {
return {
toggle: 0,
favs: [],
id: 0
}
},
mounted () {
var x
for(x=0; x < this.postFavourite.length; x++){
this.favs = this.postFavourite[x];
if(this.favs['post_id'] == this.postData) {
this.toggle = 1
this.id = this.favs['id']
}
}
},
methods: {
onClick() {
console.log(this.postData)
if(this.toggle == 1){
axios.post('favourite/delete', {
postid: this.id
})
.then(response => {})
.catch(e => {
this.errors.push(e)
})
}
else if(this.toggle == 0){
axios.post('favourite', {
user: this.userId,
post: this.postData
})
.then(response => {
this.id = response.data
})
.catch(e => {
this.errors.push(e)
})
}
this.toggle = this.toggle ? 0 : 1;
}
}
}
</script>
Where I pass my props.
<favorite :post-data="'{{ $post->id }}'"
:post-favourite="{{ Auth::user()->favourite }}"
:user-id="'{{ Auth::user()->id }}'"></favorite>
Thanks to all that tried to help me.
From just the code you provided, I see no issue. However lodash is not required for this problem.
Using ES2015 arrow functions
let pf = this.postFavourite.find(item => item.user_id === this.userId);
Will find the correct item in your array
You can read more about this function in the mdn webdocs
You can use find() directly on this.postFavourite like this:
this.postFavourite.find(pf => pf.user_id == this.userId);
Here is another way to do it that might help you as well.
[EDIT]
In order to use find() the variable needs to be an array, this.postFavourite is sent as a string if you didn't use v-bind to pass the prop thats what caused the error.
To pass an array or an object to the component you have to use v-bind to tell Vue that it is a JavaScript expression rather than a string. More informations in the documentation
<custom-component v-bind:post-favourite="[...array]"></custom-component>

Laravel router-link works only the first time

I am trying to fetch results from database in News.vue, and display them in Topnews.vue. I have two links fetched. When I click link1, it shows up the Topnews.vue template with everything working as intended, however, if i click link2, nothing happens, except for that the URL changes, but the template does not show up the result. If i refresh the page and click link2 or click on the navbar, then link2, it shows up, and same, clicking then link1, changes the URL, but doesnt show up. I'm really stuck on that and I'd be really glad if you help me out on that issue. Hope you understand.
News.vue
<template id="news">
<div class="col-sm-5">
<div class="cars" v-for="row in filteredNews" >
<div class="name" >
<p class="desc_top_time">{{row.created_at}}</p>
<span class="last_p"> {{row.category}}</span>
<h3 style="margin-bottom:-4px; font-size: 16px;">
<router-link class="btn btn-primary" v-bind:to="{name: 'Topnews', params: {id: row.id} }">{{row.title}}</router-link></h3>
</div></div></div>
</template>
<script>
export default {
data: function() {
return {
news: [],
}
},
created: function() {
let uri = '/news';
Axios.get(uri).then((response) => {
this.news = response.data;
});
},
computed: {
filteredNews: function() {
if (this.news.length) {
return this.news;
}
}
}
}
</script>
Topnews.vue
<template id="topnews1">
<div class="col-sm-7">
<div class="cars">
<img :src="topnews.thumb" class="img-responsive" width=100%/>
<div class="name" ><h3>{{ topnews.title }}</h3>
<p>
<br>{{ topnews.info }}<br/>
</p>
</div></div></div>
</template>
<script>
export default {
data:function(){
return {topnews: {title: '', thumb: '', info: ''}}
},
created:function() {
let uri = '/news/'+this.$route.params.id;
Axios.get(uri).then((response) => {
this.topnews = response.data;
});
}
}
</script>
Like GoogleMac said Vue will reuse the same component whenever possible. Since the route for both IDs use the same component Vue will not recreate it, so the created() method is only being called on the first page. You'll need to use the routers beforeRouteUpdate to capture the route change and update the data.
in TopNews.vue:
export default {
data:function(){
return {topnews: {title: '', thumb: '', info: ''}}
},
beforeRouteEnter:function(to, from, next) {
let uri = '/news/'+ to.params.id;
Axios.get(uri).then((response) => {
next(vm => {
vm.setData(response.data)
})
});
},
beforeRouteUpdate: function(to, from, next) {
let uri = '/news/'+ to.params.id;
Axios.get(uri).then((response) => {
this.setData(response.data);
next();
});
},
methods: {
setData(data) {
this.topnews = data
}
}
}
If you click a link referring to the page you are on, nothing will change. Vue Router is smart enough to not make any changes.
My guess is that the IDs are messed up. If you are using Vue devtools you will be able to easily see what data is in each link. Are they as you expect.

Resources