VueJS2 - Triggering modal component from another component - javascript

So this one is a bit tricky for me. I have a Sidebar.vue component with a link(Help). I want to trigger my Modal.vue component with different data for every main view eg. Home, About, Contact etc. So whenever I am on Home I want to trigger help modal with hints via that link for Home.vue, when I am on About I want to show hints for About.vue and so on..
My code:
Sidebar.vue
<template>
<ul>
<li>
<a #click="openHelp">Help</a>
</li>
</ul>
</template>
<script>
export default {
name: 'Sidebar',
methods: {
openHelp() {
this.$emit('help-modal');
},
},
};
</script>
Modal.Vue
<template>
<div class="modal" v-if="open">
<div class="modal-title">
<h4>Help</h4>
</div>
</div>
</template>
<script>
export default {
name: 'Modal',
props: {
open: {
type: Boolean,
default: false,
}
},
methods: {
close() {
this.$emit('closed');
},
},
};
</script>
Home.vue
<template>
<div>
Home
<modal
:open="helpOpen"
#closed="openHelpModal">
<p>Home Help</p>
</modal>
</div>
<template>
<sidebar/>
</template>
</template>
<script>
import Sidebar from '#/components/Sidebar.vue';
export default {
name: 'Home',
components: {
Sidebar,
},
data() {
return {
helpOpen: false,
}
}
methods: {
openHelpModal() {
this.helpOpen = !this.helpOpen;
},
}
}
</script>
I know that vuex would be the best solution but don't have an idea how to approach it. Modal would show only static images with a bit of text for every main view.

One way of doing this would be to simply add a property to the modal component e.g helpData and pass the relevant data to this prop in each of the main pages as shown below
Modal.vue
<template>
<div class="modal" v-if="open">
<div class="modal-title">
<h4>Help</h4>
</div>
</div>
</template>
<script>
export default {
name: 'Modal',
props: {
open: {
type: Boolean,
default: false,
},
helpData: {
type: String,
required: true
}
},
methods: {
close() {
this.$emit('closed');
},
},
};
</script>
Home.vue
<template>
<div>
Home
<modal
:open="helpOpen"
#closed="openHelpModal"
:help-data="This is a sample help data for the home view"
/>
</div>
<template>
<sidebar #help-modal="helpOpen = true"/>
</template>
</template>
<script>
import Sidebar from '#/components/Sidebar.vue';
export default {
name: 'Home',
components: {
Sidebar,
},
data() {
return {
helpOpen: false,
}
}
methods: {
openHelpModal() {
this.helpOpen = !this.helpOpen;
},
}
}
</script>
As indicated by #ljubadr, I have included the logic that would open the modal in the sidebar component of Home.vue. Also, I would recommend you change the name of the function openHelpModal to closeHelpModal (seeing as this function will basically close the modal) or toggleHelpModal (since from the logic of the function, it toggles the modal state).

Related

how to edit the data value of the parent component from the child component in vue js

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

Delete function in parent component in vue.js

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.

Bootstrap Vue Modal Component

I'm looking to make a global Bootstrap Vue Modal component that I can use for all of my modal needs.
The issue I am facing is that it keeps giving me an error regarding prop mutation. You cannot update them directly in the child component. Which makes sense.
This is what I have so far:
<template>
<b-modal
v-model="show"
size="lg"
scrollable
centered
header-class="event-close-modal-header"
#hide="$emit('close')"
#cancel="$emit('close')"
#ok="$emit('close')"
#show="handleShow"
>
<template #modal-title="{}">
<span class="event-close-modal-header-mc2">{{ title }}</span>
</template>
<b-container fluid class="pb-4">
<h1>Content goes here</h1>
<slot />
</b-container>
</b-modal>
</template>
<script>
export default {
props: {
show: {
type: Boolean,
default: false,
required: true,
},
title: {
type: String,
required: true,
},
},
data() {
return {};
},
};
</script>
And in my parent I have something like this:
<ModalHelper
:show.sync="modalOne" // modalOne is a data property
title="One"
#close="modalOne = false"
/>
<button #click="modalOne = true">Open Modal One</button>
Error:
You can't use props in v-model. Try watch it, and copy new value into new variable in data:
data() {
return {
showModal: false
}
},
watch: {
show(newValue) {
this.showModal = newValue;
}
}
now use showModal in your code instead of "show".
However, this is no good approach.
You should open-close bootstrap vue modals using:
$bvModal.show()
$bvModal.hide()
Vue Bootstrap Modal

