Laravel+vue - Props is undefined - javascript

so I am new to vue, but getting up to speed. However, I cannot make the props thing to work - it always remains undefined in my child component.
The idea of the below is to create a app-wide notification modal window to display notifications.
This is my app.js
require('./bootstrap');
import ModalNotification from './components/Modal-Notification.vue';
const app = new Vue({
el: '#app',
data : {
formInputs: {},
formErrors: {},
showModal: false
},
components: {ModalNotification}
});
This is my Modal-Notification.vue
<template>
<transition name="modal">
<div class="modal-mask" #click="$emit('close')">
<div class="modal-wrapper">
<div class="modal-container" #click.stop>
<!-- <div class="modal-header">
<slot name="header">
NOTIFICATION
</slot>
</div> -->
<div class="modal-body">
<slot name="body">
Bla bla
</slot>
</div>
<div class="modal-footer">
<slot name="footer">
<button class="modal-default-button btn btn-success" #click="$emit('close')">
OK
</button>
</slot>
</div>
</div>
</div>
</div>
</transition>
</template>
<script>
export default {
name: 'ModalNotification',
data: function() {
return {
};
},
props: ['showModal'],
mounted: function() {
console.log(this);
document.addEventListener("keydown", (e) => {
console.log(this.showModal);
if (this.showModal && e.keyCode == 27) {
this.$emit('close');
}
});
},
methods: {
}
}
</script>
And the relevant part of app.blade.php
<div class="container-fluid" id="app">
<button #click="showModal = true" class="btn btn-default">MODAL</button>
<modal-notification v-if="showModal" #close="showModal = false" :showModal="false">
<p slot="body" id="notification-message">hehe</p>
</modal-notification>
<div id="wrapper">
#yield('sidebar')
#yield('content')
</div>
</div>
I've tried everything out there, except switching to Browserify, babel and such stuff, but I don't think it should be needed - webpack should work just fine.
Please help, if you can.

You have mistake in the following snippet of code:
<modal-notification v-if="showModal" #close="showModal = false" :showModal="false">
<p slot="body" id="notification-message">hehe</p>
</modal-notification>
:showModal="false" is basically shortcut of v-bind:showModal="false", which tries to search vue instance variable in the value of attached HTML property(documentation). as you are passing false which is not a vue data variable, it is just passing null in showModal props.
If you want to pass only false, change the code to following:
<modal-notification v-if="showModal" #close="showModal = false" showModal="false">
<p slot="body" id="notification-message">hehe</p>
</modal-notification>
Edited
I think it is magic of camelCase-vs-kebab-case:
HTML attributes are case-insensitive, so when using non-string templates, camelCased prop names need to use their kebab-case (hyphen-delimited) equivalents:
You need to pass : show-modal="false"
<modal-notification v-if="showModal" #close="showModal = false" show-modal="false">
<p slot="body" id="notification-message">hehe</p>
</modal-notification>

Related

change data value in vue component

I'm starting with vue and I'm making a test to change a value when a button is clicked , but is not working
<template>
<div class="row">
{{ show }}
<div class="col-md-4">
<button class="btn btn-primary" #click="change_show">Good</button>
</div>
<div class="col-md-4">
<button class="btn btn-primary">Bad</button>
</div>
<div class="col-md-4">
<button class="btn btn-primary">Food</button>
</div>
</div>
</template>
<script>
export default {
name: "buttons",
data(){
return{
show: true
}
},
methods:{
change_show(event){
show = !show;
}
}
}
</script>
<style scoped>
</style>
I get this error
Uncaught ReferenceError: show is not defined
How I can access to the variables and change it?
You have declared your variable incorrectly, you should add show (and all your reactive variables) in the return:
data(){
return{
show: true
};
},

vue button related event not fired

