I want to display an arbitrary value from the state array stored in the store.js file - vue.js

User information is managed by the state of the store.js file.
User information is managed by the state of the store.js file.
const store = createStore({
state() {
return {
users:[
{id: 0, userName: 'test1', wallet: 500},
{id: 1, userName: 'test2', wallet: 1000},
{id: 2, userName: 'test3', wallet: 1500},
{id: 3, userName: 'test4', wallet: 2000},
],
userName: '',
updateUserName: '',
userLoginInfomation: '',
errorMessage: ''
};
},
A modal is displayed when you press the button to view the wallet.
↓The modal that appears when you click View Wallet.
<div class="overlay" v-show="showContent" #click="closeModal">
<transition name="modal">
<div v-show="showContent" class="content">
<p>{{ viewUsers[1].userName }} balance</p>
<p>{{ viewUsers[1].wallet }}yen</p>
<p>
<button class="close-button" #click="closeModal">Close</button>
</p>
</div>
</transition>
</div>
↓The modal that appears when you click send.
<div class="overlay" v-show="showContent2" #click="closeModal">
<div class="content">
<p> Your balance:500yen</p>
<p>Amount to send</p>
<input type="text" class />
<p>
<button class="close-button" #click="closeModal2">Close</button>
</p>
</div>
</div>
What you want to achieve
(1) When you click the View Wallet button, you want to get the user name with the same index number as the clicked index number from state.users in the store.js file and display it modally.
(2) When you click the send button, enter the amount to send to the displayed modal input. I want to add the amount of money entered in the wallet of the state.users data of the store.js file that has the same sequence number as the clicked button.

This is untested, but should help you get in the right direction:
<template>
<ul>
<li v-for="user in users" :key="user.id" #click="selectUser(user)">
{{ user.userName }} | {{ user.wallet }}
</li>
</ul>
<div class="modal" v-if="showModal">
<!--
here you can now access whatever user properties
you want via the selectedUser property
-->
<p>Name: {{ selectedUser.userName }}</p>
<p>ID: {{ selectedUser.id }}</p>
<p>Wallet: {{ selectedUser.wallet }}</p>
</div>
</template>
<script>
data: () => ({
selectedUser: null
}),
computed: {
users() {
return this.$store.users;
}
},
methods: {
selectUser(user) {
this.selectedUser = user;
this.showModal = true;
}
},
</script>

Related

Removing specific object from array keeps removing last item

Here is what I have and I will explain it as much as I can:
I have a modal inside my HTML code as shown below:
<div id="favorites-modal-edit" class="modal">
<div class="modal-background"></div>
<div class="modal-card px-4">
<header class="modal-card-head">
<p class="modal-card-title">Favorites</p>
<button class="delete" aria-label="close"></button>
</header>
<section class="modal-card-body">
<div class="container">
<div id="favorites-modal-edit-wrapper" class="columns is-multiline buttons">
<favorites-edit-component v-for="(favorite, index) in favorites_list" :key="favorite.id" :favorite="favorite" />
</div>
</div>
</section>
<footer class="modal-card-foot">
<button class="button" #click="addItem">
Add Item
</button>
</footer>
</div>
</div>
The id="favorites-modal-edit" is the Vue.js app, then I have the <favorites-edit-component /> vue.js component.
Here is the JS code that I have:
I have my favorites_list generated which is an array of objects as shown below:
const favorites_list = [
{
id: 1,
name: 'Horse',
url: 'www.example.com',
},
{
id: 2,
name: 'Sheep',
url: 'www.example2.com',
},
{
id: 3,
name: 'Octopus',
url: 'www.example2.com',
},
{
id: 4,
name: 'Deer',
url: 'www.example2.com',
},
{
id: 5,
name: 'Hamster',
url: 'www.example2.com',
},
];
Then, I have my vue.js component, which is the favorites-edit-component that takes in the #click="removeItem(this.index) which is coming back as undefined on the index.
Vue.component('favorites-edit-component', {
template: `
<div class="column is-half">
<button class="button is-fullwidth is-danger is-outlined mb-0">
<span>{{ favorite.name }}</span>
<span class="icon is-small favorite-delete" #click="removeItem(this.index)">
<i class="fas fa-times"></i>
</span>
</button>
</div>
`,
props: {
favorite: Object
},
methods: {
removeItem: function(index) {
this.$parent.removeItem(index);
},
}
});
Then I have the vue.js app that is the parent as shown below:
new Vue({
el: '#favorites-modal-edit',
// Return the data in a function instead of a single object
data: function() {
return {
favorites_list
};
},
methods: {
addItem: function() {
console.log('Added item');
},
removeItem: function(index) {
console.log(index);
console.log(this.favorites_list);
this.favorites_list.splice(this.favorites_list.indexOf(index), 1);
},
},
});
The problem:
For some reason, each time I go to delete a item from the list, it's deleting the last item in the list and I don't know why it's doing it, check out what is happening:
This is the guide that I am following:
How to remove an item from an array in Vue.js
The item keeps coming back as undefined each time the remoteItem() function is triggered as shown below:
All help is appreciated!
There is an error in your favorites-edit-component template, actually in vue template, when you want to use prop, data, computed, mehods,..., dont't use this
=> there is an error here: #click="removeItem(this.index)"
=> in addition, where is index declared ? data ? prop ?
you're calling this.$parent.removeItem(index); then in removeItem you're doing this.favorites_list.splice(this.favorites_list.indexOf(index), 1); this means that you want to remove the value equal to index in you array no the value positioned at the index
=> this.favorites_list[index] != this.favorites_list[this.favorites_list.indexOf(index)]
In addition, I would suggest you to modify the favorites-edit-component component to use event so it can be more reusable:
favorites-edit-component:
<template>
<div class="column is-half">
<button class="button is-fullwidth is-danger is-outlined mb-0">
<span>{{ favorite.name }}</span>
<span class="icon is-small favorite-delete" #click="$emit('removeItem', favorite.id)">
<i class="fas fa-times"></i>
</span>
</button>
</div>
</template>
and in the parent component:
<template>
...
<div id="favorites-modal-edit-wrapper" class="columns is-multiline buttons">
<favorites-edit-component
v-for="favorite in favorites_list"
:key="favorite.id"
:favorite="favorite"
#removeItem="removeItem($event)"
/>
</div>
...
</template>
<script>
export default {
data: function () {
return {
favorites_list: [],
};
},
methods: {
...
removeItem(id) {
this.favorites_list = this.favorites_list.filter((favorite) => favorite.id !== id);
}
...
},
};
I would restructure your code a bit.
In your favorites-edit-component
change your removeItem method to be
removeItem() {
this.$emit('delete');
},
Then, where you are using your component (in the template of the parent)
Add an event catcher to catch the emitted "delete" event from the child.
<favorites-edit-component v-for="(favorite, index) in favorites_list" :key="favorite.id" :favorite="favorite" #delete="removeItem(index)"/>
The problem you have right now, is that you are trying to refer to "this.index" inside your child component, but the child component does not know what index it is being rendered as, unless you specifically pass it down to the child as a prop.
Also, if you pass the index down as a prop, you must refer to it as "index" and not "this.index" while in the template.

Vue - Accessing Attributes From One Component to Another

New to vue and struggling to understand how data is passed back and forth between components. I'm aware of props and emit on the parent/child, child/parent, but I can't quite understand how they work in my case. I have two components: a parent component called "Letters" and a child called "ClaimantSearch". Claimant search return data about a person based on a call to a flask backend:
<div>
<b-form #submit="onSubmit" class="w-100">
<b-form-group id="form-title-group"
label="Claim Number:"
label-for="form-claim-number-input">
<b-form-input id="form-claim-number-input"
type="text"
v-model="claimNumberForm.claimNumber"
required
placeholder="Enter claim number">
</b-form-input>
<button
type="button"
class="btn btn-warning btn-sm"
#click="getClaimant(claimNumber)">
Get Claimant
</button>
</b-form-group>
</b-form>
<span v-if="claimant">
<p> {{ claimant.name }} </p>
<p> {{ claimant.address1 }} </p>
<p> {{ claimant.address2 }} </p>
<p> {{ claimant.city }}, {{ claimant.state }} </p>
<p> {{ claimant.zip }} </p>
</span>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
claimant: '',
claimNumberForm: {
claimNumber: '',
},
};
},
methods: {
getClaimant(number) {
const path = `http://localhost:5000/claimant/${number}`;
axios.get(path)
.then((res) => {
this.claimant = res.data.claimant;
})
.catch((error) => {
// eslint-disable-next-line
console.error(error);
});
},
onSubmit(evt) {
evt.preventDefault();
this.getClaimant(this.claimNumberForm.claimNumber);
},
},
};
</script>
I then have a Letters parent component:
<template>
<div>
<claimant></claimant>
</div>
</template>
<script>
// import axios from 'axios';
import ClaimantSearch from './ClaimantSearch.vue';
export default {
data() {
return {
claimant: '',
claimNumberForm: {
claimNumber: '',
},
};
},
components: {
claimant: ClaimantSearch,
},
methods: {
},
};
</script>
What I'd like to be able to do is access {{claimant}} outside of the <claimant> tag, if that makes sense. So inside Letters I'd like to do something like:
<template>
<div>
<div>
<claimant></claimant>
</div>
<div>
Dear Mr. {{claimant.name}},
Please get bent. Sincerly, crappy insurance company.
</div>
</div>
</template>
I can't remember exactly where I found this link, but there's an excellent post on medium, with code samples, that discusses all the state management patterns in vue starting with props and events, eventbus, simple store and then vuex.
https://medium.com/fullstackio/managing-state-in-vue-js-23a0352b1c87

