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>
Related
I have two components, is there a way to store value from another component's data?
Here is Create.vue
<template>
<div id="main">
<Editor />
//some codes here
</div>
</template>
<script>
import Editor from './_Create_Editor.vue'
export default {
components: { Editor },
data: () => ({
text: ''
}),
}
</script>
And here is the _Create_Editor.vue.
<template>
//sample input for demonstration purposes
<input type="text" class="form-control" v-model="text"/>
</template>
The code above returns an error:
Property or method "text" is not defined on the instance but referenced during render
I want everytime I type the data: text from Create.vue has the value of it.
How can I possibly make this? Please help.
You can do this by using $emit.
Create.vue
<template>
<div id="main">
<Editor
#edit={onChangeText}
/>
//some codes here
</div>
</template>
<script>
import Editor from './_Create_Editor.vue'
export default {
components: { Editor },
data: () => ({
text: ''
}),
methods: {
onChangeText: function (value) {
this.text = value
}
}
}
</script>
_Create_Editor.vue
<template>
//sample input for demonstration purposes
<input
type="text"
class="form-control"
#change="onChange"
/>
</template>
<script>
export default {
methods: {
onChange: function (event) {
this.$emit('edit', event.target.value)
}
}
}
</script>
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
I have a component with following template:
<div v-for:"item in store" v-bind:key="item.type">
<a>{{item.type}}</a>
</div>
I have another component called 'StoreComponent'
On click of a element in first component I want to clear the current component and show the StoreComponent and able to pass item.type to StoreComponent.
I don't want to use router-link or router.push as I don't want to create a new url but override the current component with the new one depending on the item.type value.
StoreComponent.vue
export default{
name: 'StoreComponent',
props: ['item'],
data: function () {
return {
datum: this.item
}
},
methods: {
//custom methods
}
}
You could use dynamic components and pass the item-type as a prop.
Vue.component('foo', {
name: 'foo',
template: '#foo'
});
Vue.component('bar', {
name: 'bar',
template: '#bar',
props: ['test']
});
new Vue({
el: "#app",
data: {
theComponent: 'foo', // this is the 'name' of the current component
somethingWeWantToPass: {
test: 123 // the prop we are passing
},
},
methods: {
goFoo: function() {
this.theComponent = 'foo';
},
goBar: function() {
this.theComponent = 'bar';
},
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<button #click="goFoo">Foo</button>
<button #click="goBar">Bar</button>
<component :is="theComponent" v-bind="somethingWeWantToPass"></component>
</div>
<template id="foo">
<div>
Foo
</div>
</template>
<template id="bar">
<div>
Bar
<div>This is a prop: {{ this.test }}</div>
</div>
</template>
I wrote a basic vue js 2 basic example to test nested components.
Below is components and template structure.
Vue.component('form-com', {
template: '#form',
props: ['value'],
methods: {
onInput: function (event) {
this.$emit('input', event.target.value);
}
}
});
Vue.component('message-com', {
template: '#message',
data: function () {
return {
msg: 'Hello'
}
},
props: ['user']
});
Vue.component('welcome-com', {
template: '#welcome',
data: function () {
return {
user: 'ahmad'
}
}
});
new Vue({
el: '#container'
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.js"></script>
<!--Form Template-->
<template id="form">
<div>
<div class="form-control">
<label>Enter Your Name:</label>
<input type="text" v-bind:value="value" :input="onInput">
</div>
</div>
</template>
<!--Hello Message Template-->
<template id="message">
<div>
<h3>{{msg}} {{user}}</h3>
</div>
</template>
<template id="welcome">
<div>
<form-com :value="value"></form-com>
<br>
<message-com :user="user"></message-com>
</div>
</template>
<div id="container">
<welcome-com></welcome-com>
</div>
But when run app in browser this error is shown:
[Vue warn]: Property or method "value" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.
found in
---> <WelcomeCom>
<Root>
what is problem?
Update:
I just Rewrite this Fiddle from one of chapters of Learning Vue.js 2. I just rename some parameters and component and templates names. but when I copy main fiddle to my code all things worked.
In your form-com component you can set up a v-model which binds the input value and set up a watcher to observer the changes in the input which in turn emits an custom-event which telss the parent comonent that a change has taken place.
Vue.component('form-com', {
template: '#form',
data(){
return{
myInput:''
}
},
watch: {
myInput: function (inputVal) {
this.$emit('input', inputVal);
}
}
});
Vue.component('message-com', {
template: '#message',
data: function () {
return {
msg: 'Hello'
}
},
props: ['user']
});
Vue.component('welcome-com', {
template: '#welcome',
data: function () {
return {
user: 'ahmad'
}
},
methods:{
updateUser(value){
this.user = value;
}
}
});
new Vue({
el: '#container'
})
You can listen to the events emitted from the child **form-com ** component using v-on:input or shorthand #input directly in the parent template (welcome component) where the child component is used.
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.js"></script>
<!--Form Template-->
<template id="form">
<div>
<div class="form-control">
<label>Enter Your Name:</label>
<input type="text" v-model="myInput">
</div>
</div>
</template>
<!--Hello Message Template-->
<template id="message">
<div>
<h3>{{msg}} {{user}}</h3>
</div>
</template>
<template id="welcome">
<div>
<form-com #input="updateUser($event)" ></form-com>
<br>
<message-com :user="user"></message-com>
</div>
</template>
<div id="container">
<welcome-com></welcome-com>
</div>
Here is the jsFiddle
If you don't want to use a watcher then you can do it using computed setter . Have a look at the fiddle which is using a computed-setter
You are missing in your Component 'welcome-com' the value object:
Vue.component('welcome-com', {
template: '#welcome',
data: function () {
return {
value: '',
user: 'ahmad'
}
}
});
This is my current method:
Parent.vue:
// Template
<form-child :schema="schema"><form-child>
// JS
data () {
return {
schema: [{ // name: '', value: '', type: '' }, { //etc ... }]
}
}
FormChild.vue:
// Template
<div v-for="field in schema">
<input v-if="field.type === 'text'" #change="updateValue(field.name, field.value)">
<textarea v-if="field.type === 'textarea'" #change="updateValue(field.name, field.value)">/textarea>
</div>
// JS
props: {
schema: Arrary
}
methods: {
updateValue (fieldName, fieldValue) {
this.schema.forEach(field => {
// this makes schema update in Parent.vue
if (field.name === fieldName) field.value = fieldValue
})
}
}
Is this the optimal way? Or maybe there's a better one using emit and v-model? (If so, could you provide a sample code?)
A properly encapsulated child component would be decoupled from the parent data structure. It would take type and value as separate props, plus an opaque id to tell the parent which value the component is emitting about.
By making a settable computed based on the value parameter, the component can use v-model on its form elements. The set function emits an input event with the id and the newValue, and the parent takes it from there.
Update: I decided I didn't like the id going to the component, so I handled that in the input handler: #input="updateField(index, $event).
new Vue({
el: '#app',
data: {
schema: [{
type: 'text',
name: 'one',
value: "1"
},
{
type: 'textarea',
name: 'two',
value: "stuff in the textarea"
}
]
},
methods: {
updateField(index, newValue) {
this.schema[index].value = newValue;
}
},
components: {
formInput: {
props: ['type', 'value'],
computed: {
proxyValue: {
get() { return this.value; },
set(newValue) {
this.$emit('input', newValue);
}
}
}
}
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.min.js"></script>
<div id="app">
<div v-for="field in schema">
{{field.name}} = {{field.value}}
</div>
<form-input inline-template v-for="field, index in schema" :type="field.type" :key="index" :value="field.value" #input="updateField(index, $event)">
<div>
<input v-if="type === 'text'" v-model="proxyValue">
<textarea v-if="type === 'textarea'" v-model="proxyValue"></textarea>
</div>
</form-input>
</div>
For what you are doing here, there is no need to separate the form into a component. Just make it part of the parent and use v-model.
new Vue({
el: '#app',
data: {
schema: [{
type: 'text',
name: 'one',
value: "1"
},
{
type: 'textarea',
name: 'two',
value: "stuff in the textarea"
}
]
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.min.js"></script>
<div id="app">
<div v-for="field in schema">
{{field.name}} = {{field.value}}
</div>
<div v-for="field in schema">
<input v-if="field.type === 'text'" v-model="field.value">
<textarea v-if="field.type === 'textarea'" v-model="field.value"></textarea>
</div>
</div>
If you want the component for reusability and you don't care about insulating the parent from changes (best practice is not to have anything outside a component change its data), you can just wrap the same thing in a component:
new Vue({
el: '#app',
data: {
schema: [{
type: 'text',
name: 'one',
value: "1"
},
{
type: 'textarea',
name: 'two',
value: "stuff in the textarea"
}
]
},
components: {
formChild: {
props: ['value']
}
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.min.js"></script>
<div id="app">
<div v-for="field in schema">
{{field.name}} = {{field.value}}
</div>
<form-child inline-template v-model="schema">
<div>
<div v-for="field in value">
<input v-if="field.type === 'text'" v-model="field.value">
<textarea v-if="field.type === 'textarea'" v-model="field.value"></textarea>
</div>
</div>
</form-child>
</div>