v-for loop only array correspond to the dynamic page number - vue.js

I store results per page number, see below:
<ul v-for="iten in listingsData" :key="item.id">
<li>{{ item.name }}</li>
</ul>
<button #click="pushPrev">Push Prev Results</button>
<button #click="pushNext">Push Next Results</button>
export default {
data(){
return {
listingsData : [],
page : 1
}
},
methods : {
pushNext(){
var _self = this;
axios.get('https://myapi.com/get/users?page='+this.page+1).then(function(response){
_self.page = _self.page + 1;
_self.listingsData = _self.listingsData.push({
page : _self.page,
results : response.data.results
})
});
},
pushPrev(){
var _self = this;
axios.get('https://myapi.com/get/users?page='+this.page-1).then(function(response){
_self.page = _self.page + 1;
_self.listingsData = _self.listingsData.push({
page : _self.page,
results : response.data.results
})
});
}
}
created(){
//load default data
var _self = this;
axios.get('https://myapi.com/get/users?page='+this.page).then(function(response){
_self.listingsData = {
page : 1,
results : response.data.results
}
});
}
}
Now how I can show or loop only results correspond to the the page number this.page?
_self.listingsData = _self.listingsData.push({
page : _self.page, // page number
results : response.data.results
})
What can I try?
I'm using Vue CLI and webpack.

You should iterate over computed property that will return specific page from listingsData, not over all pages.
Something like that:
<template>
<div v-if="currentPage">
<ul v-for="item in currentPage.results" :key="item.id">
<li>{{ item.name }}</li>
</ul>
</div>
<div v-else><i>Loading...</i></div>
<button #click="fetchPage(-1)" :disabled="page===1">Prev Results</button>
<button #click="fetchPage(1)" :disabled="page===10">Next Results</button>
</template>
<script>
const api = "https://myapi.com/get/users";
export default {
data() {
return {
page: 1,
listingsData: [],
};
},
created() {
this.fetchPage(0);
},
computed: {
currentPage() {
return this.listingsData.find(i => i.page === this.page);
},
},
methods: {
fetchPage(diff) {
this.page += diff;
if (this.currentPage) {
return; // page already loaded
}
const page = this.page;
axios.get(api, { params: { page } })
.then((res) => {
this.listingsData.push({
page,
results: res.data.results,
});
});
},
}
};
</script>
Here, we're loading page only if it hasn't been loaded before, and disable Prev/Next buttons if current page is 1/10 respectively.
Here is jsfiddle (with mockup data instead of actual API calls).

Related

How to Add javascript paggination code in Vue js component

I'm trying to add pagination code in the Vue component and I've tried to add the code in the mounted hook to call the function but it doesn't work. I want to load the code after component loaded completely.Also, jQuery code doesn't load in Vue component. Do I need to change my code to pure javascript for that. Can you guide me how to fix the issue?
// Create a root instance for each block
var vueElements = document.getElementsByClassName('search-bento-block');
var count = vueElements.length;
const store = new Vuex.Store({
state: {
query: drupalSettings.bento.query ? drupalSettings.bento.query : '',
bentoComponents: []
},
mutations: {
add (state, payload) {
state.bentoComponents.push(payload)
}
},
getters: {
getComponents: state => {
return state.bentoComponents
}
}
})
// Loop through each block
for (var i = 0; i < count; i++) {
Vue.component('results', {
template: `
<div v-if="results && results.length > 0">
<div v-for="result in results">
<div class="search-result-item">
<div class="image-holder">
<img src="https://images.unsplash.com/photo-1517836477839-7072aaa8b121?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=750&q=80">
</div>
<div class="container-content">
<a v-bind:href="result.url">
<h3 v-html="result.title"></h3>
</a>
<p>Subjects: <span v-html="result.subjects"></span></p>
</div>
</div>
</div>
</div>
<div v-else>
<p>No results found.</p>
</div>
`,
props: ['results'],
})
new Vue({
el: vueElements[i],
store,
data: {
message: 'Hello There!',
results: [],
total: 0,
bentoSettings: [],
},
methods: {
addComponentToStore: function (type) {
this.$store.commit('add', type);
console.log("test");
console.log(this.results.length);
}
},
mounted: function() {
// console.log(this.$route.query.bentoq);
const id = this.$el.id;
this.bentoSettings = drupalSettings.pdb.configuration[id];
var bentoConfig = drupalSettings.pdb.configuration[id].clients[this.bentoSettings.bento_type] ? drupalSettings.pdb.configuration[id].clients[this.bentoSettings.bento_type].settings : [];
axios
.get('/api/search/' + this.bentoSettings.bento_type, {
params: {
query: this.$store.state.query,
plugin_id: this.bentoSettings.bento_type,
bento_limit: this.bentoSettings.bento_limit,
bento_config: bentoConfig,
}
})
.then(response => {
console.log(response);
this.results = response.data.records;
this.total = response.data.total;
this.addComponentToStore({
title: this.bentoSettings.example_field,
count: this.total
});
})
.catch(error => {
console.log(error.response);
})
}
});
}
// I'm trying to call following function in Vue component.
function baseThemePagination1() {
//Pagination
pageSize = 3;
var pageCount = $('.line-content').length / pageSize;
for (var i = 0; i < pageCount; i++) {
$('#pagin').append('<li><a href=\'#\'>' + (i + 1) + '</a></li> ');
}
$('#pagin li').first().find('a').addClass('current')
showPage = function(page) {
$('.line-content').hide();
$('.line-content').each(function(n) {
if (n >= pageSize * (page - 1) && n < pageSize * page)
$(this).show();
});
}
showPage(1);
$('#pagin li a').click(function() {
$('#pagin li a').removeClass('current');
$(this).addClass('current');
showPage(parseInt($(this).text()))
});
}
What you are trying to do is not the recommended way to use vue, direct DOM manipulation is one of the things that vue is made to avoid (although can be done). The Vue way would be to bind the value you want to a variable with v-model assuming it is an input and then create your pagination based on that.
If you insist on DOM manipulation then try ref="line-content" and then call it like so:
this.refs.line-content.
In terms of reacting to a page change click simply use a method in your methods section there is no reason to use jQuery for that.
See here for a simple explanation:
https://medium.com/#denny.headrick/pagination-in-vue-js-4bfce47e573b

