fire parent method on child component value change vuejs - vue.js

i want to fire a parent method on child component value change...
i pasted code below
first section is parent component and second is child component.... when i change value of ifsc field i want to fire getIfscCode() method.
Problem: when event happen getIfscCode() method not fire may be cause of i not passed any props to the child element but you can suggest me that how to do it.
parent component
<template>
<div class="bg-gray-200 h-full">
<header-view></header-view>
<div class="px-32 py-6">
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Id nemo quam voluptates tenetur atque, similique odio quasi assumenda, quo porro deleniti eos nobis officia tempore, at harum odit. Illo, doloribus.</p>
</div>
<h2 class="text-2xl text-center about">Apply for Subsidy</h2>
<div class="w-full h-full flex justify-center mt-16">
<form class="w-full max-w-lg">
<text-input id="name" placeholder="Enter Name" title="Name"></text-input>
<text-input id="fathername" placeholder="Enter Father Name" title="Father Name"></text-input>
<text-input id="phone" placeholder="Enter Active Mobile No" title="Mobile No"></text-input>
<text-input id="aadhar" placeholder="Enter Aadhar No" title="Aadhar No"></text-input>
<text-input id="accountno" placeholder="Enter Account No" title="Bank Account No"></text-input>
<text-input
id="ifsc"
placeholder="Enter IFSC Code"
title="Bank Branch IFSC Code"
v-model="data.ifsc"
#change="getIfscCode(data.ifsc)"
></text-input>
<text-input id="accountname" placeholder="Enter Account Name" title="Bank Account Name"></text-input>
</form>
</div>
<footer-view></footer-view>
</div>
</template>
<script>
import HeaderView from "./header/Header";
import FooterView from "./footer/Footer";
import TextInput from "./form/TextInput";
export default {
components: {
HeaderView,
FooterView,
TextInput
},
data() {
return {
data: {
name: "",
ifsc: ""
}
};
},
created() {},
methods: {
getIfscCode(code) {
axios
.get(
"https://mfapps.indiatimes.com/ET_Calculators/getBankDetailsByIfsc.htm?ifsccode=" +
code +
"&callback=objIFSC.getIfscData"
)
.then(res => {
console.log(res.data);
});
}
}
};
</script>
child component
<template>
<div class="flex flex-wrap mb-6">
<div class="w-full px-3">
<label
class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2"
:for="id"
>{{title}}</label>
<input
class="appearance-none block w-full bg-gray-900 p-text-color border border-gray-200 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white focus:border-gray-500 placeholder-blue-300"
:id="id"
type="text"
:placeholder="placeholder"
:value="inputvalue"
#input="$emit('update', $event.target.value)"
/>
</div>
</div>
</template>
<script>
export default {
props: {
title: {
type: String
},
id: {
type: String
},
placeholder: {
type: String
},
inputvalue: undefined
},
model: {
prop: "inputvalue",
event: "update"
}
};
</script>

You emit update event but you catch change event. Also because you don't emit input event then v-model in a parent component will not work.
Try to correct like this:
child component
"$emit('input', $event.target.value)"
parent component
#input="getIfscCode($event)"

Related

Emit a second variable from Vue component to parent (vue / inertiajs)