Bootstrap-vue: how to pass data to modal?

I'm trying to use bootstrap-vue modal to show details from a collection of items.
What I want is to pass data to modal to show a simple message.
I first loop over recordset to show button.
<ul>
<li v-for="item in items">{{ item.first_name }}
<b-button size="sm" v-b-modal="'myModal'" user="'item'">
Saluta {{item.first_name}}
</b-button>
</li>
</ul>
And then display name in modal:
<b-modal id="myModal" :user="'user'">
Hello {{user}}!
</b-modal>
Here's my fiddle https://jsfiddle.net/bptLavov/259/
This works just fine:
HTML:
<div id="app">
<ul>
<li v-for="item in items">
{{ item.first_name }}
<b-button size="sm" v-b-modal="'myModal'" user="'item'" click="sendInfo(item)">
Saluta {{item.first_name}}
</b-button>
</li>
</ul>
<b-modal id="myModal">
Hello {{selectedUser.first_name}} {{selectedUser.last_name}} !
</b-modal>
</div>
JAVASCRIPT:
new Vue({
el: '#app',
data: {
items :
[
{ first_name: 'Dickerson', last_name: 'Macdonald' },
{ first_name: 'Larsen', last_name: 'Shaw' },
{ first_name: 'Geneva', last_name: 'Wilson' },
{ first_name: 'Jami', last_name: 'Carney' }
],
selectedUser: '',
},
methods: {
sendInfo(item) {
this.selectedUser = item;
}
}
})
What it does is:
1) Execute a method named sendInfo
2) That methods will set the selectedUser variable inside data with the selected user which information is sent thanks to the v-on:click (#click) directive depending on the v-for iteration. Because of that, each button will send the right information.
3) Display the information inside the modal
You can use vuex and your components won't have to be in the same file or related.
Component which will open the modal:
<ul>
<li v-for="item in items">{{ item.first_name }}
<b-button #click="$store.dispatch('modals/openModal', { data: item, modalId: 'myModal' })">
Saluta {{item.first_name}}
</b-button>
</li>
</ul>
Modal's template:
<b-modal id="myModal">
Hello {{selectedUser.first_name}} {{selectedUser.last_name}} !
</b-modal>
Modal's computed property:
selectedUser() { return this.$store.state.modals.modalData },
Vuex module (modals.js):
const state = {
modalData: {},
}
const mutations = {
setModalData(state, data) { state.modalData = data },
}
const actions = {
openModal(context, data) {
context.commit('setModalData', data.data)
$('#' + data.modalId).modal('show')
},
}

