I have a ticket as you can see in the picture below:
I have a delete button as a component and I am trying to add delete functionality to it. I am using this component in my ticket component. So this is my delete component:
<template>
<div id="delete-button" #click.prevent="removeProductFromCart(item.id)">
<input type="checkbox" id="checkbox">
<div id="bin-icon">
<div id="lid"></div>
<div id="box">
<div id="box-inner">
<div id="bin-lines"></div>
</div>
</div>
</div>
</div>
</template>
<script>
import cartHelper from "../helpers/cartHelper";
export default {
props: {
item: Object,
},
data() {
return {
loading: false,
};
},
methods: {
removeProductFromCart(id) {
this.loading = true;
setTimeout(() => {
cartHelper.removeFromCart(id, (response) => {
this.$store.dispatch('removeProductFromCart', {
cart: response.data,
})
this.loading = false
});
}, 1000)
}
}
};
</script>
So the parent component is ticket component:
[![<template>
<div id="sold-tickets">
<div class="card">
<div class="sold-tickets-actions properties">
<div class="sold-tickets-inner">
<DeleteButton :item = "item" />
</div>
</div>
</div>
</div>
</template>][1]][1]
<script>
import image from "../../../../img/Hallenbad.jpg";
import DeleteButton from "./DeleteButton";
import cartHelper from "../helpers/cartHelper";
export default {
props: {
item: Object,
},
components: {DeleteButton},
data() {
return {
image: image,
};
},
};
</script>
My problem is, the ticket is being deleted even when I click outside of the child component (Delete component). But I want to use the delete component like a button and I only want to delete the ticket when it is clicked, not outside of the button.
Try to stop propagation. You probably do not even need the prevent modifier.
#click.prevent.stop='...'
or
#click.stop='...'
If the display type is block, you might also want to inspect the div to see if it is actually filling the entire width of the page. If so, use an inline type, a flex layout, or similiar.
Related
I want to show or hide the item by clicking the button or clicking the item itself, for example:
<template>
<div>
<button #click="show?show = false:show = true">
{{show?"Hide":"Show"}}
</button>
<div #click="show?show = false:show = true" v-if="show">
Vue Js - click here to Hide
</div>
</div>
</template>
<script>
export default {
data() {
return {
show: null,
};
}
};
</script>
but i want to import the item from another component, so i do this:
the parent component:
<template>
<div>
<button #click="show?show = false:show = true">
{{show?"Hide":"Show"}}
</button>
<item :show="show" />
</div>
</template>
<script>
import item from "item.vue"
export default {
components: {
item
},
data() {
return {
show: null,
};
}
};
</script>
the child component:
<template>
<div #click="show?show = false:show = true" v-if="show">
Vue Js - click here to Hide
</div>
</template>
<script>
export default {
props: {
show: Boolean,
}
};
</script>
but of course, it doesn't work well.
When i click on the item it disappears but the show value in the parent component doesn't change and I get an error saying Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "show".
so how to edit the data value of the parent component from the child component?
(I use Vue 2.6.14)
The solution is to use event emitting (Youtube Tutorial) to send updated data up to the parent component from the child component.
so the code becomes like this:
the parent component:
<template>
<div>
<button #click="show?show = false:show = true">
{{show?"Hide":"Show"}}
</button>
<item :show="show" #state="update($event)" />
</div>
</template>
<script>
import item from "item.vue"
export default {
components: {
item
},
data() {
return {
show: null,
};
},
methods: {
update(value) {
this.show = value;
}
}
};
</script>
the child component:
<template>
<div #click="action" v-if="show">
Vue Js - click here to Hide
</div>
</template>
<script>
export default {
props: {
show: Boolean,
},
methods: {
state() {
if (this.show) {
return false;
} else {
return true;
}
},
action() {
this.$emit("state", this.state())
}
}
};
</script>
Thank you #D.Schaller for helping
In my vue app I have two components one which is a form that posts the form data to my api. And the other gets and displays these posts in a section on the page. My issue is that when I submit a new post the posts lists aren't updated. The data stays the same unless I refresh the page. How can I get my posts list to update when I submit the form?
My Code:
client/src/App.vue
<template>
<div id="app">
<MainHeader :modalVisability="modal" v-on:showModal="toggleModal" />
<div id="content_wrap">
<Summary />
</div>
<OppForm :modalVisability="modal" />
</div>
</template>
<script>
import MainHeader from './components/MainHeader.vue';
import OppForm from './components/oppForm.vue';
import Summary from './components/Summary.vue';
export default {
name: 'App',
components: {
MainHeader,
Summary,
OppForm
},
data () {
return {
modal: false
}
},
methods: {
toggleModal (modalBool) {
this.modal = modalBool;
}
}
}
</script>
client/src/components/oppForm.vue
<template>
<div id="opp_form_modal" >
<form #submit.prevent="SubmitOpp" v-if="modalVisability">
<input type="text" name="company_name" v-model="company_name">
<button type="submit">Submit</button>
</form>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'oppForm',
props: {
modalVisability: Boolean,
},
data () {
return {
company_name: ''
}
},
methods: {
SubmitOpp () {
axios.post('http://localhost:5000/', {
company_name: this.company_name,
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
}
}
}
</script>
client/src/components/Summary.vue
<template>
<div id="summary_section">
<h2>Summary</h2>
<div id="summary_board">
<div class="column">
<div class="head">
<h3>Opportunities</h3>
</div>
<div class="body">
<div class="post"
v-for="(post, index) in posts"
:key="index"
>
<p class="company">{{ post.company_name }}</p>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return{
posts: []
};
},
created() {
axios.get('http://localhost:5000/')
.then(res => {
// console.log(res);
const data = res.data;
this.posts = data;
})
.catch(error => console.log(error));
}
}
</script>
The problem is that you're actually fetching your posts only on the app creation (i.e. inside the created() method).
You should wrap your axios call inside a function updatePosts() and then call it whenever you add a new post successfully, or you could create a custom event that is triggered whenever a new post is added.
created() is called only once (see vue lifecycle) so you fetch API before submitting form.
Try to add some console.log to understand what is called when.
You could use an global event bus and send form value as event data to summary. I could imagine also a solution where event is used to "tell" summary that form was submitted (just boolean, not data itself). In summary you then call API each time you receive event.
Or simple add an "update" button to summary to manually call API.
See Communication between sibling components in VueJs 2.0
or global vue instance for events for detailed examples.
I'd like to render new component in vue.js as if it's new page.
I'm trying to do it with something called "dynamic component"
parent: Post.vue
child: Detail.vue
so, if one of the posts is clicked, Post is off and Detail is on.
The thing is I have to send clicked post's id to the Detail.
Here's some of my code.
Post.vue
<template>
<div>
<div v-if="loading">
loading...
</div>
<div v-else class="container">
<ul>
<li v-for="(post, index) in filteredPosts" v-bind:key="post.no">
<section class="post__main">
<div #click..?? class="main__title">{{post.title}}</div>
</section>
</li>
</ul>
</div>
</div>
</template>
<script>
created() {
axios.get(this.url, {
params: {
page: this.page,
ord: this.ord,
category: []
}
}).then(res => {
this.posts = res.data.list;
this.loading = false;
this.page++;
});
Detail.vue
<template>
<div>
{{contents}}
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'Datail',
data() {
return {
req_no: null,
contents: {}
}
},
created() {
axios.get(url, {
params: {
req_no: this.req_no
}
}).then(res => {
this.contents = this.res.data
});
}
}
</script>
I feel like I can do it with props and v-if.
Can someone help me? Thanks!
Once a post is clicked, pass post id to the click handler. In the click handler, route to detail.vue passing post id as route param.
like below:
<li v-for="(post, index) in filteredPosts" v-bind:key="post.no">
<section class="post__main">
<div #click="onPostClick(post.id)" class="main__title">{{post.title}}</div>
</section>
</li>
And in your click handler:
onPostClick(id: number) {
this.$router.push({
name: 'details',
params: {
id
}
});
}
This will work provided you set up vue router correctly in your app and have a valid route for details.
You can access the post id in details component as follows:
created() {
this.postId = this.$route.params.id;
}
I would take a look at the <component> which takes a prop :to and renders a component, this is good for something like tabs where you are rendering different component from a the same general location on the page without reloading the whole page. See here:
https://v2.vuejs.org/v2/guide/components-dynamic-async.html
This seems to be a very good use case for you, just pass into the component the props you need.
In my vue.js app, I need to display a list of items which the user can click.
When clicked, each of these items should then fire a modal, containing additional information about the item that the user just clicked.
What I have so far in my Items.vue component is:
<template>
<div id="content">
<li v-for="item in items" class="list-item pa2 ba">
<div class="f5 pt2 pb2">
<span>{{item.name}}</span>
</div>
</li>
</div>
</template>
<script>
import Items from '#/api/items';
export default {
name: 'items',
asyncComputed: {
items: {
async get() {
const items = await Items.getAll();
return items.data;
},
default: []
}
},
components: {}
}
</script>
Now, I could simply add a modal component to the v-for loop, thus creating one modal for each item, but this does not seem ideal if, for example, I have a list of thousands of items.
This led me to believe that the modal should probably be placed at the root of the app (in my case App.vue), like this:
<template>
<div id="app">
<modal></modal>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'app'
}
</script>
and then somehow fired with custom data whenever I needed it.
However, I'm not sure how to proceed. How do I fire this modal with custom information from inside the v-for loop, which is in a child component relative to App.vue?
These two links helped me figure this out:
https://v2.vuejs.org/v2/examples/modal.html
https://laracasts.com/discuss/channels/vue/passing-data-form-v-for-loop-to-modal-component
#In your Parent Component
You don't have to create a modal for each item within the v-for loop, simply include the modal-component at the beginning of your parent and then work with v-if="..." and props.
<template>
<div>
<modal v-if="modalVisible" #close="modalVisible = false" :data="modalData"/>
<div v-for="item in items">
<button type="button" #click="openModal(item)">Open Modal</button>
</div>
</div>
</template>
and then in your script:
import modal from './Modal'
export default {
components: {
modal
},
data() {
return {
modalVisible: false,
modalData: null
}
},
methods: {
openModal(data) {
this.modalData = data
this.modalVisible = true
},
}
#In your child (modal) component
In your modal you can now do the following:
Template:
<template>
{{ data.foo }}
<button #click="$emit('close')">Cancel</button>
</template>
Script
<script>
export default {
props: ['user']
};
</script>
Hope that helps :-)
I have created a vue component for selecting photos. When the user clicks any photo the id of the photo will be assigned to a hidden input field inside the component.
Now I have used this component twice on the same page with different data. The problem is when I click on the photo of one component the value of the input field of both the components gets updated. I am using vue.js version 2.1.10
Here is the simplified version of my component.
<div>
<input type="hidden" :name="inputName" :value="currentSelectedPhoto.id">
<div v-for="photo in photos">
<div v-on:click="updateSelectedPhoto(photo)">
<img :src="photo.photo_url" />
</div>
</div>
</div>
The Component
export default {
props: {
...
},
methods: {
getPhotos(){
...
},
updateSelectedPhoto(photo){
this.currentSelectedPhoto = photo;
}
}
}
This is how I am using it in html
<div>
<div>
Profile Photo
<photo-selector
photos="{{ $photos }}"
input-name="profile_photo_id"
>
</photo-selector>
</div>
<div class="col-sm-4">
Cover Photo
<photo-selector
photos="{{ $otherPhotos }}"
input-name="cover_photo_id"
>
</photo-selector>
</div>
</div>
Based on your codepen sample, it's because you are sharing the state object between the two:
const initalState = {
selectedPhoto: null
};
const PhotoSelector = Vue.extend({
data: () => {
return initalState
},
Vue mutates the initial state object (by wrapping it in reactive getters etc), so you need to have data() return a fresh state object for the instance to use:
data: () => {
return {
selectedPhoto: null
};
},