How to transfer post from one component to another? - vue.js

Good afternoon, please tell me. I am training now using Vuex and I cannot transfer the post from one component to another. I have a component Pagination, where all the posts and the history component are stored where and should send the first 5 posts that I click on to visit them. That is, it should work approximately as a history of viewing posts. I wrote some code here, but my posts are not displayed, tell me what I'm doing wrong and how to fix it.
Component code where all posts are stored:
<template>
<div class = "app">
<ul>
<li v-for="(post, index) in paginatedData" class="post" :key="index">
<router-link :to="{ name: 'detail', params: {id: post.id, title: post.title, body: post.body} }" #click="addPostToHistoryComp(post.id, post.title, post.body)">
<img src="src/assets/nature.jpg">
<p class="boldText"> {{ post.title }}</p>
</router-link>
<p> {{ post.body }}</p>
</li>
</ul>
<div class="allpagination">
<button type="button" #click="page -=1" v-if="page > 0" class="prev"><<</button>
<div class="pagin">
<button class="item"
v-for="n in evenPosts"
:key="n.id"
v-bind:class="{'selected': current === n.id}"
#click="page=n-1">{{ n }} </button>
</div>
<button type="button" #click="page +=1" class="next" v-if="page < evenPosts-1">>></button>
</div>
</div>
</template>
<script>
import {mapState} from 'vuex'
export default {
name: 'app',
data () {
return {
current: null,
page: 0,
visiblePostID: '',
}
},
mounted(){
this.$store.dispatch('loadPosts')
},
computed: {
posts(){
return this.$store.state.posts
},
search(){
return this.$store.state.sSearch
},
evenPosts: function(posts){
return Math.ceil(this.posts.length/6);
},
paginatedData() {
const start = this.page * 6;
const end = start + 6;
return this.filteredPosts.slice(start, end);
},
filteredPosts() {
return this.posts.filter((post) => {
return post.title.match(this.search);
});
},
},
methods: {
addPostToHistoryComp(val){
this.$store.dispatch('transforPostToHistoryComp', { // как вызвать actions с объект с параметром
pTitle: val.post.title,
pBody: val.post.body,
pId: val.post.id
})
},
}
}
</script>
The code of the History component where the last 5 posts that were opened should be displayed:
<template>
<div class="history">
<ul>
<li v-for="(historyPost, index) in historyPosts" class="post" :key="index">
<img src="src/assets/nature.jpg">
<p class="boldText"> {{ post.title }}</p>
<p> {{ post.body }}</p>
</li>
</ul>
</div>
</template>
<script>
export default{
computed: {
historyPosts(){
return this.$store.state.historyPosts
},
},
}
</script>
And the code of my story (Vuex):
export default new vuex.Store({
state: {
posts: [],
sSearch: '',
title: '',
body: '',
id: Number,
historyPosts: []
},
actions: {
loadPosts ({commit}) {
axios.get('http://jsonplaceholder.typicode.com/posts').then(response => {
let posts = response.data
commit('SET_POSTS', posts)
}).catch(error => {
console.log(error);
})
},
transforTitleAndBody({commit}, payload){ // мутация которая изменяет сосотаяние в sSearch
const todo = {
title: payload.sTitle,
body: payload.sBody,
id: payload.sId
}
axios.post('http://jsonplaceholder.typicode.com/posts', todo).then(_ => {
commit('ADD_TODO', todo)
}).catch(function (error) {
console.log(error);
})
},
transforPostToHistoryComp({commit}, payload){ // мутация которая изменяет сосотаяние в sSearch
const todohistory = {
title: payload.pTitle,
body: payload.pBody,
id: payload.pId
}
commit('ADD_TODO_HISTORY', todohistory)
}
},
mutations: {
SET_POSTS(state, posts) {
state.posts = posts
},
transforSearch(state, payload){ // мутация которая изменяет сосотаяние в sSearch
state.sSearch = payload
},
ADD_TODO (state, todoObject) {
state.posts.unshift(todoObject)
},
ADD_TODO_HISTORY (state, todohistoryObject) {
state.historyPosts.unshift(todohistoryObject)
},
},
})

