How to toggle button in vuejs - vue.js

I have play and pause buttons.
How to make them toggle button(show/hide) in Vue?
This is my code so far,
<button #click="slickPause" v-if="">slickPause</button>
<button #click="slickPlay" v-else>slickPlay</button>
methods: {
slickPause() {
this.$refs.carousel.pause();
},
slickPlay() {
this.$refs.carousel.play();
}
please help

You could make a state for playing and toggle it
new Vue({
el: '#app',
data: {
playing: false
},
methods: {
slickPause() {
// this.$refs.carousel.pause();
this.playing = false
},
slickPlay() {
// this.$refs.carousel.play();
this.playing = true
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="slickPause" v-if="playing">slickPause</button>
<button #click="slickPlay" v-else>slickPlay</button>
</div>

This should work.
<button #click="togglePlay">
{{ isPlaying ? slickPause : slickPlay }}
</button>
data() {
return {
isPlaying: false
}
},
methods: {
togglePlay() {
let method = this.isPlaying ? pause : play;
this.$refs.carousel[method]();
this.isPlaying = !this.isPlaying
}
}

Related

Property or method "isOpen" is not defined on the instance but referenced during render

I am relatively new to vue.js. I am trying to create a modal dialog that has an initial displayed state set to false. This dialog is used in another component like it is shown billow.
I cannot figure out why the data is isOpen is undefined
// My main component here
<template>
<button #click="openMyModal">Open</button>
<MyDialog ref="dialog"/>
</template>
<script>
...
methods: {
openMyModal(){
this.$refs.dialog.open().then((confirm) => {
console.log("confirm", confirm)
return true
}).catch();
}
}
...
</script>
<template>
<div class="overlay" v-if="isOpen">
<div class="modal">
<h1>My modal dialog here</h1>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'my-dialog'
}
data () {
return {
isOpen: false
...
}
}
methods() {
open() {
this.isOpen = true;
...
},
close() {
this.isOpen = false;
},
}
</script>
It is mostly because of syntax errors. Here is an example after debugging your code:
In the parent:
methods: {
openMyModal() {
this.$refs.dialog.open();
}
}
In the child:
export default {
name: "my-dialog",
data() {
return {
isOpen: false
};
},
methods: {
open() {
this.isOpen = true;
},
close() {
this.isOpen = false;
}
}
};
Something is missing in your example because from what you gave to us it's working as intended:
Vue.component('MyDialog', {
template: `
<div>
isOpen: {{ isOpen }}
<div v-if="isOpen">
<h1>My modal dialog here</h1>
</div>
</div>
`,
data () {
return {
isOpen: false
}
},
methods: {
open() {
this.isOpen = true;
},
close() {
this.isOpen = false;
},
}
})
Vue.config.productionTip = false
new Vue({
el: '#app',
template: `
<div>
<button #click="openMyModal">Open</button>
<button #click="closeMyModal">Close</button>
<MyDialog ref="dialog"/>
</div>
`,
methods: {
openMyModal(){
this.$refs.dialog.open()
},
closeMyModal(){
this.$refs.dialog.close()
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<body>
<div id="app" />
</body>

Vue v-for conditional styling

I use v-for to create buttons. I add the .active class if isActiveButton() returns true:
<button
v-for="(text, index) in this.buttonOptions"
class="btn"
:class="{active: isActiveButton(text)}"
:value='text'
#mousedown.prevent #click="some_method">
{{text}}
</button>
What is the best way to add the .active class to the first button if isActive() returns false for all buttonOptions? Note that the buttonOptions is a prop.
A Computed Property would be the way to go!
var app = new Vue({
el: '#app',
data: {
buttonOptions: ['button1', 'button2', 'button3', 'button4']
},
methods: {
isActiveButton: function (text) {
return (text === text.toUpperCase());
},
some_method: function() {
console.log('Button clicked')
}
},
computed: {
shouldFirstBeActive: function () {
return (this.buttonOptions.filter(el => this.isActiveButton(el))).length === 0
}
}
});
.active {
background: #f00;
}
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
<section>
<button
v-for="(text, index) in buttonOptions"
class="btn"
:class="{active: isActiveButton(text) || (shouldFirstBeActive && index === 0)}"
:value='text'
#mousedown.prevent #click="some_method">
{{text}}
</button>
</section>
</div>
I don't know what the methods isActiveButton do, so I had to improvise: It checks if the string is uppercase.
What does the trick is the computed property shouldFirstBeActive which returns true if all the items in the buttonOptions array fails the isActiveButton method:
return (this.buttonOptions.filter(el => this.isActiveButton(el))).length === 0
If you change the button2 to BUTTON2 for example, then the isActiveButton returns true for that item, which renders the shouldFirstBeActive computed property to false
var app = new Vue({
el: '#app',
data: {
buttonOptions: ['button1', 'BUTTON2', 'button3', 'button4']
},
methods: {
isActiveButton: function (text) {
return (text === text.toUpperCase());
},
some_method: function() {
console.log('Button clicked')
}
},
computed: {
shouldFirstBeActive: function () {
return (this.buttonOptions.filter(el => this.isActiveButton(el))).length === 0
}
}
});
.active {
background: #f00;
}
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
<section>
<button
v-for="(text, index) in buttonOptions"
class="btn"
:class="{active: isActiveButton(text) || (shouldFirstBeActive && index === 0)}"
:value='text'
#mousedown.prevent #click="some_method">
{{text}}
</button>
</section>
</div>
Use a computed that filters this.buttonOptions where isActiveButton is true and that takes index as a parameter

Custom Vue Material md-Input how to get isDirty or isTouched

I would like to create my own CustomMdInput, with basic validation. I would like to implement a input that work that way:
I use a <fp-input v-model="test"></fp-input>, and It is important, that when this input is required, when someone clicked on it or typesomething (turns 'touched' or 'dirty' property), and next defocus this input and go to the another input, the previous one stays Invalid with all the validation, so i have something like this:
<template>
<div class="md-layout-item">
<md-field>
<label :for="id">Imię</label>
<md-input :name="id" :id="id" :required="required" v-model="value" :ref="id" #input="emitValue()"></md-input>
<span class="md-error">Imię jest obowiązkowe</span>
</md-field>
</div>
</template>
<script>
export default {
name: 'FpInput',
props: {
value: {
required: true
},
id: {
required: true,
type: String
},
required: {
default: false,
type: Boolean
}
},
methods: {
emitValue () {
this.$emit('input', this.$refs[this.id].value)
}
}
}
</script>
<style scoped>
</style>
But i don't know how to check if this input isDirty or isTouched, and how can i set Validity of this input to check isFormValid after submit
give you an example
const MyInput = {
template: '#myInput',
props: ['value'],
data () {
return {
inputValue: this.value,
dirty: false,
touched: false,
inValid: false
}
},
methods: {
validate () {
if(this.inputValue.length<5){
this.inValid = true
this.dirty = true
}else{
this.inValid = false
this.dirty = false
this.touched = false
}
},
emitValue() {
this.validate()
this.$emit('input', this.inputValue);
}
}
}
var app = new Vue({
el: '#app',
components: {MyInput},
data () {
return {
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
<my-input value="5"></my-input>
</div>
<script type="text/x-template" id="myInput">
<div>
<input v-model="inputValue" #input="emitValue()" #touchstart="touched = true" #mousedown="touched = true"/>
<span v-show="(dirty || touched) && inValid">must be at least 5 letters</span>
<div>dirty:{{dirty}}</div>
<div>touched:{{touched}}</div>
<div>inValid:{{inValid}}</div>
</div>
</script>
give you a full example
const MyInput = {
template: '#myInput',
props: ['value'],
data () {
return {
inputValue: this.value,
dirty: false,
touched: false,
inValid: false
}
},
methods: {
validate () {
if(('' + this.inputValue).length<5){
this.inValid = true
this.dirty = true
}else{
this.inValid = false
this.dirty = false
this.touched = false
}
},
emitValue(e) {
this.validate()
this.$emit('input', this.inputValue);
}
},
created () {
this.inputValue = this.value;
this.validate();
this.dirty = false;
}
}
var app = new Vue({
el: '#app',
components: {MyInput},
data () {
return {
inputList: new Array(4).fill('').map(o=>({val:5}))
}
},
methods: {
submit () {
if(this.$refs.inputs.some(o=>o.inValid)){
alert('you have some input invalid')
}else{
alert('submit data...')
}
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
<form #submit.prevent="submit">
<my-input ref="inputs" v-for="item in inputList" v-model="item.val"></my-input>
<button type="submit">submit</button>
</form>
</div>
<script type="text/x-template" id="myInput">
<div>
<input v-model="inputValue" #input="emitValue()" #touchstart="touched = true"/>
<span v-show="(dirty || touched) && inValid">must be at least 5 letters</span>
</div>
</script>

How to defer form input binding until user clicks the submit button?

I wanted to make a two-way data binding on my form input in Vue.js 2.3. However, I cannot use the v-model directive, because I want the data to be updated only on clicking the submit button. Meanwhile, the input value may be updated from another Vue method, so it should be bound to the data property text. I made up something like this jsFiddle:
<div id="demo">
<input :value="text" ref="input">
<button #click="update">OK</button>
<p id="result">{{text}}</p>
</div>
new Vue({
el: '#demo',
data: function() {
return {
text: ''
};
},
methods: {
update: function () {
this.text = this.$refs.input.value;
}
}
});
It works, but it does not scale well when there are more inputs. Is there a simpler way to accomplish this, without using $refs?
You can use an object and bind its properties to the inputs. Then, in your update method, you can copy the properties over to another object for display purposes. Then, you can set a deep watcher to update the values for the inputs whenever that object changes. You'll need to use this.$set when copying the properties so that the change will register with Vue.
new Vue({
el: '#demo',
data: function() {
return {
inputVals: {
text: '',
number: 0
},
displayVals: {}
};
},
methods: {
update() {
this.copyObject(this.displayVals, this.inputVals);
},
copyObject(toSet, toGet) {
Object.keys(toGet).forEach((key) => {
this.$set(toSet, key, toGet[key]);
});
}
},
watch: {
displayVals: {
deep: true,
handler() {
this.copyObject(this.inputVals, this.displayVals);
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>
<div id="demo">
<input v-model="inputVals.text">
<input v-model="inputVals.number">
<button #click="update">OK</button>
<input v-for="val, key in displayVals" v-model="displayVals[key]">
</div>
If you're using ES2015, you can copy objects directly, so this isn't as verbose:
new Vue({
el: '#demo',
data() {
return {
inputVals: { text: '', number: 0 },
displayVals: {}
};
},
methods: {
update() {
this.displayVals = {...this.inputVals};
},
},
watch: {
displayVals: {
deep: true,
handler() {
this.inputVals = {...this.displayVals};
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>
<div id="demo">
<input v-model="inputVals.text">
<input v-model="inputVals.number">
<button #click="update">OK</button>
<input v-for="val, key in displayVals" v-model="displayVals[key]">
</div>
You can use two separate data properties, one for the <input>'s value, the other for the committed value after the OK button is clicked.
<div id="demo">
<input v-model="editText">
<button #click="update">OK</button>
<p id="result">{{text}}</p>
</div>
new Vue({
el: '#demo',
data: function() {
return {
editText: '',
text: ''
};
},
methods: {
update: function () {
this.text = this.editText;
}
}
});
Updated fiddle
With a slightly different approach than the other answers I think you can achieve something that is easily scalable.
This is a first pass, but using components, you could build your own input elements that submitted precisely when you wanted. Here is an example of an input element that works like a regular input element when it is outside of a t-form component, but only updates v-model on submit when inside a t-form.
Vue.component("t-input", {
props:["value"],
template:`
<input type="text" v-model="internalValue" #input="onInput">
`,
data(){
return {
internalValue: this.value,
wrapped: false
}
},
watch:{
value(newVal){
this.internalValue = newVal
}
},
methods:{
update(){
this.$emit('input', this.internalValue)
},
onInput(){
if (!this.wrapped)
this.$emit('input', this.internalValue)
}
},
mounted(){
if(this.$parent.isTriggeredForm){
this.$parent.register(this)
this.wrapped = true
}
}
})
Here is an example of t-form.
Vue.component("t-form",{
template:`
<form #submit.prevent="submit">
<slot></slot>
</form>
`,
data(){
return {
isTriggeredForm: true,
inputs:[]
}
},
methods:{
submit(){
for(let input of this.inputs)
input.update()
},
register(input){
this.inputs.push(input)
}
}
})
Having those in place, your job becomes very simple.
<t-form>
<t-input v-model="text"></t-input><br>
<t-input v-model="text2"></t-input><br>
<t-input v-model="text3"></t-input><br>
<t-input v-model="text4"></t-input><br>
<button>Submit</button>
</t-form>
This template will only update the bound expressions when the button is clicked. You can have as many t-inputs as you want.
Here is a working example. I included t-input elements both inside and outside the form so you can see that inside the form, the model is only updated on submit, and outside the form the elements work like a typical input.
console.clear()
//
Vue.component("t-input", {
props: ["value"],
template: `
<input type="text" v-model="internalValue" #input="onInput">
`,
data() {
return {
internalValue: this.value,
wrapped: false
}
},
watch: {
value(newVal) {
this.internalValue = newVal
}
},
methods: {
update() {
this.$emit('input', this.internalValue)
},
onInput() {
if (!this.wrapped)
this.$emit('input', this.internalValue)
}
},
mounted() {
if (this.$parent.isTriggeredForm) {
this.$parent.register(this)
this.wrapped = true
}
}
})
Vue.component("t-form", {
template: `
<form #submit.prevent="submit">
<slot></slot>
</form>
`,
data() {
return {
isTriggeredForm: true,
inputs: []
}
},
methods: {
submit() {
for (let input of this.inputs)
input.update()
},
register(input) {
this.inputs.push(input)
}
}
})
new Vue({
el: "#app",
data: {
text: "bob",
text2: "mary",
text3: "jane",
text4: "billy"
},
})
<script src="https://unpkg.com/vue#2.2.6/dist/vue.js"></script>
<div id="app">
<t-form>
<t-input v-model="text"></t-input><br>
<t-input v-model="text2"></t-input><br>
<t-input v-model="text3"></t-input><br>
<t-input v-model="text4"></t-input><br>
<button>Submit</button>
</t-form>
Non-wrapped:
<t-input v-model="text"></t-input>
<h4>Data</h4>
{{$data}}
<h4>Update Data</h4>
<button type="button" #click="text='jerome'">Change Text</button>
</div>

Focus input of freshly added item

So I have a list of items and list of inputs linked to each item via v-for and v-model.
I click a button and add new item to that list. I want to focus input which is linked to newly added item.
Can't figure out how to achieve this goal.
<div id="app">
<div v-for="item in sortedItems">
<input v-model="item">
</div>
<button #click="addItem">
add
</button>
</div>
new Vue({
el: '#app',
data: {
items: []
},
methods: {
addItem: function() {
this.items.push(Math.random());
}
},
computed: {
sortedItems: function() {
return this.items.sort(function(i1, i2) {
return i1 - i2;
})
}
}
})
Here's fiddle with sorted list
https://jsfiddle.net/sfL91r95/1/
Thanks
Update: inspired by pkawiak's comment, a directive-based solution. I found that calling focus in the bind section didn't work; I had to use nextTick to delay it.
Vue.directive('focus-on-create', {
// Note: using Vue 1. In Vue 2, el would be a parameter
bind: function() {
Vue.nextTick(() => {
this.el.focus();
})
}
})
new Vue({
el: '#app',
data: {
items: []
},
methods: {
addItem: function() {
this.items.push(Math.random());
}
},
computed: {
sortedItems: function() {
return this.items.sort(function(i1, i2) {
return i1 - i2;
})
}
}
})
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js"></script>
<div id="app">
<div v-for="item in sortedItems">
<input v-focus-on-create v-model="item">
</div>
<button #click="addItem">
add
</button>
</div>
Original answer:
Make your input a component so that you can give it a ready hook.
const autofocus = Vue.extend({
template: '<input v-model="item" />',
props: ['item'],
ready: function() {
this.$el.focus();
}
})
new Vue({
el: '#app',
data: {
items: []
},
methods: {
addItem: function() {
this.items.push(Math.random());
}
},
components: {
autofocus
},
computed: {
sortedItems: function() {
return this.items.sort(function(i1, i2) {
return i1 - i2;
})
}
}
})
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js"></script>
<div id="app">
<div v-for="item in sortedItems">
<autofocus :item="item"></autofocus>
</div>
<button #click="addItem">
add
</button>
</div>
I Like to extend #Roy's answer.
if you are using any UI framework then it will create DIV and within the DIV input tag will be created so this Snippet will handle that case.
Vue.directive('focus-on-create', {
bind: function(el) {
Vue.nextTick(() => {
el.querySelector('input').focus()
})
}
})