Vue.js / How to access the function in another component

I just started learning Vue.js. I need to call the function in another component in my project. When I add new data to the table with createAnnouncement.vue, I want to go into announcement.vue and call the queryAnnouncement function. How can I do that? I would appreciate if you could help, please explain with a sample. Or edit my codes.
Announcement.Vue template:
<template>
// more div or not important template code
<div class="dataTables_filter" style="margin-bottom:10px">
<label>
<input class="form-control form-control-sm" placeholder="Search" aria-controls="m_table_1" type="search" v-model="searchField" #change="filter()">
</label>
<a style="float:right" href="#" data-target="#create-announcement-modal" data-toggle="modal" class="btn btn-primary">
<i class="">Add</i>
</a>
</div>
// more div or not important template code
</template>
Announcement.Vue Script Code:
<script>
import toastr from "toastr";
export default {
name: 'announcement',
data() {
return {
announcements: [],
searchField: "",
deleteCsrfToken: this.$root.csrfTokens["deleteAnnouncement"]
}
},
beforeMount: async function () {
await this.queryAnnouncements();
},
methods: {
filter: async function () {
await this.queryAnnouncements(this.searchField);
},
queryAnnouncements: async function (filter, pageSize, pageIndex, sortBy, sortType) {
var data = {
"query[general-filter]": filter,
"pagination[perpage]": !!pageSize ? pageSize : 10,
"pagination[page]": !!pageIndex ? pageIndex : 1,
"sort[field]": sortType,
"sort[sort]": !!sortBy ? sortBy : "asc"
};
let response = await axios
.get("/Announcement/QueryAnnouncements", { params: data })
this.announcements = response.data.data;
},
}
}
createAnnouncement.vue code:
<template>
<button #click="createAnnouncement()" v-bind:disabled="contentDetail === ''" class="btn btn-success">
Save</button>
//not important template codes
<template>
<script>
import toastr from "toastr";
export default {
name: 'create-announcement',
data() {
return {
contentDetail: "",
createCsrfToken: this.$root.csrfTokens["createAnnouncement"],
}
},
methods: {
createAnnouncement: async function () {
var self = this;
var data = {
content: this.contentDetail,
};
let response = await axios
.post("/Announcement/createAnnouncement",
data,
{
headers: {
RequestVerificationToken: self.createCsrfToken
}
})
if (response.status == 200) {
$("#create-announcement-modal .close").click()
$("#create-announcement-form").trigger('reset');
toastr["success"]("Kayıt başarıyla eklendi!", "Başarılı!");
self.contentDetail = "";
}
else {
toastr["warning"]("Hata", "Kayıt Eklenemedi.");
}
}
},
}
</script>
Please show with sample or arrangement, my english is not very good. Thanks.