I found what happening. You have some erros on code of the file Pagination.vue
You was putting #click under <router-link>, that doesn't work because router link change the page with preventing effect any other event before leave.
I made some changes on template and script. I think will work.
<template>
<div class="app">
<ul>
<template v-for="(post, index) in paginatedData">
<li class="post" :key="index" #click="addPostToHistoryComp(post)">
<img src="src/assets/nature.jpg">
<p class="boldText">{{ post.title }}</p>
<p>{{ post.body }}</p>
</li>
</template>
</ul>
<div class="allpagination">
<button type="button" #click="page -=1" v-if="page > 0" class="prev"><<</button>
<div class="pagin">
<button
class="item"
v-for="n in evenPosts"
:key="n.id"
v-bind:class="{'selected': current === n.id}"
#click="page=n-1"
>{{ n }}</button>
</div>
<button type="button" #click="page +=1" class="next" v-if="page < evenPosts-1">>></button>
</div>
</div>
</template>
<script>
import { mapState } from "vuex";
export default {
name: "app",
data() {
return {
current: null,
page: 0,
visiblePostID: ""
};
},
mounted() {
this.$store.dispatch("loadPosts");
},
computed: {
posts() {
return this.$store.state.posts;
},
search() {
return this.$store.state.sSearch;
},
evenPosts: function(posts) {
return Math.ceil(this.posts.length / 6);
},
paginatedData() {
const start = this.page * 6;
const end = start + 6;
return this.filteredPosts.slice(start, end);
},
filteredPosts() {
return this.posts.filter(post => {
return post.title.match(this.search);
});
}
},
methods: {
addPostToHistoryComp(post) {
this.$store.dispatch("transforPostToHistoryComp", {
pTitle: post.title,
pBody: post.body,
pId: post.id
});
this.$router.push({
name: "detail",
params: { id: post.id, title: post.title, body: post.body }
});
}
}
};
</script>

Related

Vuex / Filter: preserve input value on navigation

Does anyone know how can I select an item from my filteredResults array and display it as the input's value? Furthermore, I need that on page navigation this input value to remain the same if I refresh or navigate back and forth through the pages.
<template>
<div class="search-input relative">
<label class="block">
<span class="text-gray-700 font-bold">{{ label }}</span>
<input
type="search"
class="mt-1 p-2 block min-w-full border-0 focus:border-blue-500 focus:ring focus:ring-blue-500 focus:ring-opacity-50 bg-transparent border-b-2 border-blue-500"
autocomplete="off"
:placeholder="placeholder"
v-model.trim="keyword"
#click="resetAirport"
>
</label>
<ul v-if="filteredResults != null" class="mt-2 absolute shadow-md rounded-lg">
<li v-for="result in filteredResults" :key="result.id" class="p-2 bg-white hover:bg-gray-100" #click="selectAirport(result)">
<NuxtLink to="/" class="block">
{{ result.icao }}, {{ result.iata }} {{ result.name }}
</NuxtLink>
</li>
</ul>
<NuxtLink :to="`/airport/LRAR`" class="text-gray-300 mt-2 text-xs text-right">Details</NuxtLink>
</div>
</template>
<script>
import { defineComponent } from '#vue/composition-api'
import { mapGetters, mapActions } from 'vuex'
export default defineComponent({
setup(props) {},
data() {
return {
keyword: '',
}
},
computed: {
...mapGetters({
getAirports: 'getAirports'
}),
filteredResults() {
if (this.keyword.length < 3) return null
return this.getAirports.filter(item => {
if (!item.icao) return false
return item.icao
.toString()
.toLowerCase()
.includes(this.keyword.toLowerCase())
})
},
},
methods: {
selectAirport: function(airport) {
this.addAirport({airport: airport, type: this.type})
this.keyword = `${airport.icao}, ${airport.iata} (${airport.city}) / ${airport.name}`
},
resetAirport: function() {
if (this.keyword != null) {
this.removeAirport(this.type)
this.keyword = ''
}
},
...mapActions([
'addAirport',
'removeAirport'
])
},
props: {
label: String,
placeholder: String,
type: String,
}
})
</script>
Here is my store:
export const state = () => ({
airports: [],
pairing: {
departure: null,
arrival: null
},
loading: false
})
export const mutations = {
SET_AIRPORTS(state, payload) {
state.airports = payload
},
SET_AIRPORT(state, { airport, type }) {
state.pairing[type] = airport
},
CLEAR_AIRPORT(state, type) {
state.pairing[type] = null
}
}
export const actions = {
addAirport({ commit }, { airport, type }) {
commit('SET_AIRPORT', { airport, type })
},
removeAirport({ commit }, type) {
commit('CLEAR_AIRPORT', type)
}
}