Accessing siblings elements in a v-for

New to Vuejs 2, need a bit of help to understand how this would be done.
So, I have a template that looks like the following:
<template>
<div class="wrapper">
<div class="item" v-for="item in items">
<div class="item">
<div class="title">{{ item.title }}</div>
<div class="action">
<a href="#" #click.prevent="editSettings(item, $event)">
Click Me
</a>
</div>
</div>
<div v-if="showSettings" class="settings">
<!-- Settings component for this item. -->
<settings-panel item="item"></settings-panel>
</div>
</div>
</div>
</template>
The scripts looks like this:
export default {
data() {
return {
items: [
// This data is from an API usually and only
// shown here for example purposes
{ id: 1, title: 'Title 1' },
{ id: 2, title: 'Title 2' },
{ id: 3, title: 'Title 3' },
{ id: 4, title: 'Title 4' }
],
showSettings: false
};
},
methods: {
editSettings(item, event) {
// Only show the clicked items settings, not all
this.showSettings = !showSettings;
}
}
}
What is the easiest and cleanest approach to only showing the settings panel for the clicked item? Right now, if I click the Click Me settings, all settings open up. The data listed in the code is only there for example, but is coming from an API so I don't have much to do with the manipulation of that data.
Can anyone help lead me in the right direction?
Instead of using showSettings add a selectedItem.
export default {
data() {
return {
items: [
// This data is from an API usually and only
// shown here for example purposes
{ id: 1, title: 'Title 1' },
{ id: 2, title: 'Title 2' },
{ id: 3, title: 'Title 3' },
{ id: 4, title: 'Title 4' }
],
selectedItem: null
};
},
methods:{
selectItem(item){
if (this.selectedItem === item) this.selectedItem = null
else this.selectedItem = item
}
}
}
And modify your template to set the selectedItem when the item is clicked and show the settings only if the selectedItem is the current item.
<template>
<div class="wrapper">
<div class="item" v-for="item in items">
<div class="item">
<div class="title">{{ item.title }}</div>
<div class="action">
<a href="#" #click.prevent="selectItem(item)">
Click Me
</a>
</div>
</div>
<div v-if="selectedItem === item" class="settings">
<!-- Settings component for this item. -->
<settings-panel item="item"></settings-panel>
</div>
</div>
</div>
</template>
The above will show the settings for the clicked item if the currently selected item is not the item that was clicked. If the currently selected item is the one that was clicked, it will hide the settings.

