I have two component namely App.vue and hello.vue
In App component I import the hello component and use props to pass relevant data to the hello component.
there I bind data which are took from the App component.
In my hello component I have a input box bind to the passed value.
My final goal is pass values as props to the hello component and change it and finally
pass that edited values to the backend using the save method.
How do I achive this?
This is what I have done up to now.
App.vue
<template>
<div id="app">
<hello-world :msg="'hello good morning'"></hello-world>
</div>
</template>
<script>
import helloWorld from "./components/HelloWorld";
export default {
components: {
helloWorld
}
};
</script>
hello.vue
<template>
<div>
<input type="text" :value="msg">
</div>
</template>
<script>
export default {
name: "HelloWorld",
props: {
msg: String
}
};
</script>
In my hello component's input field v-model is not possible. I want something similar to the v-model.
You cannot use prop to bind to v-model. Child component is not supposed to modify prop passed by the parent component.
You will have to create a copy of prop in your child component if you wish to use prop with v-model and then watch prop like this:
<template>
<div>
<input type="text" #input="onInput" v-model="msgCopy">
</div>
</template>
<script>
export default {
name: "HelloWorld",
props: {
msg: String
},
data() {
return { msgCopy: '' };
},
methods: {
onInput(newInputValue) {
this.$emit('msgChange', newInputValue);
}
}
watch: {
msg(newVal) {
this.msgCopy = newVal;
}
}
};
</script>
Also, notice the use of event handler #input to pass changed prop back to the parent component via event. As a syntax sugar, you can make your Hello component work as a custom form input control by adopting to v-model lifecycle.
Related
In the tutorial of vue.js, we have this code
<script>
export default {
data() {
return {
text: ''
}
},
methods: {
onInput(e) {
this.text = e.target.value
}
}
}
</script>
<template>
<input :value="text" #input="onInput" placeholder="Type here">
<p>{{ text }}</p>
</template>
And I don't understand why when I delete the bind on value, the two way binding is still working ?
In the tuto, it says that using the v-on & v-bind allow to do two way binding
Am I missing something ?
The Vue example is sort of a bad use case, a little simple for what it's trying to convey:
v-on is for assigning event listeners, so v-on:click="doSomething(value)"
v-bind is binding the actual value of vue data/state. So example:
<button v-on:click="setUserDetails(value)" v-bind:value="user.id">Click</button>
Imagine this component:
<template>
<input :value="value"/>
</template>
<script>
export default {
name: 'MyComp',
props:{
value: String
}
}
</script>
And now a simple usage of it:
<template>
<MyComp v-model="passwd" type="password" minlength="3" #focus="onFocus"/>
</template>
<script>
export default {
name: 'MyOtherComp',
data(){
return {
passwd: ''
}
},
methods:{
onFocus(){}
}
}
</script>
As you can see, value, type, and minlength properties and focus event are bidden to MyComp.
Now question: How can I handle extra props in MyComp? they are not defined in MyComp props. Vue gathers them in a special variable called $attrs, which is a normal JS object. Vue also gathers all events into $listeners variable.
Now inside MyComp these special variables are:
$atrrs:{
type: 'password',
minlength: '3'
}
$listerners:{
focus: /* function onFocus from parent */
}
To redirect these values:
<template>
<input :value="value" v-bind="$attrs" v-on="$listeners"/>
</template>
<script>
export default {
name: 'MyComp',
props:{
value: String
}
}
</script>
As you can see, we use v-bind to bind extra props, and we use v-on to bind (redirect) events. The result is:
<input :value="value" :type="$attrs.type" :minlength="$attrs.minlength" #focus="$listeners.focus"/>
Of course you can use these directions to bind you objects too:
<template>
<input :value="value" v-bind="$attrs" v-bind="accumulated" v-on="$listeners"/>
</template>
<script>
export default {
name: 'MyComp',
props:{
value: String
},
data(){
return {
accumulated:{
maxlenght: (+this.$attrs.minlength || 2) + 30, // It's just for a practice to use extra props inside JS code :-)
rows: 5,
}
}
}
}
</script>
Keep in mind that duplicate props will replace and the last one wins.
Currently trying to use a method belonging to the parent
<p class="message-date text-center">
{{ $emit('format_date_day_month_year_time', message.date) }}
</p>
However I am getting the error.
Converting circular structure to JSON
--> starting at object with constructor 'Object'
How can I call a function inside a child component that does not rely on an event? I apologize for asking such a simple question but everything I was able to find on google is using $emit and using an event.
$emit was designed to only trigger an event on the current instance of vue. Therefore, it is not possible to receive data from another component this way.
For your case, I would suggest to use Mixins especially if you need to use certain functions among multiple vue components.
Alternately, let the child component call the the parent through $emit then receive the result from the parent through a prop.
Your code could be something as follows:
Child component
<template>
<p class="message-date text-center">
{{ date }}
</p>
</template>
<script>
export default {
name: 'Child',
props: {
date: String,
},
mounted() {
this.$emit("format-date", message.date);
},
}
</script>
Parent component
<template>
<Child :date="childDate" #format-date="formatChildDate" />
</template>
<script>
import Child from '#/components/Child';
export default {
components: {
Child,
},
data: () => ({
childDate: '',
}),
methods: {
formatChildDate(date) {
this.childDate = this.formatDateDayMonthYearTime(date)
},
formatDateDayMonthYearTime(date) {
//return the formatted date
},
},
}
</script>
with $emit you call a function where the Parent can listento.
where you are using it i would suggest a computed prop of the function.
But back to your Question here is a example of emiting and listen.
//Parent
<template>
<MyComponent #childFunction="doSomethingInParent($event)"/>
</template>
//Child
<template>
<button #click="emitStuff">
</template>
.....
methods:{
emitStuff(){
this.$emit(childFunction, somedata)
}
with the event you can give Data informations to a Parentcomponent.
I am trying to create a few custom form fields for my page and i learned that i cannot use props to do so so i am trying to find a way to update my parent component variable when i use my child component. Whe i check the parent variable it is always empty.
Here is my component:
<template>
<input
v-model="value"
:placeholder="placeHolder"
class="form-field"
>
</template>
<script>
export default {
props: ['placeHolder'],
data() {
return {
value: ''
}
},
methods: {
updateValue(){
this.$emit("update-text", this.value);
}
},
watch: {
value: function(){
this.updateValue
}
}
}
</script>
And this is how i use the component:
<TextField placeholder="Nome" :update-text="name = value"/>
what exactly am i doing wrong?
I am using vue.js with nuxt.js
I think a simpler approach in this case might be emitting an input event from your custom text field and binding the component to the variable using v-model.
TextField.vue
<template>
<input
#input="$emit('input', $event.target.value)"
:placeholder="placeHolder"
class="form-field"
>
</template>
<script>
export default {
props: ['placeHolder']
}
</script>
Usage
<template>
<TextField placeholder="Nome" v-model="name"/>
</template>
<script>
export default {
data: () => ({
name: '',
}),
}
</script>
Read more about using v-model on custom components here.
I know that in Vue parents should update the children through props and children should update their parents through events.
Assume this is my parent component .vue file:
<template>
<div>
<my-child-component :category="category"></my-child-component>
</div>
</template>
<script>
export default {
data: {
return {
category: 'Test'
}
}
}
</script>
When I update the category data in this component, it will also update the category props in my-child-component.
Now, when I want to use Vue in Laravel, I usually use an inline template and pass the value from the blade directly to my components (as for example also suggested at https://stackoverflow.com/a/49299066/2311074).
So the above example my my-parent-component.blade.php could look like this:
#push('scripts')
<script src="/app.js"></script>
#endpush
<my-parent-component inline-template>
<my-child-component :category="{{ $category }}"></my-child-component>
</my-parent-component>
But now my-parent-component is not aware about the data of category. Basically only the child knows the category and there is no communication between parent and child about it.
How can I pass the data from blade without breaking the parent and child communication?
I just had to pass the category to the inline-template component through props like this:
#push('scripts')
<script src="/app.js"></script>
#endpush
<my-parent-component :initcategory="{$category}}" inline-template>
<my-child-component v-model="category"></my-child-component>
</my-parent-component>
In my-parent-component I had to set the props and initialize is using the create method:
export default {
props: {
initcategory: '',
},
data() {
return {
category: '',
};
},
created(){
this.category = this.initcategory;
}
}
Now my my-parent-component is fully aware of the category and it can communicate to the child using props and $emit as usual.
Your reference to this answer is different altogether from what you are looking for!
He's binding the :userId prop of the example component but not the parent component or in simple words: Any template using the example vue can either pass a string prop or bind :userId prop to a string variable. Following is similar:
<example :userId="{{ Auth::user()->id }}"></example>
OR
<example :userId="'some test string'"></example>
So you should rather assign {{ $category }} to a data variable but rather binds to a child component prop which will have no effect on the parent.
In the following snippet you're only binding the string but rather a data key:
<my-child-component :category="{{ $category }}"></my-child-component>
Update
See the following example which will change the h1 title after 3 seconds
// HelloWorld.vue
<template>
<app-name :name="appName" #appNameChanged="appName = $event"></app-name>
</template>
<script>
export default {
props: ['name'],
data() {
return {
appName: null
}
},
mounted() {
// NOTE: since Strings are immutable and thus will assign the value while objects and arrays are copied by reference
// the following is just for the purpose of understanding how binding works
this.appName = this.name;
}
}
</script>
The template which renders the app title or you can say the child component
// AppName.vue
<template>
<h1>{{ name }}</h1>
</template>
<script>
export default {
props: ['name'],
mounted() {
setTimeout(() => {
this.$emit('appNameChanged', 'Change App')
}, 3000);
}
}
</script>
And here's how it is being used in the welcome.blade.php
<div id="app">
<hello-world :name="'Laravel App'"></hello-world>
</div>
I have a component that takes a main <slot> from a form that is generated elsewhere in my application. I'm trying to use v-model on the form inputs but my vue component just spits out a warning about the properties not being defined, when in fact they are.
I admit it's a weird way of doing things, but it seems to be the easiest way for me to do this since my form is being generated by Symfony.
html:
<my-component>
<input ref="start" v-model="start"/>
</my-component>
my component:
<script>
export default {
data() {
start: null
},
mounted() {
console.log(this.$refs) // === {}; expected {"start":{...}}
}
}
</script>
<template>
<div>
<slot/>
... other stuff here
</div>
</template>
console log:
Property or method "start" is not defined on the instance but referenced during render
I cannot use $refs or v-model in the html. Am I doing something wrong? Or is this just not possible.
If you declare v-model="start" in the parent then it belongs to the parent and needs to be declared there. It looks like instead you declare it in the component instead as null.
If you reorder things it should work as you expect:
Parent:
<parent>
<input v-model="start" :start="start"/>
</parent>
<script>
export default {
data() {
start: null // Important to define start here if it's used in this component's html
}
}
</script>
Component:
<template>
<div>
<slot/>
... other stuff here
</div>
</template>
<script>
export default {
props: ['start'], // Receive the prop from the parent
data() {
},
mounted () {
console.log(this.start) // Should echo the value of the prop from the parent
}
}
</script>