How to pass data on the root components in Vue

How can I pass the page_id to the Sidebar component method highlightNode(), because I want to highlight a newly added item. My current code is the page id is undefined.
This is my code & structure.
my root component is Sidebar.vue
<template>
<div>
<ul>
<li v-for="page in pages">
<div :class="{ 'highlight': highlightedNode == page.id }">
<router-link :to="'/view/' + page.id" #click.native="highlightNode(page.id)">
<span v-title="page.title"></span>
</router-link>
</div>
</li>
</ul>
</div>
</template>
export default {
data () {
return {
pages: [],
highlightedNode: null
}
},
mounted() {
this.getPages()
this.$root.$refs.Sidebar = this
},
methods: {
getPages() {
axios.get('/get-pages').then(response => {
this.pages = response.data
});
},
highlightNode(id) {
this.highlightedNode = id
},
}
}
my add new Page component AddNewPage.vue
<template>
<div>
<div class="main-header">
<div class="page-title">
<input type="text" v-model="page.title" class="form-control">
</div>
</div>
<div class="main-footer text-right">
<button class="btn btn-success btn-sm" #click="saveChanges()">Save and Publish</button>
</div>
</div>
</template>
export default {
data () {
return {
page: {
title: null,
},
}
},
mounted() {
//
},
methods: {
saveChanges() {
axios.post('/store-new-filter', this.page)
.then(response => {
const id = response.data.id // return page id
this.$root.$refs.Sidebar.highlightNode(id) // <-- this line, I want to pass page id to hightlight the newly added page.
})
.catch( error => {
})
},
}
}
Or any alternative way to achieve my expected output.
Thanks in advance.

input field value keeps getting reset #input?