why doesn't my interpolated html display properly in vue.js?

The data in my app.js file is not being interpolated and displayed on the screen. I tried the older v-repeat. Could it be some missing items in the installation? Any help would be appreciated.
Here is my html
<!-- show the events -->
<div class="col-sm-6">
<div class="list-group">
<a href="#" class="list-group-item" v-for="event in events">
<!-- <h1>{{text}}</h1> -->
<h4 class="list-group-item-heading">
<i class="glyphicon glyphicon-bullhorn"></i>
{{ event.name }}
</h4>
<h5>
<i class="glyphicon glyphicon-calendar" v-if="event.date"></i>
{{ event.date }}
</h5>
<p class="list-group-item-text" v-if="event.description">{{ event.description }}</p>
<button class="btn btn-xs btn-danger" v-on="click: deleteEvent($index)">Delete</button>
</a>
</div>
</div>
And here is my js file
new Vue({
data: {
text: 'hello world',
event: { name: '', description: '', date: '' },
events: []
},
// Anything within the ready function will run when the application loads
ready: function() {
// When the application loads, we want to call the method that initializes
// some data
this.fetchEvents();
},
// Methods we want to use in our application are registered here
methods: {
// We dedicate a method to retrieving and setting some data
fetchEvents: function() {
var events = [
{
id: 1,
name: 'TIFF',
description: 'Toronto International Film Festival',
date: '2015-09-10'
},
{
id: 2,
name: 'The Martian Premiere',
description: 'The Martian comes to theatres.',
date: '2015-10-02'
},
{
id: 3,
name: 'SXSW',
description: 'Music, film and interactive festival in Austin, TX.',
date: '2016-03-11'
}
];
// $set is a convenience method provided by Vue that is similar to pushing
// data onto an array
this.$set('events', events);
},
// Adds an event to the existing events array
addEvent: function() {
if(this.event.name) {
this.events.push(this.event);
this.event = { name: '', description: '', date: '' };
}
}
}
});
Thanks for any help.
So, there are a few issues here. I've cleaned them up for you to look over.
Added the el: "#app" property to tell Vue where to mount your Vue.
Changed your ready to mounted (you could also use created).
Changed this.$set('events',events) to this.events = events.
Added the unreferenced deleteEvent method.
Fixed the click handler syntax for v-on:click.
So the code ends up looking like this.
new Vue({
el: "#app",
data: {
text: 'hello world',
event: { name: '', description: '', date: '' },
events: []
},
mounted: function() {
this.fetchEvents();
},
// Methods we want to use in our application are registered here
methods: {
// We dedicate a method to retrieving and setting some data
fetchEvents: function() {
var events = [...];
this.events = events;
},
// Adds an event to the existing events array
addEvent: function() {
if(this.event.name) {
this.events.push(this.event);
this.event = { name: '', description: '', date: '' };
}
},
deleteEvent(){
alert('delete this one')
}
}
});
Template
<div id="app">
<!-- show the events -->
<div class="col-sm-6">
<div class="list-group">
<a href="#" class="list-group-item" v-for="event in events">
<!-- <h1>{{text}}</h1> -->
<h4 class="list-group-item-heading">
<i class="glyphicon glyphicon-bullhorn"></i>
{{ event.name }}
</h4>
<h5>
<i class="glyphicon glyphicon-calendar" v-if="event.date"></i>
{{ event.date }}
</h5>
<p class="list-group-item-text" v-if="event.description">{{ event.description }}</p>
<button class="btn btn-xs btn-danger" v-on:click=" deleteEvent(event)">Delete</button>
</a>
</div>
</div>
</div>
Working example.