I have the following component, I would like to emit a second value isDeleted to the parent:
<template>
<div class="flex items-center">
<button class="btn my-auto inset-y-0 ml-1 mr-3"
:class="isDeleted ? 'btn-danger' : 'btn-secondary'"
#click="switchMode"
>
<Icon name="Trash" class="w-4 h-4" />
</button>
<div class="w-56 relative text-slate-500 mr-2">
<input
id="tabulator-html-filter-value"
name="search"
type="text"
autocomplete="off"
class="form-control w-56 box pr-10"
aria-label="default input example"
:value="modelValue"
#input="$emit('update:modelValue', $event.target.value)"
placeholder="Search..."
/>
<Icon name="Search" class="w-4 h-4 absolute my-auto inset-y-0 mr-3 right-0"/>
</div>
<button
id="tabulator-html-filter-reset"
type="button"
class="btn btn-secondary w-full sm:w-16 mt-2 sm:mt-0 sm:ml-1"
#click="$emit('reset')"
>
Reset
</button>
</div>
</template>
<script>
import Dropdown from '#/components/Dropdown/Dropdown'
import Icon from '#/components/Icons/Icons'
export default {
components: {
Dropdown,
Icon
},
props: {
modelValue: String,
maxWidth: {
type: Number,
default: 300,
},
},
data() {
return {
isDeleted: null,
}
},
emits: ['update:modelValue', 'reset'],
methods: {
switchMode() {
this.isDeleted = this.isDeleted ? null : 'only';
console.log('1'+ this.isDeleted);
}
}
}
</script>
The parent has the following code:
<search-filter v-model="form.search" class="mr-4 w-full max-w-md" #reset="reset" />
I tried following this example: https://dev.to/codybontecou/vuejs-custom-event-emit-multiple-values-221b to add emit the second value isDeleted without success.
Thanks
In child
<button
id="tabulator-html-filter-reset"
type="button"
class="btn btn-secondary w-full sm:w-16 mt-2 sm:mt-0 sm:ml-1"
#click="$emit('reset', isDeleted /* <---- Pass your value */)"
>
In parent
<search-filter
v-model="form.search"
class="mr-4 w-full max-w-md"
#reset="reset($event /* <---- Here got your value */)"
/>
const reset = (isDeleted) =>
{
console.log(isDeleted)
}

Using file and array in bootstrap vue

I have the following code on a form
<template>
<base-layout :layout-classes="layoutClasses">
<div class="container mt-3">
<div v-if="storing">
<h3>Please wait....</h3>
</div>
<div class="content">
<base-block rounded title="Create Center">
<b-form #submit.prevent="AddForm" class="mb-5">
<b-form-group label-cols-sm="2" label="English Name">
<b-form-input v-model="name.en" class="form-control-alt" type="text" placeholder="English name"></b-form-input>
</b-form-group>
<b-form-group label-cols-sm="2" label="French Name">
<b-form-input v-model="name.fr" class="form-control-alt" type="text" placeholder="French Name"></b-form-input>
</b-form-group>
<b-form-group label-cols-sm="2" label="Italian Name">
<b-form-input class="form-control-alt" v-model="name.it" type="text" placeholder="Italian Name"></b-form-input>
</b-form-group>
<b-form-group label="Image" label-cols-sm="2">
<b-form-file :state="Boolean(image)" plain v-model="image"></b-form-file>
</b-form-group>
<b-form-group label="" label-cols-sm="2">
<b-button type="submit" class="text-white" variant="dark">Submit</b-button>
<b-button type="reset" class="text-white" variant="danger" #click="ResetForm()">Reset</b-button>
</b-form-group>
</b-form>
</base-block>
</div>
</div>
</base-layout>
</template>
<script>
export default {
data () {
return {
name: [],
image: null,
storing: false,
}
},
methods: {
AddForm ()
{
this.submitted = true
this.storing = true
this.errors = {}
var self = this
axios.post('/form', this.form)
.then(function(response){
const status = response.status;
self.$router.push('/forms')
})
.catch(err => {
if (status == 401) {
self.$router.push('/login')
}
Object.assign(this.errors, {}, err.response.data.errors)
})
.finally(() => {
this.storing = false
})
},
}
}
</script>
Name field should come in array but when I console this.form all the values are empty. What am I doing wrong here? How should I give name to the field if the field should inside a same name attribute? How does the input file work when it come to vue?
You don't have "form" in data object and input's v-model don't have any binding with form. So:
// update v-model of all inputs
<b-form-input v-model="form.name.en" class="form-control-alt" type="text" placeholder="English name"></b-form-input>
// add form to data object:
form: {
name: {
en: "",
fr: ""
},
image: ""
}

Props being parsed undefined?

