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.
Related
So I'm trying to change a list based on a whether the elements are considered active or not. I do this through a computed data array. Basically a Search Function. However my template does not rerender and update automatically, even though I try to force it with this.forceUpdate().
This is my v-for in template:
<ion-list>
<div v-for="project in activeProjects" :key="project">
<ion-item v-if="checkemail!=project.creator">
<ion-button #click="openProjectPage(project.id)">{{ project.name }}</ion-button>
</ion-item>
</div>
</ion-list>
This is my computed array. The Log returns the correct things.
computed: {
activeProjects: function() {
return this.myprojects.filter(function(u){
console.log(u);
return u.active
})
}
}
And this is where I update the activity. The Log also returns the correct things.
search: function(){
for(var i=0; i<this.myprojects.length; i++){
if(this.myprojects[i].name.includes(this.searchinput)){
this.myprojects[i].active=true;
console.log(this.myprojects[i])
}
}
this.$forceUpdate();
}
Grateful for any help
I understand what you're attempting with the $forceUpdate, but I'm not certain that's the intended behavior here. In particular, by directly modifying the property of an Object in an Array, I believe Vue is missing the changes completely, so it doesn't know what to forceUpdate.
(See these links to read more on when Vue does / doesn't recognize mutations to Objects and Arrays)
TBH I've never attempted to use forceUpdate in this way, but I have done some Array mutation in a spreadsheet-like scenario before and it was a pain... I would avoid it if at all possible.
Rather than modifying a property in the array, I'd compute the filter on-the-fly using a method. You should get the reactivity you want because you're calculating, not mutating, the properties of the list of projects.
<script>
export default {
props: ['myprojects'],
data() {
return {
searchinput: ''
}
},
computed: {
activeProjects() {
return this.myprojects.filter(this.isInSearch)
}
},
methods: {
isInSearch(project) {
return project.name.includes(this.searchinput)
}
}
}
</script>
Vue caches nodes based on :key value. You're passing the entire object, you should be using a unique property on your project.
Try yo use name or an unique id if you have one.
<ion-list>
<div v-for="project in activeProjects" :key="project.name">
<ion-item v-if="checkemail!=project.creator">
<ion-button #click="openProjectPage(project.id)">{{ project.name }}</ion-button>
</ion-item>
</div>
</ion-list>```
<button #click="startEffect"> Start Effect</button>
<br/>
<div id="effect" class="highlight" :class="{highlight: enableHighlight}"></div>
I just need the highlight class to be applied based on the data property enableHighlight but for some reason It doesn't apply the class when the startEffect function is called.
<script>
export default {
data() {
return {
enableHighlight: false
}
},
methods: {
startEffect: function () {
this.enableHighlight = !this.enableHighlight;
}
}
}
</script>
I have debugged and confirmed that value of enableHighlight is switched when clicking the button and that the CSS classes are present. However upon clicking the button the class is not applied to the div.
You really mess with Vue when having a "normal" class attribute and one dynamic. Remove the normal one.
<div id="effect" :class="{highlight: enableHighlight}"></div>
To make it work, you need to remove the function in startEffect's definition:
startEffect() {
this.enableHighlight = !this.enableHighlight;
}
Why? because this isn't the same in the different ways to define the function. Learn more about this here.
I'm trying to simplify my problem:
Let us say I have a modal component
//example.php
<modal>
<div>A big div</div>
</modal>
Before the modal is shown I need to calculate the height for the proper animation. Inside the modal Vue it looks like this:
//Modal.vue
...
<transition
:name="transition"
#before-enter="beforeTransitionEnter"
#after-leave="afterTransitionLeave"
>
<div
v-if="visibility.modal"
ref="modal"
class="v--modal v--modal-box"
:style="modalStyle"
>
<slot/>
</div>
</transition>
I know that with this.$slots.default I get the node of the slot.
But I'm not sure how I can create a div, add the node to the div so that I can then calculate the height of it?
Edit:
Is it possible to call your own render function so I can use it like in the docs?
render: function (createElement) {
// `<div><slot></slot></div>`
return createElement('div', this.$slots.default)
}
Like
guessSlotsHeight(){
let modalDiv = document.createElement('div')
modalDiv.className = 'v--modal v--modal-box'
//something like
let slotDiv = this.render('div', this.$slots.default)
modalDiv.appendChild(slotDiv);
},
Your logic is too complex. It's easiest than you think.
Look, animation is the thing which happens to DOM. Therefore, DOM is available when animation starts. So, you can access any DOM element using Vue ref (recommended) or native javascript syntax in mounted hook. And there you can take the element height or any other property you want and store in in your data (for example).
Possible solution example #1
Vue transition before-enter event function has the target element as an argument by default. So, you can get the modal height in your beforeTransitionEnter function:
Modal.vue
data() {
return {
modalHeight: 0,
visibility: {
modal: false
}
}
},
methods: {
beforeTransitionEnter(element) {
this.modalHeight = element.offsetHeight;
}
}
Possible solution example #2
For this example there is one important detail. I see in your code example that you display the next div conditionally:
`<div v-if="visibility.modal" class="v--modal v--modal-box">`
Be careful with that, as I don't know what is the init value of visibility.modal.
I would use watch feature to know content height every time once visibility.modal gets true in this particular case.
Look at the example. It's based on the code which you provided and it's a possible solution in your case.
Modal.vue
data() {
return {
isMounted: false,
modalHeight: 0,
visibility: {
modal: false
}
}
},
mounted() {
this.isMounted = true;
},
watch: {
visibility: {
handler(value) {
if (value.modal && this.isMounted) {
this.modalHeight = this.$refs.modal.clientHeight;
}
},
deep: true
}
}
As you can see - you will always have actual modalHeight value and you can use it whenever you want within the Modal.vue component.
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>
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)'