vue: transforms a prop in its definition - vuejs2

I've a component which is a button with some stuff like a icon. I use it like this:
<ti-btn icon="..." #click.native="..."></ti-btn>
also, I can pass a prop which isn't required, this prop is the size, which is a Number.
<ti-btn icon="..." size="32" #click.native="..."></ti-btn>
Now, in my component's definition, at first, I wrote:
<template>
<i :class="icon" :style="{fontSize}"></i>
</template>
<script>
export default {
props: ['icon', 'size'],
computed: {
fontSize() {
return this.size ? `${this.size}px` : DEFAULT_SIZE;
}
}
}
</script>
If the size is not passed down, I set the default value. That's works, but according to good practices and vue style guide, Prop definitions should be as detailed as possible. So, I started to use this way:
props: {
icon: {
type: String,
required: true,
validate(value){
//some kind of validation here
return value.includes('ti')
}
},
size: {
type: Number,
required: false,
default: DEFAULT_SIZE
}
}
size prop receive a Number, but, it must return a string, in fact DEFAULT_SIZE is set to "24px" which is a String, also, the value receive is transformed to value+"px". So, My question is, how can I transform the size prop in its own definition object, without use the computed property?

As Terry mentioned in the comments, it's not possible to provide a way to transform the value of a prop being passed to a component from within the prop's definition itself.
You could let size just be a Number, and then add the 'px' when you bind it to the style:
<i :class="icon" :style="{ fontSize: `${size}px` }"></i>
This would mean you'd need to make DEFAULT_SIZE equal to 24.
If you're unable to change the value of DEFAULT_SIZE. Then your example of the fontSize computed property is the correct way to handle the issue.

Related

Set dynamic class depending of parameter received

I'm new using Vuejs and I have a component that I want to set height value only if I send a parameter in props
So, my class is something like this:
<input
tag="section"
class="h-full"
>
As you can see I use h-full class (tailwind framework) but I want to remove it if prop comes true, so I create a new prop:
props: {
adjustHeightToContent: {
type: Boolean,
default: false,
}
},
I want to know how can I set that CSS class to dynamic depending of parameter value
Component usage:
<BaseInput v-if="isModalShown"> </BaseInput>
Use can use this:
:class="adjustHeightToContent ? 'h-auto' : 'h-full'"

Range Slider Nuxt JS

So I have a task, I need to build a calculator based on a range slider in Nuxt that changes the color when the thumb moves and it calculates something at the same time. I've managed to get it to work on a certain level. But when I flip pages, it crashes saying that can't read addEventListener of undefined.
here is the code:
<div class="range">
<input v-html="amount" v-model="value" type="range" class="slider" id="amount" min="0" max="100">
</div>
methods: {
colorSlider(){
const slider = document.querySelector('#amount')
let x = slider.value
let color = 'linear-gradient(90deg, rgb(249,84,78)' + x+ '%, rgb(224,224,224)' + x +'%)'
slider.style.background=color
}
},
mounted(){
document.querySelector('#amount').addEventListener('mousemove',this.colorSlider)
}
Any ideeas ?
You don’t need an event listener— Vue is reactive, simply tying the slider to a data property via v-model is fine.
Eg:
...
<input type="range" v-model="sliderValue">
...
export default {
data() {
return {
sliderValue: 0
}
}
}
Now when you adjust the slider, the value of sliderValue will update. No event listener required.
To use the value of sliderValue for something useful in your template— you should use a computed property, not a method.
export default {
data() {
return {
sliderValue: 0
}
},
computed: {
sliderBgColor() {
return `linear-gradient(90deg, rgb(249,84,78) ${this.sliderValue}%, rgb(224,224,224) ${this.sliderValue}%)`
}
}
}
Now when you adjust the slider, the data property it’s tied to (sliderValue) via v-model will change. The computed property sliderBgColor notices the change and automatically updated it’s return value. Use the return value of sliderBgColor in your input and you’re done.
<input type="range" v-model="sliderValue" :style="`background: ${sliderBgColor}`"
There’s plenty of information available on computed properties, I’d recommend taking a look at the Vue docs.

Reseting a config option on vue-flatpickr programatically