Have this very simple vue component where I am trying to accept a search string as a prop
Vue.component('search-person', {
props: ['searchStr'],
created(){
console.log(this)
},
template: `<div class="container">
<form class="search-person-form">
<input type="text" class="search-person-input" placeholder="Search person" v-model="searchStr">
<div v-if="!searchStr.length">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 32 32" class="search-person-icon">
<title>Search</title>
<path d="M32 31.060l-12.233-12.24c1.777-1.992 2.863-4.634 2.863-7.53 0-6.259-5.074-11.333-11.333-11.333s-11.333 5.074-11.333 11.333c0 6.259 5.074 11.333 11.333 11.333 2.896 0 5.538-1.086 7.541-2.873l-0.011 0.010 12.233 12.24zM1.293 11.333c0-5.523 4.477-10 10-10s10 4.477 10 10c0 5.509-4.454 9.977-9.958 10h-0.002c-0 0-0 0-0 0-5.531 0-10.017-4.472-10.040-9.998v-0.002z"/>
</svg>
</div>
<div v-else >
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 32 32" class="search-person-icon pointer">
<title>cross</title>
<path d="M26 6.96l-0.96-0.96-9.040 9.040-9.040-9.040-0.96 0.96 9.040 9.040-9.040 9.040 0.96 0.96 9.040-9.040 9.040 9.040 0.96-0.96-9.040-9.040 9.040-9.040z"/>
</svg>
</div>
</form>
</div>`
})
I am then trying to use the component and parse a search string that is part of the data as a prop
<search-person :searchStr="personSearchStr"/>
personSearchStr is a part of the data object of the component that is nesting the search-person component but in my search-person component the searchStr prop is undefined
Following up on my comment, I created an example scenario with a Search Input component and it's Parent container.
The initial value of the search input (child) comes from a prop set in the parent. The search input watches the prop, so when the parent value is changed (via an input in the parent in my example), the child updates it's local copy. This happens as the user types (no form submission in parent).
When the search input (child) value is changed and the child form is submitted, an event is emitted to send the child value to update the parent.
Parent.vue
<template>
<div class="parent">
<h4>Parent Component</h4>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="prop-value">Enter new prop value</label>
<input type="text" class="form-control" id="prop-value" v-model="parentSearch">
</div>
<hr>
</div>
</div>
<search-input :searchFromParent="parentSearch" #update-search-event="updateSearch" />
</div>
</template>
<script>
import SearchInput from './SearchInput.vue'
export default {
components: {
SearchInput
},
data() {
return {
parentSearch: 'Search value from parent prop'
}
},
methods: {
updateSearch(searchFromChild) {
this.parentSearch = searchFromChild;
}
}
}
</script>
SearchInput.vue
<template>
<div class="search-input">
<h5>Search Input Component (child)</h5>
<div class="row">
<div class="col-md-6">
<form #submit.prevent="submitForm">
<div class="form-group">
<input type="text" class="form-control" id="search-input" v-model="localSearch">
</div>
<button type="submit" class="btn btn-secondary">Submit search value to parent</button>
</form>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
searchFromParent: {
type: String,
required: true
}
},
data() {
return {
localSearch: this.searchFromParent
}
},
watch: {
searchFromParent(newValue) {
this.localSearch = newValue;
}
},
methods: {
submitForm() {
this.$emit('update-search-event', this.localSearch)
}
}
}
</script>

Vue-formulate - Group item collapsible / toggle collapse

