How databind a textarea component text value and update it? - javascript

I've been working on VueJS just 1 weeks ago for a project.
I've created two components:
* Account.vue (Parent)
<!--It's just a little part of the code-->
<e-textarea
title="Informations complémentaires"
#input="otherInformation" <!--otherInformation is a string variable which contains the text value-->
:value="otherInformation"></e-textarea>
TextArea.vue (Children Component)
<template>
<div class="form-group">
<label for="e-textarea">{{ title }}</label>
<textarea
id="e-textarea"
class="form-control"
row="3"
:value="value"
v-on="listeners"
>
</textarea>
</div>
</template>
<script>
import { FormGroupInput } from "#/components/NowUiKit";
export default {
name: "e-textarea",
components: {
[FormGroupInput.name]: FormGroupInput
},
props: {
title: String,
value: String
},
computed: {
listeners() {
return {
...this.$listeners,
input: this.updateValue
};
}
},
methods: {
updateValue(value) {
this.$emit("input", value);
}
},
mounted() {
console.log(this.components);
}
};
</script>
<style src="#/assets/styles/css/input.css" />
When I write something in my TextArea Custom component from my Account.vue, my text value does not update and my listener function is not passed. Does I need to have something else?

You can easily do this by v-model:
<textarea
id="e-textarea"
class="form-control"
row="3"
v-model="value"
>
</textarea>
it's equals to:
<textarea
id="e-textarea"
class="form-control"
:value="value"
#input="value = $event.target.value"> </textarea>

Bind the value in your custom textarea and the input event:
CustomTextarea.vue
<template>
<div class="form-group">
<label for="e-textarea">{{ title }}</label>
<textarea
id="e-textarea"
class="form-control"
row="3"
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
</textarea>
</div>
</template>
<script>
import { FormGroupInput } from "#/components/NowUiKit";
export default {
name: "e-textarea",
components: {
[FormGroupInput.name]: FormGroupInput
},
model: {
prop: "textAreaVue"
},
props: {
title: String,
value: String
},
computed: {
listenerFunction() {
return {
...this.$listener,
input: this.updateValue
};
}
},
methods: {
updateValue(value) {
console.log("function has been passed");
this.$emit("input", value);
}
},
mounted() {
console.log(this.components);
}
};
</script>
<style src="#/assets/styles/css/input.css" />
And use it with v-model :
<custom-textarea
title="Informations complémentaires"
v-model="otherInformation"></custom-textarea>
More explanation here

Hopefully it will save someone some time. In Vue.js 3 they changed this a bit compared the Vue.js 2 and i got it working with this:
<textarea :value="modelValue"
#input="onInput" />
and the onInput method looks like this:
onInput: function (e) {
this.$emit("update:modelValue", e.target.value);
}
of course you have to have the prop 'modelValue' on your 'e-textarea' component instead of value as it was the case in vue 2.
i also added a watcher on this modelValue just in case it fails to update:
props: {
modelValue: {
type: String,
default: null
}
}
watch: {
modelValue: function () {
return this.modelValue;
},
},
and yes, you use the component like so:
<e-textarea v-model="otherInformation" />
of course you can listen to the input event also if you want to, or need to for your specific case
you can also find more about this changes on
here
and here

Related

VueJs 3 - Custom Input Component

