This is my first time using vuex so I am looking for some help. I have a products component and I would like to create a product component which will display the active products details. I think that I am nearly there but I am now stuck.
in my store I am setting
activeProduct: null, in the state and I have a mutation
mutations: {
setActiveProduct(state, product) {
state.activeProduct = product
}
}
then my products page (results.vue)
<template>
<div class="results">
<h1>Results</h1>
<ul>
<li v-for="product in products" :key="product.Vehicle.id">
<img v-bind:src="product.Vehicle.Url">
<div>
<h3>
{{ product.Vehicle.Manufacturer }}
{{ product.Vehicle.Model }}
</h3>
<h3>
<router-link
:to="'./Vehicle/'+product.Vehicle.id"
#input="setActiveProduct"
:value="$store.state.activeProduct"
><button>More</button></router-link>
</h3>
</div>
</li>
</ul>
</div>
</template>
<script>
import { mapState, mapMutations } from 'vuex';
export default {
name: 'Results',
data() {
return {};
},
computed: {
...mapState([
'products',
'activeProduct'
])
},
methods: {
...mapMutations([
'setActiveProduct'
]),
setActiveProduct(val) {
this.$store.commit('setActiveProduct', val)
}
}
};
</script>
<style scoped lang="scss">
ul {
list-style-type: none;
padding: 0;
}
</style>
and my product page (vehicle.vue)
<template>
<div class="Vehicle">
<h1>Vehicle</h1>
<pre>store.$state.activeProduct: {{ $store.state.activeProduct }}</pre>
<!-- {{ activeProduct.Vehicle.Manufacturer }}
{{ activeProduct.Vehicle.Model }} -->
</div>
</template>
<script>
import { mapState, mapMutations } from 'vuex'
export default {
name: 'Vehicle',
data() {
return {
product: this.$store.getters.productById(this.$route.params['id'])
}
},
computed: {
...mapState([
'products',
'activeProduct'
]),
store() {
return this.$store.state
}
}
}
</script>
Any help getting this to work would be great! Thank you
i used a getter instead of a mutation to solve this problem
Related
I am working on a small Todo App with Vue 3.
In App.vue I have the 3 child components and the methods:
<template>
<div id="app">
<Header/>
<List/>
<Footer/>
</div>
</template>
<script>
import axios from 'axios'
import Header from './components/Header.vue'
import List from './components/List.vue'
import Footer from './components/Footer.vue'
export default {
name: 'App',
components: {
Header,
List,
Footer
},
props: ['todos'],
data() {
return {
url: "https://jsonplaceholder.typicode.com/todos?&_limit=5",
dataIsLoaded: false,
isValidInput: true,
todos: [],
unsolvedTodos: [],
newTitle: "",
}
}
}
</script>
In List.vue I have:
<template>
<div>
<h1>List</h1>
<ul class="todo-list" v-if="dataIsLoaded">
<TodoItem v-for="todo in todos.slice().reverse()" :key="todo.id" v-bind:class="{done: todo.completed}" :todo="todo" />
</ul>
</div>
</template>
<script>
import TodoItem from "./TodoItem.vue";
export default {
name: 'List',
components: { TodoItem },
props: ['todos']
}
</script>
In TodoItem.vue:
<template>
<li>
<input type="checkbox" :checked="todo.completed" #change="toggleTodo(todo)" />
<span class="title">{{todo.title}}</span>
<button #click="deleteTodo(todo.id)">
<i class="fa fa-trash" aria-hidden="true"></i>
</button>
</li>
</template>
<script>
export default {
name: 'TodoItem',
props: ['todo']
}
</script>
The todo list's <h1> heading is displayed, but the unordered list of todos is not.
Introducing props: ['todos'] in App.vue throws the error Duplicated key 'todos'.
What am I missing?
On your App.vue you defined props['todos'] and at the same time todos is defined in your data(). That might be the one causing the error.
Looks like you forgot to define dataIsLoaded in List.vue. It either needs to be defined in data or be a computed property.
I want to ask why the newly added data does not display in the template, but it does show the newly added data in using console.log (see the ViewPost.vue).
This is the result after I add new post: result.png. Someone knows how to achieve this?
Here is my parent component:
<template>
<section>
//more codes here
<ViewPost v-for="data in postData" :key="data.post_id" :data="data" />
</section>
</template>
<script>
import { mapState } from 'vuex';
export default {
components: {ViewPost},
computed: {
...mapState({
postData: state => state.post.datas,
}),
}
//more codes here
};
</script>
And below is the ViewPost.vue
<template>
<span>
<div class="card mb-10">
<h1>body</h1>
{{ data.post_body }}
</div>
</span>
</template>
<script>
export default {
props: {
data: {},
},
created() {
console.log(this.data);
},
};
index as a key may work fine.
<template>
<section>
//more codes here
<ViewPost v-for="(data,index) in postData" :key="index" :data="data" />
</section>
</template>
<script>
import { mapState } from 'vuex';
export default {
components: {ViewPost},
computed: {
...mapState({
postData: state => state.post.datas,
}),
}
//more codes here
};
</script>
ViewPost.vue
<template>
<span>
<div class="card mb-10">
<h1>body</h1>
{{ data.post_body || data.body }}
</div>
</span>
</template>
<script>
export default {
props: ['data']
created() {
console.log(this.data);
},
};
My goal is when clicking on any search item to open the Popup.
Why does not work — this.$emit('openPopup', bookId); in the method selectBook(bookId)
There is a component of Results.vue, which displays search results by using Google Books API:
<template>
<div class="results">
<ul class="results-items">
<li
class="book"
#click="selectBook(result.id)"
>
<img
:src="'http://books.google.com/books/content?id=' + result.id + '&printsec=frontcover&img=1&zoom=1&source=gbs_api'"
class="cover"
>
<div class="item-info">
<div class="bTitle">{{ result.volumeInfo.title }}</div>
<div
class="bAutors"
v-if="result.volumeInfo.authors"
>
{{ result.volumeInfo.authors[0] }}
</div>
</div>
</li>
</ul>
</div>
</template>
<script>
export default {
props: ['result'],
data() {
return {
bookId: '',
};
},
methods: {
selectBook(bookId) {
this.$router.push(`bookId${bookId}`);
this.$store.dispatch('selectBook', bookId);
this.$emit('hideElements', bookId);
this.$emit('openPopup', bookId);
},
},
};
</script>
Rusults screenshot
Is the main App.vue, which I want to display Popup:
<template>
<div id="app">
<div class="container">
<Header />
<popup
v-if="isOpenPopup"
#closePopup="closeInfoPopup"
#openPopup="showModal"
/>
<router-view/>
</div>
</div>
</template>
<script>
import Header from '#/components/Header.vue';
import Popup from '#/components/Popup.vue';
import 'bootstrap/dist/css/bootstrap.css';
export default {
components: {
Header,
Popup,
},
data() {
return {
isOpenPopup: false,
};
},
methods: {
showModal() {
console.log('click');
this.isOpenPopup = true;
},
closeInfoPopup() {
this.isOpenPopup = false;
},
},
};
</script>
The component Popup.vue
<template>
<div class="popup">
<div class="popup_header">
<span>Popup name</span>
</div>
<div class="popup_content">
<h1>Hi</h1>
<slot></slot>
<button #click="closePopup">Close</button>
</div>
</div>
</template>
<script>
export default {
name: 'popup',
props: {},
data() {
return {};
},
methods: {
closePopup() {
this.$emit('closePopup');
},
},
};
</script>
You are listening on popup component but triggering events on Result
Don't remember that events are bound to component.
You have several options how to handle it
Add event listeners (like #openPopup) only on Result component and use portal-vue library to render popup on top level
use global events, this.$root.emit and listen also on this.$root. In this case you add event listener in mount() hook and don't forget unregister it in beforeDestroy() hook
you can use Vuex and popup visibility and related data in global application store provided by Vuex
I'm trying to dynamically change the req attribute of my axios request on button click.
I'm using SWAPI, then when I call get "people/", it returns "next" as the link for the next page.
When I click the button, I want to change the request to "next" attribute retrieved from the api.
<template>
<div class="container">
<ul>
<li v-bind:key="person.birth_year" v-for="person of people" class="response">
<div class="card">
<h1>{{person.name}}</h1>
<h3>Altura: {{person.height}}</h3>
<h3>Peso: {{person.mass}}</h3>
<h3>Gênero: {{person.gender}}</h3>
<h3>Cor da pele: {{person.skin_color}}</h3>
</div>
</li>
<button v-on:click="req=next">Proxima Pagina</button>
</ul>
</div>
</template>
<script>
import api from '../services/api'
import '../css/index.css'
export default {
data(){
return {
people:[],
next:"",
req:""
}
},
mounted() {
let req="people/"
api.get(req)
.then(response=>{
this.people=response.data.results;
this.next=response.data.next;
});
},
}
</script>
Use the same variable to set the new link as below :
<template>
<div class="container">
<ul>
<li v-bind:key="person.birth_year" v-for="person of people" class="response">
<div class="card">
<h1>{{person.name}}</h1>
<h3>Altura: {{person.height}}</h3>
<h3>Peso: {{person.mass}}</h3>
<h3>Gênero: {{person.gender}}</h3>
<h3>Cor da pele: {{person.skin_color}}</h3>
</div>
</li>
<button v-on:click="getData()">Proxima Pagina</button>
</ul>
</div>
</template>
<script>
import api from '../services/api'
import '../css/index.css'
export default {
data() {
return {
people: [],
next: "",
req: "people/"
}
},
mounted() {
this.getData();
},
methods: {
getData() {
api.get(this.req).then(response => {
this.people = response.data.results;
this.req = response.data.next;
});
}
}
}
</script>
I'm having an issue when using a child component, a list does not update based on a prop passed into it.
If the comments array data changes, the list will not update when it uses a child component <comment></comment>.
TopHeader template:
<template>
<ul v-for="comment in comments">
// If don't use a child component, it updates whenever `comments` array changes:
<li>
<div>
/r/{{comment.data.subreddit}} ·
{{comment.data.score}}
</div>
<div class="comment" v-html="comment.data.body"></div>
<hr>
</li>
</ul>
</template>
TopHeader component:
import Comment from 'components/Comment'
export default {
name: 'top-header',
components: {
Comment
},
data () {
return {
username: '',
comments: []
}
},
methods: {
fetchData: function(username){
var vm = this;
this.$http.get(`https://www.reddit.com/user/${username}/comments.json?jsonp=`)
.then(function(response){
vm.$set(vm, 'comments', response.body.data.children);
});
}
}
}
However, if I use a child component it does not update.
Modified TopHeader template:
<template>
<ul v-for="comment in comments">
// If I instead use a component with prop data, it does not update
<comment :data="comment.data"></comment>
</ul>
</template>
Comment child template:
<template>
<li>
<div>
/r/{{subreddit}} ·
{{score}}
</div>
<div class="comment" v-html="body"></div>
<hr>
</li>
</template>
Comment child component:
export default {
name: 'comment',
props: ['data'],
data() {
return {
body: this.data.body,
subreddit: this.data.subreddit,
score: this.data.score,
}
}
}
Move the v-for statement to the component <comment>. With the v-for on the ul-tag you would repeat the ul tag. See http://codepen.io/tuelsch/pen/YNOqYR again for an example.
<div id="app">
<button v-on:click="fetch">Fetch data</button>
<ul>
<comment v-for="comment in comments" v-bind:comment="comment" />
</ul>
</div>