I have a vue date component that is composed of a vue-flatpickr-component. When I pass config options in as props, of course, they work as expected, however, if want to change one of the config options which should be possible, it won't propagate down. I'm not a Vue guru, any advice would be helpful.
I'm using a page component in a Laravel app, it shouldn't be relevant, however, just in case someone answers with vuex or vue-router, those won't work here.
Here are the form elements in play from page.vue:
<material-select
name="specialist"
label="Specialist"
default-text="CHOOSE HOMEVISIT SPECIALIST"
:options="staffMembers"
v-model="form.specialist"
:validation-error="form.errors.first('specialist')"
class="mb-4"
></material-select>
<div class="w-1/2">
<material-date
label="Appointment date"
name="appointment_date"
v-model="form.appointment_date"
:validation-error="form.errors.first('appointment_date')"
class="mb-4"
:external-options="{
enable: this.appointmentDates,
}"
></material-date>
<pre>{{ this.appointmentDates }}</pre>
</div>
Here is the computed property driving the config change:
computed: {
appointmentDates(){
if(this.form.specialist !== null){
return this.availableDates[this.form.specialist - 1]
}
return []
},
When a different home visit specialist is chosen, it will update with Vue's reactivity.
I have a computed property changing the config options. Here are the props data and the relevant computed property from the MaterialDate.vue file:
import flatPickr from 'vue-flatpickr-component';
import 'flatpickr/dist/flatpickr.css';
export default {
components: {
flatPickr
},
props: {
value: String,
label: String,
validationError: String,
name: {required:true},
optional: {
default: false
},
externalOptions: {}
},
data() {
return {
defaults: {disableMobile: true,},
options: this.externalOptions
}
},
computed: {
config(){
return Object.assign({}, this.defaults, this.options)
},
This will of course never update the enabled dates option because the prop is immutable, I need to get access to the set(option, value) section of the wrapped by vue-flatpickr-component. However, my Vue kungfu is not really strong enough to source dive it to see how I might access it and programatically call set('enabled', [new dates]).
Sometimes, you shouldn't code when you are tired :) But Hopefully this will help someone at some point. I was over thinking this. Data is passed down through props, and if controlling data changes it has to be reflected in the propagated data. Much like v-model with it's value prop.
So instead of binding the config object on this.options which doesn't stay hooked to it's prop value that it was initialized from, the computed function should be calculated from the prop which will change based on the new passed in options prop.
so simply change the computed function to:
computed: {
config(){
return Object.assign({}, this.defaults, this. externalOptions)
},
and remove the data element.
... Elementary
Sorry for the cheese it's late and I feel relieved.

Stateless (controlled) input

Stateless input means it changes only when :value binding of parent does change. Which gives full control over what it displays, which is useful for masks and filters.
What I have
This solution is the one closest to what I need: https://codesandbox.io/s/mm9n7r08mx
The problem with existing solution
Cursor jumps to the end when I try to type something in the middle of the existing text.
What I need
Any working solution for stateless input or a way to fix the existing one.
Materials I found
React issue
React fiddle for credit card input http://jsbin .com/dunutajuqo
It's jumping because you're manually assigning a value to the field. You don't need to re-set the value during input event. The value is already in sync at that point. Posting the full code blurb here so others have context:
<template>
<input
class="com-input"
:value="value"
#input="setValue"
:placeholder="placeholder"
>
</template>
<script>
export default {
name: "ComInput",
props: {
value: {
type: String
},
placeholder: {
type: String
}
},
methods: {
setValue($event) {
const value = $event.target.value;
$event.target.value = this.value; // <-- DELETE THIS
this.$emit("input", value);
}
}
};
</script>

Binding method result to v-model with Vue.js

How do you bind a method result to a v-model with Vue.js?
example :
<someTag v-model="method_name(data_attribute)"></someTag>
I can't make it work for some reason.
Thank you.
Years later, with more experience, I found out that is it easier to bind :value instead of using v-model. Then you can handle the update by catching #change.
Edit (per request):
<input :value="myValue" #change="updateMyValue">
...
methods: {
updateMyValue (event) {
myValue = event.target.value.trim() // Formatting example
}
}
And in a child component:
// ChildComponent.vue
<template>
<button
v-for="i in [1,2,3]">
#click="$emit('change', i) />
</template>
// ParentComponent.vue
<template>
<child-component #change="updateMyValue" />
</template>
<script>
import ChildComponent from './child-component'
export default {
components: {
ChildComponent
},
data () {
return {
myvalue: 0
}
},
methods: {
updateMyValue (newValue) {
this.myvalue = newValue
}
}
}
</script>
v-model expressions must have a get and set function. For most variables this is pretty straight forward but you can also use a computed property to define them yourself like so:
data:function(){
return { value: 5 }
},
computed: {
doubleValue: {
get(){
//this function will determine what is displayed in the input
return this.value*2;
},
set(newVal){
//this function will run whenever the input changes
this.value = newVal/2;
}
}
}
Then you can use <input v-model="doubleValue"></input>
if you just want the tag to display a method result, use <tag>{{method_name(data_attribute)}}</tag>
Agree with the :value and #change combination greenymaster.
Even when we split the computed property in get/set, which is help, it seems very complicated to make it work if you require a parameter when you call for get().
My example is a medium sized dynamic object list, that populates a complex list of inputs, so:
I can't put a watch easily on a child element, unless I watch the entire parent list with deep, but it would require more complex function to determine which of the innter props and/or lists changed and do what fromthere
I can't use directly a method with v-model, since, it works for providing a 'get(param)' method (so to speak), but it does not have a 'set()' one
And the splitting of a computed property, have the same problem but inverse, having a 'set()' but not a 'get(param)'