I'm trying to build a custom HTML <input> component for VueJS3. I've been following this tutorial:
https://dev.to/viniciuskneves/vue-custom-input-bk8
So far I managed to get the CustomInput.vue component to work and emit the modified value back to the parent App.Vue.
<template>
<label>
{{ label }}
<input type="text" :name="name" :value="value" #input="onInput" #change="onChange" />
</label>
</template>
<script>
export default {
name: 'CustomInput',
props: {
label: {
type: String,
required: true,
},
value: {
type: String,
required: true,
},
},
computed: {
name() {
return this.label.toLowerCase();
},
},
methods: {
onInput(event) {
this.$emit('input', event.target.value);
},
onChange(event) {
this.$emit('change', event.target.value);
},
},
}
</script>
What I don't understand is - how will the emitted events be detected by the parent App.vue component? I can't see it happens, and I can't find it in the tutorial.
My App.Vue looks like this:
<template>
<custom-input :label="'Name'" :value="name"></custom-input>
<div>{{ name }}</div>
</template>
<script>
import customInput from "./components/CustomInput.vue";
export default {
components: { customInput },
name: "App",
data: function () {
return {
name: "",
};
},
mounted() {
this.name = "Thomas";
},
};
</script>
Thanks in advance for any help :-)
This tutorial is for Vue 2 - for Vue 3 there is another tutorial (https://www.webmound.com/use-v-model-custom-components-vue-3/)
Emitting input event works in Vue 2 only - for Vue 3 you will have to emit update:modelValue and also use modelValue as a prop instead of just value.
You can do it right in your template.
<custom-input :label="'Name'" :value="name" #change='name=$event' #input='name=$event'></custom-input>
You can also use a method or computed with setter as well.

VueJs emit('input') firing 2 values

I have a vue component which has an input so to use v-model I use this
<template>
<div class="input-field">
<input
type="text"
v-model="value"
:id="inputId"
placeholder=""
#input="updateText"
/>
<label :for="inputId">{{ label }}</label>
</div>
</template>
<script>
import { ref } from "#vue/reactivity";
export default {
name: "InputField",
props: {
value: { type: String },
inputId: { type: String },
label: { type: String },
},
setup(props, { emit }) {
const value = ref("");
const updateText = () => {
emit("input", value.value);
};
return {
value,
updateText,
};
},
};
</script>
<style lang="less" scoped>
</style>
So when I console so I used the v-model in the parent component but the value is not changing so I tried to print the #input data .. It returns 2 values.. The value of the input and the ref object
<div class="login-box">
<InputField
v-model="username"
label="Username "
inputId="username"
#input="printUser"
/>
<input type="text" />
<div>{{ username }}</div>
Any help.. Thanks!
In vue 3 v-model on a component is syntactic sugar for
<input-field
:model-value="username"
#update:model-value="username = $event"
></input-field>
so you're input field component should be
<template>
<div class="input-field">
<input
type="text"
:value="modelValue"
:id="inputId"
placeholder=""
#input="updateText"
/>
<label :for="inputId">{{ label }}</label>
</div>
</template>
<script>
import { ref } from "#vue/reactivity";
export default {
name: "InputField",
props: {
...
modelValue: { type: String },
...
},
setup(props, { emit }) {
const updateText = (e) => {
emit("update:modelValue", e.target.value);
};
return {
updateText,
};
},
};
</script>

Vue JS prop error for value on radio button with v-model and v-bind="$attrs"

I am getting some strange behaviour that I cannot wrap my head around.
I have a simple radio button component that's used as a "wrapper" for an actual radio button.
On this component, I have inheritAttrs: false and use v-bind="$attrs" on the element itself so I can use v-model and value etc.
However, upon selecting a radio button, an error is thrown that the prop value is invalid (because it's an event and not a string) and interestingly I noticed that on initial render the value prop is blank in Vue Devtools.
I'm simply trying to get these radio buttons updating the parent's data object value for location with a string value of the radio button selected.
I can't figure out where I'm going wrong here exactly. Any help greatly appreciated.
Example project of the problem:
https://codesandbox.io/embed/m40y6y10mx
FormMain.vue
<template>
<div>
<p>Location: {{ location }}</p>
<form-radio
id="location-chicago"
v-model="location"
value="Chicago"
name="location"
label="Chicago"
#change="changed"
/>
<form-radio
id="location-london"
v-model="location"
value="London"
name="location"
label="London"
#change="changed"
/>
</div>
</template>
<script>
import FormRadio from "./FormRadio.vue";
export default {
name: "FormMain",
components: {
FormRadio
},
data() {
return {
location: ""
};
},
methods: {
changed(e) {
console.log("Change handler says...");
console.log(e);
}
}
};
</script>
FormRadio.vue
<template>
<div>
<label :for="id">
{{ label }}
<input
:id="id"
type="radio"
:value="value"
v-on="listeners"
v-bind="$attrs"
>
</label>
</div>
</template>
<script>
export default {
name: "FormRadio",
inheritAttrs: false,
props: {
id: {
type: String,
required: true
},
label: {
type: String,
required: true
},
value: {
type: String,
required: true
}
},
computed: {
listeners() {
return {
...this.$listeners,
change: event => {
console.log("Change event says...");
console.log(event.target.value);
this.$emit("change", event.target.value);
}
};
}
}
};
</script>
Edit
Found this neat article which describes the model property of a component. Basically it allows you to customise how v-model works. Using this, FormMain.vue would not have to change. Simply remove the value prop from FormRadio and add the model property with your own definition
See updated codepen:
FormRadio Script
<script>
export default {
name: "FormRadio",
inheritAttrs: false,
props: {
id: {
type: String,
required: true
},
label: {
type: String,
required: true
}
},
// customize the event/prop pair accepted by v-model
model: {
prop: "radioModel",
event: "radio-select"
},
computed: {
listeners() {
return {
...this.$listeners,
change: event => {
console.log("Change event says...");
console.log(event.target.value);
// emit the custom event to update the v-model value
this.$emit("radio-select", event.target.value);
// the change event that the parent was listening for
this.$emit("change", event.target.value);
}
};
}
}
};
</script>
Before Edit:
Vue seems to ignore the value binding attribute if v-model is present. I got around this by using a custom attribute for the value like radio-value.
FormMain.vue
<form-radio
id="location-chicago"
v-model="location"
radio-value="Chicago"
name="location"
label="Chicago"
#change="changed"
/>
<form-radio
id="location-london"
v-model="location"
radio-value="London"
name="location"
label="London"
#change="changed"
/>
The input event handler will update the v-model.
FormRadio.vue
<template>
<div>
<label :for="(id) ? `field-${id}` : false">
{{ label }}
<input
:id="`field-${id}`"
type="radio"
:value="radioValue"
v-on="listeners"
v-bind="$attrs"
>
</label>
</div>
</template>
<script>
export default {
name: "FormRadio",
inheritAttrs: false,
props: {
id: {
type: String,
required: true
},
label: {
type: String,
required: true
},
radioValue: {
type: String,
required: true
}
},
computed: {
listeners() {
return {
...this.$listeners,
input: event => {
console.log("input event says...");
console.log(event.target.value);
this.$emit("input", event.target.value);
},
change: event => {
console.log("Change event says...");
console.log(event.target.value);
this.$emit("change", event.target.value);
}
};
}
}
};
</script>
See forked codepen
I removed the v-model entirely from the child component call (this was conflicting);
<form-radio
id="location-chicago"
value="Chicago"
name="location"
label="Chicago"
#change="changed"
/>
<form-radio
id="location-london"
value="London"
name="location"
label="London"
#change="changed"
/>
I then updated your changed method to include to set the location variable
methods: {
changed(e) {
console.log("Change handler says...");
console.log(e);
this.location = e;
}
}
Updated: Link to updated CodeSandbox

How to use $refs in template - vuejs

I have a input with attribute "ref" and I don't want to use v-model
<div class="form-group m-b-40">
<input type="text" class="form-control" id="name" ref="name" required>
</div>
{{showInput}}
I want to show my input value automatically. I do this
methods: {
showInput: function () {
this.$refs.name.value
},
}
but it isn't updated.
Because the value of a ref isn't an observable object unless it's bound to the component instance:
data() {
return {
name: ''
}
}
Then give your input a :value="name" and now it has an observer attached to it
I can't understand what you want to do,but the way you are doing it seems to be wrong.Anyway,you said i dont want to use v-model.
I am going to show you how to do it without v-model,you can fetch the input value from api(you have to write your own code for this) and set it to input:
<template>
<div>
<div class="form-group m-b-40">
<input type="text" :value="text" #input="updateValue">
<hr>
</div>
The input value is: {{text}}
</div>
</template>
<script>
export default {
data() {
return {
text: ''
}
},
created() {
this.fetchFromApi()
},
methods: {
updateValue(value) {
let newValue = value.target.value
this.text = newValue
},
fetchFromApi() {
//write the code to get from API the input value and then:
this.text = 'input value' //set the input value
}
}
}
</script>
See it in action here

Vue JS - injecting default value to custom radio group

So i tried to inject a default value to custom radio component that i wrote
Here's the code:
<template>
<div class="custom-radio-button">
{{value}}
<div v-for= "item in localValue">
<input type="radio" :value="item.value" name=item.name #click=onSelected(item.value) >
<span>{{item.label}}</span>
</input>
</div>
</div>
<script>
import Vue from 'vue'
const name = 'CustomRadioButton'
export default {
name,
componentName: name,
props: [ 'name', 'value', 'isDefault', 'label'],
data() {
return {
localName: this.name,
localValue: this.value
}
},
methods: {
onSelected (value) {
this.$emit('clicked', value)
}
}
}
</script>
And here's how i called it:
<CustomRadioButton :value=RadioFieldData #clicked="isRadioButtonSelection" isDefault='yellow'></CustomRadioButton>
And here's the Json Data that goes with it
RadioFieldData:[
{label:'Fire', value:'red', name:'colour' },
{label:'Sun', value:'yellow', name:'colour',isDefault:'yellow'},
{label:'Water', value:'blue', name:'colour'}
]
My question is what is the best way to pass the value "yellow" to the radio buttons group?
Your issue is that props need to be represented in their kebab-case format when used in your template. To set isDefault to "yellow", you need to use
is-default="yellow"
See https://v2.vuejs.org/v2/guide/components.html#camelCase-vs-kebab-case
Once you're able to read that property correctly, you can use
:checked="item.value == isDefault"
Here's an example.
Vue.component('custom-radio-button', {
template: `<div class="custom-radio-button">
Default: {{isDefault}}
<div v-for="item in value">
<input type="radio" :value="item.value" name="item.name" #click="onSelected(item.value)" :checked="item.value == isDefault" />
<span>{{item.label}}</span>
</div></div>`,
props: ['value', 'isDefault'],
methods: {
onSelected(value) {
this.$emit('clicked', value)
}
}
})
new Vue({
el: '#app',
methods: {
isRadioButtonSelection (val) {
console.log('isRadioButtonSelection', val)
}
},
data: {
RadioFieldData: [{"label":"Fire","value":"red","name":"colour"},{"label":"Sun","value":"yellow","name":"colour","isDefault":"yellow"},{"label":"Water","value":"blue","name":"colour"}]
}
})
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app">
<custom-radio-button :value="RadioFieldData"
#clicked="isRadioButtonSelection"
is-default="yellow">
</custom-radio-button>
</div>

Categories

Resources