Is there a possibility to make group item collapsible?
<FormulateInput type="group" name="employments" :repeatable="true" label="Employments"
add-label="+ Add Employment" #default="groupProps">
<!-- Clickable area -->
<div class="group text-sm font-semibold py-2 cursor-pointer relative" #click="groupProps.showForm">
....
</div>
<!-- Nested form: must be collapsible accordion -->
<div class="nested-form" v-show="groupProps.showForm">
....
</div>
</FormulateInput>
I thought to add showForm property to the group context.
For this I need to do Custom input types or is there some other way?
If anyone has any other ideas?
Thanks
I figured it out with the gist of #jpschroeder.
CollapsableGroupItem.vue:
<template>
<div class="group-item" :data-is-open="itemId === showIndex">
<div class="group group-item-title text-sm font-semibold py-2 cursor-pointer relative hover:text-blue-400" #click="toggleBody">
<slot name="title" v-bind="groupItem">#{{ context.index }}</slot>
</div>
<div class="group-item-body" v-show="itemId === showIndex">
<slot name="body">
<FormulateInput type="pelleditor" name="description" label="Description"/>
</slot>
</div>
</div>
</template>
<script>
export default {
name: "CollapsableGroupItem",
props: {
context: {
type: Object,
required: true,
},
showIndex: {
type: [Number, String],
required: true,
},
groupItem: {
type: Object,
required: true,
},
},
data () {
return {
itemId: this.context.name + this.context.index
}
},
created: function () {
// show current item
this.$emit("open", this.itemId);
},
methods: {
toggleBody() {
if (this.itemId === this.showIndex) {
// dont show anything
this.$emit("open", -1);
} else {
// show this one
this.$emit("open", this.itemId);
}
},
}
};
FormTemplate.vue:
<CollapsableGroupItem
:context="context"
:show-index="showIndex"
:group-item="educations[context.index]"
#open="showIndex = $event"
>
<template v-slot:title="education">
<span v-if="education.institution || education.degree"
>
{{ education.institution }}
<span v-if="education.institution && education.degree">at</span>
{{ education.degree }}
</span>
...
</template>
<template v-slot:body>
...
</template>
</CollapsableGroupItem>
Maybe it will help someone else or will be useful 😀

Vue cli 3 props (parent to child) child never update after the variable in parent has changed