How to 2-way bind props with local data of a component in Vue 3?

Can anyone tell me how I can bind the prop of a component to a data property of its own? For example, consider I have a component called ModalComponent
<template> // ModalComponent.vue
<div v-if="showModal">
...
<button #click="showModal = !showModal">close the modal internally</button>
</div>
</template>
<script>
export default {
props: {
show: {
type: Boolean,
default: false
},
},
data() {
return {
showModal: false,
}
}
}
</script>
<style>
</style>
Now consider I am using this modal as a reusable component from inside a parent component using an external button to open up the pop up for the modal.
<template>
<button #click="showChild = !showChild">click to open modal</button>
<ModalComponent :show="showChild" />
</template>
<script>
export default {
components: {ModalComponent},
data() {
return {
showChild: false,
}
}
}
</script>
<style>
</style>
How do I make it so that every time the parent clicks on the button, the modal pops up by binding the local showModal to the prop show ? And when the modal is closed internally by the local close button, if I click on the parent button again it will re-pop up? (I am using Vue 3, just in case)
Any help would be appreciated. Thanks.
The perfect solution for this case is using v-model instead of passing a prop :
in child component define modelValue as prop and emit its value to the parent using $emit function:
<template> // ModalComponent.vue
<div v-if="modelValue">
...
<button #click="$emit('update:modelValue',!modelValue)">close the modal internally</button>
</div>
</template>
<script>
export default {
props: {
modelValue: {
type: Boolean,
default: false
},
},
emits: ['update:modelValue'],
}
</script>
in parent component just use v-model to bind the value :
<template>
<button #click="showChild = !showChild">click to open modal</button>
<ModalComponent v-model="showChild" />
</template>
<script>
export default {
components: {ModalComponent},
data() {
return {
showChild: false,
}
}
}
</script>

vue - How a normal link could act like <route-link>?

I have two pages routed:
Home > Results
Inside 'Home', I have a button/link which is not in <route-view/> and I want this button to redirect to Results.vue passing a parameter.
This parameter named activeTab, has to open the desired vue-tabs, which I can't accomplish, because it's getting nothing from variable:
code:
Home.vue
<div class="row">
<Notes refName="Relationship" />
<Notes refName="Support" />
</div>
...
<script>
import Notes from '#/components/Monthlynotes.vue'
export default {
name: 'home',
components: {
Notes
},
</script>
/components/Monthlynotes.vue
<b-card>
<p class="card-text">{{ refName }}</p>
<b-link class="right" :activeTab="refName" href="/results">More</b-link>
</b-card>
...
<script>
export default {
props: {
refName: String,
},
</script>
Results.vue
<vue-tabs type="pills" v-model="tabName">
<v-tab title="Relationship">
<RelDT msg="Results view"/>
</v-tab>
<v-tab title="Support">
<SupDT msg="Results view"/>
</v-tab>
</vue-tabs>
...
<script>
import RelDT from '#/components/DataTable.rel.vue'
import SupDT from '#/components/DataTable.sup.vue'
export default {
name: 'results',
props: {
activeTab: String
},
components:
{
RelDT,
SupDT,
},
data() {
return {
tabName: activeTab
}
}
}
</script>
App
<router-link :to="{name:'results', param:{activeTab}}">Results</router-link>
How can I make this <b-link> route if it was a <route-link />?
Even the b-link component supports the :to property. To be found here
The value of the property will be passed to router.push().
<b-link :to="{name:'results', param:{activeTab}}">Redirect me</b-link>

Categories