Vue.js data: undefined

I am new to Vue.js.
Please advice me.
I get comments: undefined so comments are not displaying.
xhr is normal with 200.
Thank you
Thank you
Thank you
Thank you
Thank you
<template>
<div>
<ul class="media-list">
<li class="media" v-for="comment in comments">
{{ $comment.body }}
</li>
</ul>
</div>
</template>
<script>
export default {
data () {
return {
comments: []
}
},
props: {
postid: null
},
methods: {
getComments () {
this.$http.get('/blog/' + this.postid + '/comments').then((response) => {
this.comments = response.json().data;
});
}
},
mounted () {
this.getComments();
}
}
Basically there are two problems:
$comment don't exist
You have no data on response.json().data, that's why you get a undefined
I used a different API just to test it (as I don't have access to yours).
TEMPLATE
<div id="app">
<ul class="media-list">
<li class="media" v-for="comment in comments">
{{ comment.familyName + ', ' + comment.givenName }}
</li>
</ul>
</div>
SCRIPT
new Vue({
el: '#app',
data () {
return {
comments: []
}
},
props: {
postid: null
},
methods: {
getComments () {
this.$http.get('//ergast.com/api/f1/drivers.json').then((response) => {
this.comments = response.body.MRData.DriverTable.Drivers;
});
}
},
mounted () {
this.getComments();
}
});
Check out a working example here
this.comments = response.json().data;
console.log(this.comments) ;
to see what you get ;
you define comments=Array ;
maybe you get the response.json().data is not a Array;
Try using vm instead of this. In API response make sure what you are getting using console.log(). If response is already in json do not use response.json(). In HTML change $comment.body to comment.body. Make sure you have the body key in comments[] array.
<template>
<div>
<ul class="media-list">
<li class="media" v-for="comment in comments">
{{ comment.body }}
</li>
</ul>
</div>
</template>
<script>
export default {
data () {
return {
comments: [],
postid: null
}
},
props: {
},
methods: {
getComments () {
let vm = this;
vm.$http.get('/blog/' + vm.postid +
'/comments').then((response) => {
console.log(response)
vm.comments = response.data;
});
}
},
mounted () {
let vm = this;
vm.getComments();
}
}
}
:
My suggestion is to properly use try-catch statements.
I have found this is the safest and proper way to manage cases where variable could take undefined or null values, instead of trying to "if" everything.
try {
val = localStorage.getItem('accesstoken')
} catch (error) {
alert(error)
}
Take care!

VueJS: State from deleted component remains and affects the next one (sibling)

I have a notifications component that has some notifications item child components that are fed from an array in the parent component. The child component has the ability to update and delete itself. It can mark itself as read when clicked. It will make a request to the server with Axios and then change a button icon to close (fa-close). Which works fine. Now it can delete itself. When clicked it will send a delete request to the server, and when successful emit an event to the parent component to delete it from the array with splice. Now it works fine but the issue I'm having is that the new icon that I changed still remains for the next component (next item in the array). And that bugs me because I can't seem to find a way to make it display the initial icon which was initialize with the component. here's some code if that can help NotificationsItem.vue <template>
<li class="list-group-item list-group-item-info">
<button class="pull-right"
title="#lang('text.notifications.markAsRead')"
#click="markAsReadOrDestroy">
<i class="fa" :class="iconClass" v-show="!loading"></i>
<i class="fa fa-spinner fa-spin fa-lg fa-fw" v-show="loading"></i>
</button>
<!-- {{ notification.data }} -->
I'm the index {{ index}} and the ID is {{notification.id}}
<span class="hljs-tag"></<span class="hljs-name">li</span>></span>
</template>
<script>
export default {
props: ['notification', 'index'],
data() {
return {
loading: false,
icon: 'check',
markedAsRead: false,
}
},
computed: {
iconClass() {
return 'fa-' + this.icon;
}
},
methods: {
markAsReadOrDestroy() {
if (this.markedAsRead) {
this.destroy();
} else {
this.markAsRead();
}
},
markAsRead() {
let vm = this;
this.loading = true;
this.$http.patch('/notifications/markasread/' + this.notification.id)
.then(function(response) {
console.log(response);
vm.loading = false
vm.markedAsRead = true
vm.icon = 'close'
})
.catch(function(error) {
console.log(error);
vm.loading = false;
});
},
destroy() {
let vm = this;
this.loading = true;
this.$http.delete('/notifications/' + this.notification.id)
.then(function(response) {
console.log(response);
vm.loading = false;
vm.$emit('deleted', vm.index);
})
.catch(function(error) {
console.log(error);
vm.loading = false;
});
}
},
mounted() {
console.log('Notifications Item mounted.')
}
}
</script>
NotificationsList.vue <template>
<div class="list-group">
<notifications-item
v-for="(notification, index) in notifications"
:notification="notification"
#deleted="remove"
:index="index">
{{ notification.data['text'] }}
</notifications-item>
</div>
</template>
<script>
export default {
data() {
return {
notifications: notifications.data,
}
},
methods: {
remove(index) {
console.log(index);
this.notifications.splice(index, 1);
}
},
mounted() {
console.log('Notifications List mounted.')
}
}
</script>
If anyone can help me that would be greatly appreciated.
You need to pass index as paramter in remove function, like following:
<notifications-item
v-for="(notification, index) in notifications"
:notification="notification"
#deleted="remove(index)"
:index="index">
{{ notification.data['text'] }}
</notifications-item>
I found a fix by adding a key attribute on the child component with a unique value (the notification id). And that's it.