In:
https://codesandbox.io/s/upbeat-hodgkin-qjt61?file=/src/components/EditCategory.vue
the modal is shown as expected upon long click over a category:
but clicking OK does not fire the close event:
<template>
<div>
<p v-longclick="() => longClicked()" #click="longClicked()">
{{ taskItemLocal["name"] }}
</p>
<div v-if="this.showModal" #close="closeModal()">
<transition name="modal">
<div class="modal-mask">
<div class="modal-wrapper">
<div class="modal-container">
<div class="modal-header">
<slot name="header"> Edit Category </slot>
</div>
<div class="modal-body">
<slot name="name"> Edit Name </slot>
</div>
<div class="modal-body">
<slot name="delete"> Delete Category </slot>
</div>
<div class="modal-footer">
<slot name="footer">
<!-- default footer -->
<!-- EVENT NOT FIRING -->
<button class="modal-default-button" #click="$emit('close')">
OK
</button>
</slot>
</div>
</div>
</div>
</div>
</transition>
</div>
</div>
</template>
closeModal() is not called; changing showModal "directly" also fails.
You have dispatch event to parent but in parent component you have not done any thing with "close" event. Here, in GenericItem.vue I have made event listener with #close="closeBox($event)" . Here, it will trigger method of closeBox
GenericItem.vue
Changes on Template
<edit-category
v-if="editCategoryStatus"
:taskItem="taskItemLocal"
#close="closeBox($event)"
/>
Add one closeBox method
closeBox() {
this.editCategoryStatus = !this.editCategoryStatus;
},
Add editCategoryStatus on data
data() {
return {
editCategoryStatus: true,
taskItemLocal: {
type: Object,
default: {},
},
};
If you want to listen to an event within the component that emitted that event, you use the instance $on method:
mounted() {
this.$on("close", () => {
this.closeModal();
});
}
The template event handler #close="closeModal()" is used to listen to events from parent. It has no effect within the child component.
The working codesandbox: https://codesandbox.io/s/loving-kirch-vrhwn?file=/src/components/EditCategory.vue .
You could just make your button like this. You made this more complicated than it should be
<button class="modal-default-button" #click="showModal = false">
Also, there is this example from the official docs
here.

Bind element inside a for loop Vue not working properly

In the following Vue Component I want to loop through dwarfs array. And as long as I am in the current component, everything is fine (TEST) and also all the following properties are correct.
Currenct_Component.vue :
<template>
<div>
<h2>Stamm: {{ tribeName }}</h2>
<div class="card-container">
<div class="card" style="width: 18rem;" v-for="dwarf in dwarfs" :key="dwarf.name">
<!-- TEST -->
<p>{{dwarf}}</p>
<!-- CHILD COMPONENT -->
<app-modal
:showModal="showModal"
:targetDwarf="dwarf"
#close="showModal = false"
#weaponAdded="notifyApp"
/>
<!-- <img class="card-img-top" src="" alt="Card image cap">-->
<div class="card-body">
<h3 class="card-title" ref="dwarfName">{{ dwarf.name }}</h3>
<hr>
<ul class="dwarf-details">
<li><strong>Alter:</strong> {{ dwarf.age }}</li>
<li><strong>Waffen:</strong>
<ul v-for="weapon in dwarf.weapons">
<li><span>Name: {{ weapon.name }} | Magischer Wert: {{ weapon.magicValue }}</span></li>
</ul>
</li>
<li><strong>Powerfactor:</strong> {{ dwarf.weapons.map(weapon => weapon.magicValue).reduce((accumulator, currentValue) => accumulator + currentValue) }}</li>
</ul>
<button class="card-button" #click="showModal = true"><span class="plus-sign">+</span> Waffe</button>
</div>
</div>
</div>
<button id="backBtn" #click="onClick">Zurück</button>
</div>
</template>
<script>
import Modal from './NewWeaponModal.vue';
export default {
data() {
return {
showModal: false,
}
},
components: { appModal : Modal },
props: ['tribeName', 'dwarfs'],
methods: {
onClick() {
this.$emit('backBtn')
},
notifyApp() {
this.showModal = false;
this.$emit('weaponAdded');
}
},
}
</script>
But when I bind the element dwarf to the Child Component <app-modal/> it changes to the next dwarf in the array dwarfs (TEST) - (So as the result when i add a new weapon in the modal-form it gets added to the second dwarf...):
Child_Component.vue :
<template>
<div>
<div class="myModal" v-show="showModal">
<div class="modal-content">
<span #click="$emit('close')" class="close">×</span>
<h3>Neue Waffe</h3>
<!-- TEST -->
<p>{{ targetDwarf }}</p>
<form>
<input
type="text"
placeholder="Name..."
v-model="weaponName"
required
/>
<input
type="number"
placeholder="Magischer Wert..."
v-model="magicValue"
required
/>
<button #click.prevent="onClick">bestätigen</button>
</form>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
weaponName: '',
magicValue: '',
}
},
props: ['showModal', 'targetDwarf'],
methods: {
onClick() {
if(this.weaponName !== '' &&
Number.isInteger(+this.magicValue)) {
let newData = {...this.dwarf};
newData['weapons'] = [
...this.dwarf['weapons'],
{
"name": this.weaponName,
"magicValue": Number.parseInt(this.magicValue)
},
];
this.$http.post("https://localhost:5019/api", newData)
.then(data => data.text())
.then(text => console.log(text))
.catch(err => console.log(err));
this.$emit('weaponAdded');
} else {
alert('You should fill all fields validly')
}
},
}
}
</script>
It looks like you have the <app-modal/> component inside of the v-for="dwarf in dwarfs" loop, but then the control for showing all of the modal components created by that loop is just in one variable: showModal. So when showModal is true, the modal will show each of the dwarfs, and I'm guessing the second dwarf's modal is just covering up the first one's.
To fix this, you could move the <app-modal /> outside of that v-for loop, so there's only one instance on the page, then as part of the logic that shows the modal, populate the props of the modal with the correct dwarf's info.
Something like this:
<div class="card-container">
<div class="card" v-for="dwarf in dwarfs" :key="dwarf.name">
<p>{{dwarf}}</p>
<div class="card-body">
<button
class="card-button"
#click="() => setModalDwarf(dwarf)"
>
Waffe
</button>
</div>
</div>
<!-- Move outside of v-for loop -->
<app-modal
:showModal="!!modalDwarfId"
:targetDwarf="modalDwarfId"
#close="modalDwarfId = null"
#weaponAdded="onDwarfWeaponAdd"
/>
</div>
export default {
//....
data: () => ({
modalDwarfId: null,
)},
methods: {
setModalDwarf(dwarf) {
this.modalDwarfId = drawf.id;
},
onDwarfWeaponAdd() {
//...
}
},
}
You could then grab the correct dwarf data within the modal, from the ID passed as a prop, or pass in more granular data to the modal so it's more "dumb", which is the better practice so that the component isn't dependent on a specific data structure. Hope that helps
Courtesy of #Joe Dalton's answer, a bit alternated for my case:
<div class="card" style="width: 18rem;" v-for="dwarf in dwarfs" :key="dwarf.name">
...
<button class="card-button" #click="setModalDwarf(dwarf)"><span class="plus-sign">+</span> Waffe</button>
<div>
<app-modal
:showModal="showModal"
:targetDwarf="currentDwarf"
#close="showModal = false"
#weaponAdded="notifyApp"
/>
<script>
import Modal from './NewWeaponModal.vue';
export default {
data() {
return {
showModal: false,
currentDwarf: null,
}
},
components: { appModal : Modal },
props: ['tribeName', 'dwarfs'],
methods: {
setModalDwarf(dwarf) {
this.currentDwarf = dwarf;
this.showModal = true;
},
...
}
</script>

JavaScript : this.$emit('closeRequest') is not create any event in vue dev tool

I am following a tutorial and they doing a project. everything is perfect and i was doing well. but i face an error. i have a component (Home.vue) and inside component there have a button. if user click the button then a modal will open (using BULMA scss framework and there is no js). when button is clicked then another component (Add.vue) will appear where actual modal code is writed. i pass a property Home.vue to Add.vue via <Add></Add> Component. this property value is tell modal will active or not.
here is home.vue
<template>
<div>
<nav class="panel column is-offset-2 is-8">
<p class="panel-heading">
VueJs Phonebook
<button class="button is-link is-outlined" #click="openAdd">
Add New
</button>
</p>
</nav>
<Add :openmodal='addActive'></Add>
</div>
</template>
<script>
let Add = require('./Add.vue')
export default {
components:{Add},
data(){
return{
addActive:''
}
},
methods:{
openAdd(){
this.addActive = 'is-active';
}
}
}
</script>
i am successfully get the property in Add.vue but modal has cancle and close button. i use to write a method called close. if button is clicked the this.$emit('closeRequest'); should make a event in vue dev tool. but it do not create any event if cancle or close button was clicked.
here is Add.vue file
<template>
<div class="modal" :class="openmodal">
<div class="modal-background"></div>
<div class="modal-card">
<header class="modal-card-head">
<p class="modal-card-title">Modal title</p>
<button class="delete" aria-label="close" #click='close'>
</button>
</header>
<section class="modal-card-body">
<!-- Content ... -->
</section>
<footer class="modal-card-foot">
<button class="button is-success">Save changes</button>
<button class="button" #click='close'>Cancel</button>
</footer>
</div>
</div>
</template>
<script>
export default{
props:['openmodal'],
methods:{
close(){
this.$emit('closeRequest');
},
}
}
</script>
<template>
<div>
<nav class="panel column is-offset-2 is-8">
<p class="panel-heading">
VueJs Phonebook
<button class="button is-link is-outlined" #click="openAdd">
Add New
</button>
</p>
</nav>
<Add :openmodal='addActive' #closeRequest='closeAdd'></Add>
</div>
</template>
<script>
let Add = require('./Add.vue')
export default {
components:{Add},
data(){
return{
addActive:''
}
},
methods:{
openAdd(){
this.addActive = 'is-active';
},
closeAdd(){
this.addActive = '';
}
}
}
</script>

Passing functions as props to child components

Assuming I have a component named Modal which I want to reuse across my application and in the meanwhile I also want to dynamically bind functions to its Yes button, how can I pass a function to the #click event of the Yes button in my Modal as a prop. For instance:
//data tags are used for fast markup here only
<tamplate>
<div>
<div :id="`${id}`" data-modal>
<div data-modal-title>
{{title}}
</div>
<div data-modal-body>
{{body}}
</div>
<div data-modal-footer>
<button #click="//event to be passed//" data-modal-button-ok>Yes</button>
<button data-modal-button-cancel>Yes</button>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'modal',
props: [
'id',
'title',
'body',
// event?
]
}
</script>
and when using this modal, how should the event be passed?
<Modal id="x" title="y" body="x" //event="????"//></Modal>
You should $emit an event (name, say, yes) at the modal:
<button #click="$emit('yes')" data-modal-button-ok>Yes</button>
Or, use a method:
<button #click="handleYesClick" data-modal-button-ok>Yes</button>
methods: {
handleYesClick() {
this.$emit('yes');
}
}
And listen to it in the parent using:
<modal ... v-on:yes="someCodeToExecute"></modal>
or (shorthand):
<modal ... #yes="someCodeToExecute"></modal>
Demo:
Vue.component('modal', {
template: "#modal",
name: 'modal',
props: ['id', 'title', 'body']
})
new Vue({
el: '#app',
data: {},
methods: {
methodAtParentYes() {
alert('methodAtParentYes!');
},
methodAtParentCancel() {
alert('methodAtParentCancel!');
}
}
})
<script src="https://unpkg.com/vue"></script>
<template id="modal">
<div>
<div :id="`${id}`" data-modal>
<div data-modal-title>
{{title}}
</div>
<div data-modal-body>
{{body}}
</div>
<div data-modal-footer>
<button #click="$emit('yes')" data-modal-button-ok>Yes</button>
<button #click="$emit('cancel')" data-modal-button-cancel>Cancel</button>
</div>
</div>
</div>
</template>
<div id="app">
<modal id="1" title="My Title" body="Body" #yes="methodAtParentYes" #cancel="methodAtParentCancel"></modal>
</div>
There are two ways to do this.
The first way is you could pass the method down as a prop, just like you would pass anything else and then in the Modal component just call that prop right in the click handler.
Parent.vue
<template>
<Modal id="x" title="y" body="x" :handleYes="handleYes"></Modal>
</template>
<script>
methods: {
handleYes () {
// do something
}
}
</script>
Modal.vue
<button #click="handleYes()">Yes</button>
The other way is to use $emit. So in Modal.vue you would define a method to emit an event and then listen for that event in the parent and do call the method there.
Modal.vue
<template>
<button #click="emitEvent">Yes</button>
</template>
<script>
methods: {
emitEvent () {
this.$emit('userClickedYes')
}
}
</script>
Parent.vue
<template>
<Modal id="x" title="y" body="x" #userClickedYes="handleYes"></Modal>
</template>
<script>
methods: {
handleYes () {
// do something
}
}
</script>

Categories

Resources