I tried to make a chat app using Vue CLI 3 and I have finished making a real-time chat room. Then, I tried to give a citing function to it which users can cite the message before and reply to it. So, I manage to pass the cited message to the child component by props. The cited message was NULL by default. After the user clicked some buttons, I expected the value of the "cited message" would change and the new value would be passed to the child through props (automatically updated). But, in fact, it didn't.
When I was browsing the internet, I did find several questions about updating the child component when props values change. So, I tried watch:, created(), update(), but none of them worked.
I once tried to directly add an element <p> in the child component and put {{cited_message}} in it to see what was inside the variable. Then, the Vue app crashed with a white blank page left (but the console didn't show any error).
For convenience, I think the problem is around:
<CreateMessage:name="name":cited_message="this.cited_message"#interface="handleFcAfterDateBack"/>
OR
props: ["name","cited_message"],
watch: { cited_message: function (newValue){ this.c_message = newValue; } },
You can ctrl+F search for the above codes to save your time.
Parent component:
<template>
<div class="container chat">
<h2 class="text-primary text-center">Real-time chat</h2>
<h5 class="text-secondary text-center">{{ name }}</h5>
<div class="card" style="min-height: 0.8vh">
<div class="card-body">
<p class="text-secondary nomessages" v-if="messages.length == 0">
[no messages yet!]
</p>
<div class="messages" v-chat-scroll="{ always: false, smooth: false }">
<div v-for="message in messages" :key="message.id">
<div v-if="equal_name(message)">
<div class="d-flex flex-row">
<div class="text-info">
[ {{ message.name }} ] : {{ message.message }}
</div>
<div class="btn-group dropright">
<a
class="btn btn-secondary btn-sm dropdown-toggle"
href="#"
role="button"
id="dropdownMenuLink"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
</a>
<div class="dropdown-menu" aria-labelledby="dropdownMenuLink">
<button
#click="get_cited_message(message)"
:key="message.id"
class="dropdown-item"
href="#"
>
Cite
</button>
</div>
</div>
<div class="text-secondary time">
<sub>{{ message.timestamp }}</sub>
</div>
</div>
<!--below is for cited message-->
<div v-if="message.cited_message" class="d-flex flex-row">
Cited : {{ message.cited_message }}
</div>
</div>
<div v-else>
<div class="d-flex flex-row-reverse">
<div class="text-info">
[ {{ message.name }} ] : {{ message.message }}
</div>
<div class="text-secondary time">
<sub>{{ message.timestamp }}</sub>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card-action">
<CreateMessage
:name="name"
:cited_message="this.cited_message"
#interface="handleFcAfterDateBack"
/>
</div>
</div>
</div>
</template>
<script>
import CreateMessage from "#/components/CreateMessage";
import fb from "#/firebase/init.js";
import moment from "moment";
export default {
name: "Chat",
props: {
name: String,
},
components: {
CreateMessage,
},
methods: {
equal_name(message) {
if (message.name == this.name) {
return true;
} else {
return false;
}
},
get_cited_message(message) {
this.cited_message = message.message;
console.log(this.cited_message);
},
handleFcAfterDateBack(event) {
console.log("data after child handle: ", event);
},
},
data() {
return {
messages: [],
cited_message: null,
};
},
created() {
let ref = fb.collection("messages").orderBy("timestamp");
ref.onSnapshot((snapshot) => {
snapshot.docChanges().forEach((change) => {
change.type = "added";
if (change.type == "added") {
let doc = change.doc;
this.messages.push({
id: doc.id,
name: doc.data().name,
message: doc.data().message,
timestamp: moment(doc.data().timestamp).format(
"MMMM Do YYYY, h:mm:ss a"
),
cited_message: doc.data().cited_message,
});
}
});
});
},
};
</script>
<style>
.chat h2 {
font-size: 2.6em;
margin-bottom: 0px;
}
.chat h5 {
margin-top: 0px;
margin-bottom: 40px;
}
.chat span {
font-size: 1.2em;
}
.chat .time {
display: block;
font-size: 0.7em;
}
.messages {
max-height: 300px;
overflow: auto;
text-align: unset;
}
.d-flex div {
margin-left: 10px;
}
</style>
Child component:
<template>
<div class="container" style="margin-bottom: 30px">
<form #submit.prevent="createMessage()">
<div class="form-group">
<input
type="text"
name="message"
class="form-control"
placeholder="Enter your message"
v-model="newMessage"
/>
<p v-if="c_message" class="bg-secondary text-light">Cited: {{c_message}}</p>
<p class="text-danger" v-if="errorText">{{ errorText }}</p>
</div>
<button class="btn btn-primary" type="submit" name="action">
Submit
</button>
</form>
</div>
</template>
<script>
import fb from "#/firebase/init.js";
import moment from "moment";
export default {
name: "CreateMessage",
props: ["name","cited_message"],
watch: {
cited_message: function (newValue){
this.c_message = newValue;
}
},
data() {
return {
newMessage: "",
errorText: null,
c_message: null
};
},
methods: {
createMessage() {
if (this.newMessage) {
fb.collection("messages")
.add({
message: this.newMessage,
name: this.name,
timestamp: moment().format(),
cited_message: this.c_message
})
.then(function(docRef) {
console.log("Document written with ID: ", docRef.id);
})
.catch((err) => {
console.log(err);
});
this.newMessage = null;
this.errorText = null;
} else {
this.errorText = "Please enter a message!";
}
},
},
beforeMount(){
this.c_message = this.cited_message;
}
};
</script>
Side-note: In the parent component, I only made the dropdown menu for the messages on the left-hand side. If this thread solved, I would finish the right-hand side.
It is solved. I think the problem is that the child component didn't re-render when the variable in parent component updated. Only the parent component was re-rendered. So, the props' values in the child remained the initial values. In order to solve this, binding the element with v-bind:key can let the Vue app track the changes of the variable (like some kind of a reminder that reminds the app to follow the changes made on the key). When the variable(key) changes, the app will be noticed and the new value will be passed to the child.
E.g.
Original
<CreateMessage
:name="name"
:cited_message="this.cited_message"
#interface="handleFcAfterDateBack"
/>
Solved
<CreateMessage
:name="name"
:cited_message="this.cited_message"
#interface="handleFcAfterDateBack"
:key="this.cited_message"
/>
Even though the problem is solved, I don't know whether I understand the problem clearly. If I made any mistakes, please comment and let me know.