This works:
<input v-model="project.name" :readonly="isReadOnly" :disabled="isReadOnly">
Is there a way to make below code work?
<input v-model="project.name" {{ readOnlyAttr }} >
<input v-model="project.name" {{ isReadOnly : 'readonly disabled' ? ''}}>
Script as follows:
<script>
export default {
props: {
isReadOnly: {
default: ture,
type: Boolean
}
},
data () {
return {
readOnlyAttr: 'readonly disabled'
}
}
}
</script>
v-bind is your friend here.
Because you want the attributes to be computed, I created a computed method to build the object everytime there is a change to isReadOnly value.
When you want to bind statically group of attributes, you can use the data method.
<template>
<div>
<input v-model="project.name" v-bind="readOnlyAttributes">
</div>
</template>
<script>
export default {
name: 'example',
computed: {
readOnlyAttributes() {
return {
readonly: this.isReadOnly,
disabled: this.isReadOnly ? 'readonly' : ''
}
},
isReadOnly() {
// returning always true for the example
return true;
}
}
}
Related
I am trying to build a component that creates filter buttons and then sends the type attribute in the filters object through the event bus to be handled elsewhere in my app. However, when I added the array of objects (filters) in the data section, I am getting an error of this.filter is not defined when I click on a button.
I would like to keep the filters array in this component because I am also trying to dynamically change the active class to whichever button has been clicked.
Am I missing something that has to do with props? Why am I unable to display the buttons when the data and v-for was on another component? These were the questions I have been asking myself in order of solving this issue.
<template>
<div>
<button
v-for="(filter, index) in filters"
:key="index"
:class="{ active: index === activeItem }"
#click="emitFilter(), selectItem(index)"
:filter="filter"
>
{{ filter.name }}
</button>
</div>
</template>
<script>
import EventBus from '#/components/EventBus'
export default {
props: {
filter: { type: String }
},
data() {
return {
activeItem: 0,
filterResult: '',
filters: [
{ name: 'All', type: 'all' },
{ name: 'Holidays', type: 'holiday' },
{ name: 'Seasons', type: 'season' },
{ name: 'Events', type: 'custom' }
]
}
},
methods: {
emitFilter() {
this.filterResult = this.filter
EventBus.$emit('filter-catagories', this.filterResult)
},
selectItem(index) {
this.activeItem = index
}
}
}
</script>
My button component is used in a filters component
<template>
<div>
<span>filters</span>
<FilterBtn />
</div>
</div>
</template>
<script>
import FilterBtn from '#/components/FilterBtn'
export default {
components: {
FilterBtn
}
// data() {
// return {
// filters: [
// { name: 'All', type: 'all' },
// { name: 'Holidays', type: 'holiday' },
// { name: 'Seasons', type: 'season' },
// { name: 'Events', type: 'custom' }
// ]
// }
// }
}
</script>
As you can see, the commented section is where I had my filters originally, but I had to move them to the button component in order to add the active class.
I'm assuming you were actually trying to access the filter iterator of v-for="(filter, index) in filters" from within emitFilter(). For this to work, you'd need to pass the filter itself in your #click binding:
<button v-for="(filter, index) in filters"
#click="emitFilter(filter)">
Then, your handler could use the argument directly:
export default {
methods: {
emitFilter(filter) {
this.filterResult = filter
//...
}
}
}
You are passing a prop called filter typed string to your component. When you output {{ filter.name }} you are actually referring to this property instead of the variable filter you create within the v-for loop.
Unless you passed a property called "filter" to your component, this property will be undefined. Therefore outputting filter.name will result in this error message.
Yea you dont pass an prop to your component thats why its undefined.
<FilterBtn filter="test" />
Here i pass an prop named filter with the value of test.
Sure you could pass dynamic props. Just bind it.
<FilterBtn :filter="yourData" />
I need to ask: Are you passing an object or an string?
Because you defined your prop to be a string, but you actually use it as an object
{{ filter.name }}
Maybe you should also set the type to Object.
props: {
filter: { type: Object }
},
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 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
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
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>