Vuejs reactive v-if template component

I'm struggling to understand how to make my component reactive. At the moment the button is rendered correctly but once the create/delete event happens, the template does not change. Any tips on how to update the component after the event has taken place?
new Vue({
el: '#app'
});
Vue.component('favourite-button', {
props: ['id', 'favourites'],
template: '<input class="hidden" type="input" name="_method" value="{{ id }}" v-model="form.listings_id"></input><button v-if="isFavourite == true" class="favourited" #click="delete(favourite)" :disabled="form.busy"><i class="fa fa-heart" aria-hidden="true"></i><button class="not-favourited" v-else #click="create(favourite)" :disabled="form.busy"><i class="fa fa-heart" aria-hidden="true"></i></button><pre>{{ isFavourite == true }}</pre>',
data: function() {
return {
form: new SparkForm({
listings_id: ''
}),
};
},
created() {
this.getFavourites();
},
computed: {
isFavourite: function() {
for (var i = 0; this.favourites.length; i++)
{
if (this.favourites[i].listings_id == this.id) {
return true;
}
}
},
},
methods: {
getFavourites() {
this.$http.get('/api/favourites')
.then(response => {
this.favourites = response.data;
});
},
create() {
Spark.post('/api/favourite', this.form)
.then(favourite => {
this.favourite.push(favourite);
this.form.id = '';
});
},
delete(favourite) {
this.$http.delete('/api/favourite/' + this.id);
this.form.id = '';
}
}
});
Vue.component('listings', {
template: '#listing-template',
data: function() {
return {
listings: [], favourites: [],
};
},
created() {
this.getListings();
},
methods: {
getListings() {
this.$http.get('/api/listings')
.then(response => {
this.listings = response.data;
});
}
}
});
Vue expects HTML template markups to be perfect. Otherwise you will run into multiple issues.
I just inspected your template and found an issue - the first <button> element does not close.
Here is the updated version of your code:
Vue.component('favourite-button', {
props: ['id', 'favourites'],
template: `
<input class="hidden" type="input" name="_method" value="{{ id }}" v-model="form.listings_id"></input>
<button v-if="isFavourite == true" class="favourited" #click="delete(favourite)" :disabled="form.busy">
<i class="fa fa-heart" aria-hidden="true"></i>
</button> <!-- This is missing in your version -->
<button class="not-favourited" v-else #click="create(favourite)" :disabled="form.busy">
<i class="fa fa-heart" aria-hidden="true"></i>
</button>
<pre>{{ isFavourite == true }}</pre>
`,
...
Note the comment on 7th line above, the closing </button> tag is not present in your template.
As a side note, if you do not want to type back-slash at the end of every line to make multi-line template strings, you can use back-ticks as shown in my code example above. This will help you avoid markup errors leading to Vue component issues and many hours of debugging.
Another reference: Check out "Multi-line Strings" in this page: https://developers.google.com/web/updates/2015/01/ES6-Template-Strings
Relevant lines (copied from above page):
Any whitespace inside of the backtick syntax will also be considered part of the string.
console.log(`string text line 1
string text line 2`);
EDIT: Found a possible bug in code
Here is another issue in your create method of favourite-button component:
methods: {
// ...
create() {
Spark.post('/api/favourite', this.form)
.then(favourite => {
this.favourite.push(favourite); // Note: This is the problem area
this.form.id = '';
});
},
//...
}
Your success handler refers to this.favourite.push(...). You do not have this.favourite in data or props of your component. Shouldn't it be this.favourites?