I need to make a pagination in my task, but it is not working.
I made two buttons to which I attached the "click" event and I registered a property in the "data". When I click on the buttons, the property changes and is written to the link and in the same way changes the current 10 posts to the following.
But for some reason it does not work as it should work. Can someone please explain what my mistake is and if you can suggest some articles on the subject of "pagination".
This is my html:
<button type="button" #click="counter -=1" class="prev">Prev</button>
<div class="counter">{{ counter }}</div>
<button type="button" #click="counter +=1" class="next">Next</button>
This is my Vue:
export default {
name: 'app',
data () {
return {
counter: 1,
zero: 0,
posts: [],
createTitle: '',
createBody: '',
visiblePostID: ''
};
},
created () {
axios.get('http://jsonplaceholder.typicode.com/posts?_start=${this.counter}+${this.zero}&_limit=10').then(response => {
this.posts = response.data;
});
}
};
The created method is called only when the component is created. To make the GET request everytime the counter increase or decrease use watches link.
Your example will become:
export default {
name: 'app',
data () {
return {
counter: 1,
zero: 0,
posts: [],
createTitle: '',
createBody: '',
visiblePostID: '',
}
},
watch: {
counter: function(newValue, oldValue) {
this.getData()
}
}
created(){
this.getData()
},
methods: {
getData() {
axios.get(`http://jsonplaceholder.typicode.com/posts?_start=${this.counter}+${this.zero}&_limit=10`).then(response => {
this.posts = response.data
})
}
}
}
You need to create a watcher for your counter that fires a load method. This way every time your counter changes you'll load in the correct posts for the page in your paginated results.
export default {
name: 'app',
data () {
return{
counter: 1,
...
}
},
created(){
this.loadPosts()
},
watch: {
counter(newVal, oldVal){
this.loadPosts()
}
},
methods: {
loadPosts(){
axios.get('http://jsonplaceholder.typicode.com/posts?_start=${this.counter}+${this.zero}&_limit=10')
.then(response => {
this.posts = response.data
})
}
}
}
Maybe this can help you. https://scotch.io/courses/getting-started-with-vue/vue-events-build-a-counter
I don't know vue, but looks like you need a function to load new data
Related
This question is very similar to This question
I have set up a Vue page with Laravel and showing all posts with a help of a GET request. I am also listening to a Laravel ECHO event and unshifting the value to the all posts array making it appear on top.
I have set up the infinite scroll and paginating 5 results per page using this package. Results appear on the page and pushing to the array from the listener also works. However, when infinite scroll loads the 2nd results page, the 6th result is duplicated.
The aforementioned package accepts next_cursor an offset value as the parameter instead of page=2 so it exactly loads the value without any duplications.
Controller.php
public function pusherGet(Request $request) {
$jobs = Job::orderBy('id','desc')->cursorPaginate();
return response()->json($jobs);
}
Vue file
<template>
<div>
<h3 class="text-center">All Jobs</h3><br/>
<div class="container">
<div class="card" v-for="(job,index) in jobs" :key="index">
<div class="card-body">
<h4 class="card-title">{{ job.id }}</h4>
<p class="card-text">{{ job.request_type}}</p>
</div>
</div>
</div>
<infinite-loading #infinite="getJob"></infinite-loading>
</div>
</template>
<script>
export default {
data() {
return {
page:1,
jobs: [],
}
},
mounted() {
this.listenNewJobs();
},
created() {
},
methods: {
listenNewJobs() {
Echo.channel('chat-room.1')
.listen('JobCreated', (e) => {
console.log(e);
this.jobs.unshift(e.job);
});
},
getJob($state) {
axios.get('getjobs', {
params: {
page: this.page,
},
}).then(({data})=> {
console.log(data)
if(data.data.length) {
this.page += 1;
this.jobs.push(...data.data)
$state.loaded();
} else {
$state.complete();
}
});
}
}
}
</script>
Results Json
{
data: Array(5), path: "getjobs?page=1", previous_cursor: "100", next_cursor: "96", per_page: 5, …}
data: (5) [{…}, {…}, {…}, {…}, {…}]
next_cursor: "96" // this is the parameter which i should attach to the GET request to paginate correct results
next_page_url: "getjobs?page=1&next_cursor=96"
path: "getjobs?page=1"
per_page: 5
prev_page_url: "getjobs?page=1&previous_cursor=100"
previous_cursor: "100"
__proto__: Object
Any help would be greatly appreciated.
Edit : How to Set the URL for the GET request to paginate the results from the GET request response for paginated results to avoid 2nd page result duplication ?
Try the following:
<script>
export default {
data() {
return {
jobs: [],
isInitialLoaded: false,
currentPage: 1,
lastPage: 0,
}
},
mounted() {
this.listenNewJobs();
},
created() {
//
},
methods: {
listenNewJobs() {
Echo.channel('chat-room.1')
.listen('JobCreated', (e) => {
console.log(e);
this.jobs.unshift(e.job);
});
},
async getJob($state){
await this.fetchData().then((response) => {
this.lastPage = response.data.last_page;
if (response.data.data.length > 0) {
response.data.data.forEach((item) => {
const exists = this.jobs.find((job) => job.id == item.id);
if (!exists) {
// this.jobs.unshift(item); // Add to front of array
this.jobs.push(item);
}
});
if (this.currentPage - 1 === this.lastPage) {
this.currentPage = 2;
$state.complete();
} else {
this.currentPage += 1;
}
$state.loaded();
} else {
this.currentPage = 2;
$state.complete();
}
});
this.isInitialLoaded = true;
},
fetchData() {
const url = this.isInitialLoaded ? `/getjobs?page=${this.currentPage}` : `/getjobs`;
return axios.get(url);
},
}
}
</script>
The awesome tiptap wrapper for prosemirror comes with nice documentation but it lacks some clarification how to approach some (i think) basic scenarios when developing custom extensions.
My question is how to invoke toggleWrap on the node when in vue component's context.
I found example that uses transactions and allows for delete - but what i want is to clear the node leaving the text of node intact.
get view() {
return {
directives: {
"click-outside": clickOutside
},
props: ['node', 'updateAttrs', 'view', 'selected', 'getPos'],
data() {
return {
showMenu: false
}
},
computed: {
href: {
get() {
return this.node.attrs.href
},
set(href) {
this.updateAttrs({
href,
})
},
},
},
methods: {
// deleteNode() {
// let transaction = this.view.state.tr // tr - transaction
// let pos = this.getPos()
// transaction.delete(pos, pos + this.node.nodeSize)
// this.view.dispatch(transaction)
// },
stopLinkPropagation(){
return null;
},
hideMenu(){
this.showMenu = false
}
},
template: `<div #click="showMenu = true" v-click-outside="hideMenu">
<a class="email-button" #click.prevent="stopLinkPropagation" :href="href" v-text="node.textContent"></a>
<input class="iframe__input" type="text" v-model="href" v-if="showMenu" />
<button #click="clearNode">clear button wrap</button>
</div>`,
}
}
Any help would be awesome. Thanks.
I am using Vue.js 2.6 with the vue-router component. I have a search form as follows:
<form class="search-form" #submit.prevent="search">
<div class="form-group">
<input type="text" class="form-control" v-model="term" placeholder="Search">
</div>
</form>
And here is my script:
<script>
export default {
data() {
return {
term: this.$route.query.term,
items: []
}
},
created() {
if (this.term != null) {
this.search()
}
},
watch: {
'$route.query.term'() {
this.term = this.$route.query.term
this.search()
}
},
methods: {
search: function () {
window.axios.get('/images/search', {
params: {
term: this.term
}
})
.then(response => {
this.$router.push({query: { 'term' : this.term}})
this.items = response.data.collection.items
})
.catch(error => {
return error
})
}
}
}
</script>
What I am trying to achieve with this code is the following:
User submits form, the search() function is called. The URL is updated with the query param, e.g. /search?term=<term>. This is working but the search() function is being called twice.
User carries out several searches, then presses the back button. The search field in the form is updated and the search is carried out. This is working but the search() function is being called twice.
User manually enters query param in the URL bar. The search field in the form is populated and the search is carried out. This is working.
Where the search() function is being called twice, this is due to the watch() function, which is designed to watch changes to the URL bar. I am not sure how to combine this function correctly with the search() function.
In watch, you can compare new value with old value, and only perform search when new value is different with old value
watch: {
'$route.query.term'(newVal, oldVal) {
if (newVal != oldVal) {
this.term = this.$route.query.term
this.search()
}
}
},
To make it call only 1 for 1st case, you might want to separate button click handler with real search call
<script>
export default {
data() {
return {
term: this.$route.query.term,
items: []
}
},
created() {
if (this.term != null) {
this.performSearch()
}
},
watch: {
'$route.query.term': {
handler: function(newVal, oldVal) {
if (newVal != oldVal) {
this.term = this.$route.query.term
this.performSearch()
}
},
immediate: true
}
},
methods: {
search: function () {
// this is call when user click search Button
this.$router.push({query: { 'term' : this.term}})
},
performSearch() {
// perform actual searcch
window.axios.get('/images/search', {
params: {
term: this.term
}
})
.then(response => {
this.items = response.data.collection.items
})
.catch(error => {
return error
})
}
}
}
</script>
I'm new to this, please be kind!
How do I transfer the value of the object that was returned to me in the console to the webpage? As of now, the balance value is in the console but it is not displayed on the page.
edit: If I wish to display the objects in the console separately, do I use myObj.key? eg. I want to display the value of balance on the left and the value of block on the right of my webpage, do I use myObj.balance and myObj.block ?
attached a screenshot of my browser
This is my code, do guide me, thank you!
<template>
<div class="box-card">
<p class="title-text">余额</p>
<p class="number-text">{{Balance}}</p>
</div>
</template>
<script>
export default {
data() {
return {
userId: 0,
// page config
currentPage: 1,
total: 0,
pageSize: 20,
userBalance: [],
Balance: '',
}
},
watch: {},
mounted() {
this.userId = this.$route.query["user_id"];
this.userId = 41;
this.getUserBalance();
this.getUserIncomeRecord();
console.log('hello');
},
methods: {
pageChange(val) {
this.currentPage = val;
},
getUserBalance() {
Core.Api.User.getUserBalance(this.userId).then(res => {
console.log(res);
res == this.Balance;
})
},
</script>
EDITED: If you want to print in a element with certain ID instead of console.log("WHAT YOU WANT TO PRINT") use this:
document.getlementById("YOUR ELEMENT ID HERE").innerHtml("WHAT YOU WANT TO PRINT");
If you use Jquery this is equivalent to the above code:
$("#ELEMENT ID HERE").html("WHAT YOU WANT TO PRINT");
make a slight change:
getUserBalance() {
Core.Api.User.getUserBalance(this.userId).then(res => {
console.log(res);
this.Balance = res;
})
},
So I'm retrieving my data from my api using vue-resource which is happening correctly, the state is updated and from the console I am able to see the values I'm requesting. My problem is that when the application loads the data from the store doesn't seem to be impacting the application on load, but if for example I change between pages the information is displayed correctly. This is leading me to believe somewhere along the way I have gotten the life cycle hooks incorrect, or I have handled the state incorrectly inside vuex.
Vuex store
import Vue from 'vue'
import Vuex from 'vuex'
import VueResource from 'vue-resource'
Vue.use(VueResource)
Vue.use(Vuex)
const state = {
twitter: 0,
instagram: 0,
youtube: 0,
twitch: 0
}
const actions = {
LOAD_METRICS: ({commit}) => {
Vue.http.get('http://109.74.195.166:2000/metrics').then(response => {
let out = [{
twitter: Number(response.body[0].twitter),
instagram: Number(response.body[0].instagram),
youtube: Number(response.body[0].youtube),
twitch: Number(response.body[0].twitch)
}]
commit('SET_METRICS', out)
}).catch((e) => {
console.log(e)
})
}
}
const mutations = {
SET_METRICS (state, obj) {
state.twitter = obj[0].twitter
state.instagram = obj[0].instagram
state.youtube = obj[0].youtube
state.twitch = obj[0].twitch
}
}
const getters = {}
export default new Vuex.Store({
state,
getters,
actions,
mutations
})
Here I am trying to dispatch an event to gather the needed information using a mutation.
<template>
<div id="app">
<NavigationTop></NavigationTop>
<router-view></router-view>
<SocialBar></SocialBar>
<CopyrightBar></CopyrightBar>
</div>
</template>
<script>
export default {
name: 'app',
ready: function () {
this.$store.dispatch('LOAD_METRICS')
}
}
</script>
<style>
#import url('https://fonts.googleapis.com/css?family=Roboto:400,700,900');
#app {
font-family: 'Roboto', sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: white;
background: url('./assets/Images/bodyBackground.jpg');
}
</style>
Then finally I am requesting the information inside of the component to be used by countup.js and also giving it to the method inside data.
<template>
<div class="hero">
<div class="container hero-content">
<div class="row hero-back align-items-end">
<div class="col-lg-6">
<div class="row">
<div class="col-lg-6" v-for="icons in socialIcons">
<Hero-Tile
:name="icons.name"
:icon="icons.iconName"
:count="icons.count"
:numeric="icons.numeric"
></Hero-Tile>
<h1>{{origin}}</h1>
</div>
</div>
</div>
</div>
</div>
<div class="diagonal-left-lines"></div>
<div class="home-hero-img"><img class="img-fluid" src="../../assets/Images/home-hero.jpg"></div>
</div>
</template>
<script>
import HeroTile from './Hero-Tile'
import CountUp from 'countup.js'
export default {
components: {HeroTile},
name: 'hero',
data () {
return {
origin: '',
socialIcons: [
{
name: 'twitter',
iconName: 'twitter',
count: this.$store.state.twitter,
numeric: 26000
},
{
name: 'instagram',
iconName: 'instagram',
count: this.$store.state.instagram,
numeric: 35000
},
{
name: 'youtube',
iconName: 'youtube-play',
count: this.$store.state.youtube,
numeric: 15000
},
{
name: 'twitch',
iconName: 'twitch',
count: this.$store.state.twitch,
numeric: 127000
}
]
}
},
methods: {
updateNumbers: function () {
let options = {
useEasing: true,
useGrouping: true,
separator: ',',
decimal: '.',
prefix: '',
suffix: 'K'
}
function kFormatter (num) {
return num > 999 ? (num / 1000).toFixed(1) : num
}
let twitter = new CountUp('twitter', 0, kFormatter(this.$store.state.twitter), 0, 3, options)
let instagram = new CountUp('instagram', 0, kFormatter(this.$store.state.instagram), 0, 3, options)
let youtube = new CountUp('youtube', 0, kFormatter(this.$store.state.youtube), 0, 3, options)
let twitch = new CountUp('twitch', 0, kFormatter(this.$store.state.twitch), 0, 3, options)
twitter.start()
instagram.start()
youtube.start()
twitch.start()
}
},
mounted: function () {
this.updateNumbers()
}
}
</script>
To be clear at the moment it seems to just load '0k' so it's as if there is some form of race condition occurring causing it not to actually load the information on load-up. Though I'm not sure what the correct approach is here.
This was eventually solved by what I'm going to describe as hacking as I don't actually know the exact correct answer at this time. Though what I have does work.
Points of Interest below:
Store
LOAD_METRICS: ({commit}, context) => {
console.log(context)
if (context === true) {
return new Promise((resolve) => {
resolve('loaded')
})
}
return new Promise((resolve) => {
Vue.http.get('real ip is normally here').then(response => {
let out = {
twitter: Number(response.body[0].twitter),
instagram: Number(response.body[0].instagram),
youtube: Number(response.body[0].youtube),
twitch: Number(response.body[0].twitch),
loaded: false
}
commit('SET_METRICS', out)
resolve(out)
}).catch((e) => {
console.log(e)
})
})
}
In the above I am now sending an instance of the current store.state.metrics.loaded when the dispatch event is sent. Which is then checked to see the truthness of the current value, Because the first load should always return false we then return a promise utilizing an API call while also mutating the store so we have the values from later. Thus onwards because we mutated the loaded event to be true the next further instances shall return a value of true and a new promise will be resolved so we can make sure the .then() handler is present.
Component
created: function () {
this.$store.dispatch('LOAD_METRICS', this.$store.state.metrics.loaded).then((res) => {
if (res !== 'loaded') {
this.updateNumbers(res)
} else {
this.socialIcons[0].count = this.kFormatter(this.$store.state.metrics.twitter) + 'K'
this.socialIcons[1].count = this.kFormatter(this.$store.state.metrics.instagram) + 'K'
this.socialIcons[2].count = this.kFormatter(this.$store.state.metrics.youtube) + 'K'
this.socialIcons[3].count = this.kFormatter(this.$store.state.metrics.twitch) + 'K'
}
})
}
Within our component created life cycle hook we then use the resulting values to identify the path to be taken when the components are created within the DOM again, this time just loading the values and allow normal data binding to update the DOM.
I believe there is a better method of approach then deliberating the logic of the state within the action setter and returning a promise that is essentially redundant other than for ensuring the .then() handle is present.