I have created an custom reusable autocomplete component.The issue i am facing is whenever i start to type anything into the fields(#input) the value in the input field gets reset. I feel it has something to do within the code written in the debounce function but i am not sure.Plz help?
main.js
Vue.component('AutoComplete', {
props: ['list','value','title'],
data() {
return {
input: '',
}
},
template: `<template>
<div class="autocomplete">
<input style="font-size: 12pt; height: 36px; width:1800px; " type="text" v-model="input" #input="handleInput"/>
<ul v-if="input" >
<li v-for="(item, i) in list" :key="i" #click="setInput(item)" >
<template v-if="title!='manager'">
<div class="container">
<p>
<b>ID:</b>
{{item.id}}
</p>
<p>
<b>Description:</b>
{{item.description}}
</p>
</div>
</template>
<template v-else>
<div class="container">
<p>
<b>ID:</b>
{{item.id}}
</p>
<p>
<b>First Name:</b>
{{item.firstName}}
</p>
<p>
<b>Last Name:</b>
{{item.lastName}}
</p>
</div>
</template>
</li>
</ul>
</div>
</template>`,
methods: {
handleInput(e) {
console.log('inside handleInput')
this.$emit('input', e.target.value)
},
setInput(value) {
console.log('inside setInput')
this.input = value
this.$emit('click', value)
}
},
watch: {
$props: {
immediate: true,
deep: true,
handler(newValue, oldValue) {
console.log('new value is'+newValue)
console.log('old value is'+oldValue)
console.log('value inside handler'+this.value)
console.log('list inside handler'+this.list)
console.log('title inside handler'+this.title)
this.input=this.value
}
}
}
})
Currently i have called this component from JobDetail.vue page like this-
JobDetail.vue
<template>
<b-field label="Custom Action">
<AutoComplete v-on:input="getAsyncDataAction" v-on:click="(option) => {updateRowValue('records', props.index, 'action', option.id); props.row.action = option.id}" :value="props.row.action" :list="dataAction" title='action' >
</AutoComplete>
</b-field>
</template>
<script>
import { viewMixin } from "../viewMixin.js";
import debounce from "lodash/debounce";
import api from "../store/api";
const ViewName = "JobDetail";
export default {
name: "JobDetail",
mixins: [viewMixin(ViewName)],
data() {
return {
dataAction: [],
isFetching: false
};
},
methods: {
getAsyncDataAction: debounce(function(name) {
if (!name.length) {
this.dataAction = [];
return;
}
this.isFetching = true;
api
.getSearchData(`/action/?query=${name}`)
.then(response => {
this.dataAction = [];
response.forEach(item => {
this.dataAction.push(item);
});
})
.catch(error => {
this.dataAction = [];
throw error;
})
.finally(() => {
this.isFetching = false;
});
}, 500)
}
};
</script>
viewmixin.js
computed: {
viewData() {
return this.$store.getters.getViewData(viewName)
},
objectData() {
return this.$store.getters.getApiData(this.viewData.api_id).data
},
sessionData() {
return this.$store.getters.getSessionData()
},
isLoading() {
return this.$store.getters.getApiData(this.viewData.api_id).isLoading
},
newRecord() {
return this.$route.params.id === null;
}
},
I don't understand why the input fields value keeps resetting #input. Please help and also let me know if this is the correct approach?

How to make pagination?

How to make pagination. I tried many times already, but I can’t do it. I have to paginate without Laravel. The problem is that I can not make a cycle that will display the number of pages, each page should have 10 posts, and there are 98 posts in total. I made the property to be calculated, thanks to which you can find out how many pages there will be. I made a page switch that works. But for some reason, the cycle with which I will display the number of pages does not work for me, I cannot understand what the problem is?
Screenshot
My Vue js:
import axios from 'axios';
export default {
name: 'app',
data () {
return{
counter: 1,
zero: 0,
posts: [],
createTitle: '',
createBody: '',
visiblePostID: '',
}
},
watch: {
counter: function(newValue, oldValue) {
this.getData()
}
},
created(){
this.getData()
},
computed: {
evenPosts: function(posts){
return Math.ceil(this.posts.length/10);
}
},
methods: {
getData() {
axios.get(`http://jsonplaceholder.typicode.com/posts?_start=${this.counter}+${this.zero}&_limit=10`).then(response => {
this.posts = response.data
})
},
// even: function(posts) {
// return Math.ceil(this.posts.length/10)
// },
deleteData(index, id) {
axios.delete('http://jsonplaceholder.typicode.com/posts/' + id)
.then(response => {
console.log('delete')
this.posts.splice(index, 1);
})
.catch(function(error) {
console.log(error)
})
},
addPost() {
axios.post('http://jsonplaceholder.typicode.com/posts/', {
title: this.createTitle,
body: this.createBody
}).then((response) => {
this.posts.unshift(response.data)
})
},
changePost(id, title, body) {
axios.put('http://jsonplaceholder.typicode.com/posts/' + id, {
title: title,
body: body
})
},
},
}
My html:
<div id="app">
<div class="smallfon">
<div class="blocktwitter"><img src="src/assets/twitter.png" class="twitter"/></div>
<div class="addTextPost">Add a post</div>
<input type="text" v-model="createTitle" class="created"/>
<input type="text" v-model="createBody" class="created"/>
<div><button #click="addPost()" class="addPost">AddPost</button></div>
<div class="post1">
<div class="yourPosts">Your Posts</div>
<ul>
<li v-for="(post, index) of posts" class="post">
<p><span class="boldText">Title:</span> {{ post.title }}</p>
<p><span class="boldText">Content:</span> {{ post.body }}</p>
<button #click="deleteData(index, post.id)" class="buttonDelete">Delete</button>
<button #click="visiblePostID = post.id" class="buttonChange">Change</button>
<div v-if="visiblePostID === post.id" class="modalWindow">
<div><input v-model="post.title" class="changePost"><input v-model="post.body" class="changePost"></div>
<button type="button" #click="changePost(post.id, post.title, post.body)" class="apply">To apply</button>
</div>
</li>
</ul>
<button type="button" #click="counter -=1" class="prev">Предыдущая</button>
<!-- <div class="counter">{{ counter }}</div> --> <span v-for="n in evenPosts" :key="n.id">{{ n }} </span>
<button type="button" #click="counter +=1" class="next">Следущая</button>
<!-- <span v-for="n in evenPosts" :key="n.id">{{ n }} </span> -->
</div>
</div>
</div>
If you bind a limit to your fetching request (axios.get(...&_limit=10)), you can't return a paginate count because your computed evenPost property will always return 1 i.e Math.ceil(10/10) == 1
To fix your pagination, remove the parameters query to get the data:
getData() {
axios.get('https://jsonplaceholder.typicode.com/posts').then(response => {
this.posts = response.data
})
}
Then change the default counter page to 0 and add a computed property to return 10 posts based on it:
data () {
return {
counter: 0,
//...
}
},
computed: {
paginatedPosts() {
const start = this.counter * 10;
const end = start + 10;
return this.posts.slice(start, end);
}
}
Now you can iterate on this property:
<ul>
<li v-for="(post, index) of paginatedPosts" class="post">
...
</li>
</ul>
Basic live example

Pagination. How to make moving between pages by clicking on numerals

Tell me how to make it so that when you click on a button from a cycle with page numbers, this particular page opens. Switching along the arrows works for me, but I cannot understand how to switch between pages. I take data from Api. Total posts 98. It is possible to add your posts. On one page only 10 posts are shown.
My html:
<div id="app">
<div class="smallfon">
<div class="blocktwitter"><img src="src/assets/twitter.png" class="twitter"/></div>
<div class="addTextPost">Add a post</div>
<input type="text" v-model="createTitle" class="created"/>
<input type="text" v-model="createBody" class="created"/>
<div><button #click="addPost()" class="addPost">AddPost</button></div>
<div class="post1">
<div class="yourPosts">Your Posts</div>
<ul>
<li v-for="(post, index) of paginatedData" class="post">
<p><span class="boldText">Title:</span> {{ post.title }}</p>
<p><span class="boldText">Content:</span> {{ post.body }}</p>
<button #click="deleteData(index, post.id)" class="buttonDelete">Delete</button>
<button #click="visiblePostID = post.id" class="buttonChange">Change</button>
<div v-if="visiblePostID === post.id" class="modalWindow">
<div><input v-model="post.title" class="changePost"><input v-model="post.body" class="changePost"></div>
<button type="button" #click="changePost(post.id, post.title, post.body)" class="apply">To apply</button>
</div>
</li>
</ul>
<button type="button" #click="page -=1" v-if="page > 0" class="prev"><<</button>
<button class="item"
v-for="n in evenPosts"
:key="n.id"
v-bind:class="{'selected': current === n.id}">{{ n }} </button>
<button type="button" #click="page +=1" class="next" v-if="page < evenPosts-1">>></button>
</div>
</div>
</div>
My js:
export default {
el: "#app",
data () {
return {
current: null,
page: 0,
posts: [],
createTitle: '',
createBody: '',
visiblePostID: '',
}
},
watch: {
counter: function(newValue, oldValue) {
this.getData()
}
},
created(){
this.getData()
},
computed: {
evenPosts: function(posts){
return Math.ceil(this.posts.length/10);
},
paginatedData() {
const start = this.page * 10;
const end = start + 10;
return this.posts.slice(start, end);
}
},
methods: {
setCurrent: function(id) {
this.current = id;
},
getData() {
axios.get(`https://jsonplaceholder.typicode.com/posts`).then(response => {
this.posts = response.data
})
},
deleteData(index, id) {
axios.delete('http://jsonplaceholder.typicode.com/posts/' + id)
.then(response => {
console.log('delete')
this.posts.splice(index, 1);
})
.catch(function(error) {
console.log(error)
})
},
addPost() {
axios.post('http://jsonplaceholder.typicode.com/posts/', {
title: this.createTitle,
body: this.createBody
}).then((response) => {
this.posts.unshift(response.data)
})
},
changePost(id, title, body) {
axios.put('http://jsonplaceholder.typicode.com/posts/' + id, {
title: title,
body: body
})
},
}
}
Screenshot of application
add click event #click="page=n" in button
<button #click="page=n" class="item"
v-for="n in evenPosts"
:key="n.id"
v-bind:class="{'selected': current === n.id}">{{ n }} </button>
Codepen : https://codepen.io/anon/pen/bZOROO