How to avoid Infinite Loop in vue.js watcher and socket.io - vue.js

I have bulma slider on client side with vue.js and socket.io. In my case I would have few instances of clients - if somebody moves his slider socket.io start sending message to server (json file) server saves this status and broadcasting this json to others. Problem is when others recives messages - they started sending this json too, because data value on their instances changes too.
Thanks for help
<template>
<div>
<input class="slider is-fullwidth is-large is-danger" step="3" min="0" max="255" v-model="value" type="range" v-bind:disabled="disabled">
<p>{{value}}</p>
</div>
</template>
<script>
export default {
name: 'app',
data: function () {
return {
value: 0,
disabled: false
}
},
methods: {
sendChange: function () {
this.$socket.send({io: "io1", mode: "brightness", user: "tomek", param: this.value})
},
},
watch: {
value: function (val) {
this.sendChange()
}
},
sockets: {
dataFromServer: function (data) {
this.value = data.param
},
},
}
</script>

Bind action to an event, like #input or #change insted of v-model value, get rid of watcher and try something like this:
<input class="..."
step="3"
min="0"
max="255"
v-model="value"
type="range"
:disabled="disabled"
#input="sendChange()">
This way you send request only when user intentionally changes his value, not when value updates by any mean.

Related

Vue onEnable/onDisable event

In my vue application I need to observe an element getting enabled/disabled (It binds to a function) and by looking that I need to trigger an onEnabled/onDisabled event which will clean up some other data nodes.
So is there a listener like #click, #enabled or something?
Eg:
<v-checkbox :value="getValue(layout.responseNode)" #change="setValue(layout.responseNode, $event)" :label="expression(layout.label)" :disabled="expression(layout.enableIf)" ></v-checkbox>
This is the code so far with me. here enableIf will be a dynamic expression from server.
Its properly working now.
Now I need to run some more expression like
<v-checkbox :value="getValue(layout.responseNode)" #change="setValue(layout.responseNode, $event)" :label="expression(layout.label)" :disabled="expression(layout.enableIf)" #onDisabled="expression(layout.disableCommand)" ></v-checkbox>
Is there an event matching onDisabled?
i would recommend watchers you can bind a variable/computed to :disabled of the checkbox and watch the value changing
exp.
<template>
<div>
<p>{{ checkboxState }}</p>
<input type="checkbox" :disabled="checkboxState" />
<button #click="checkboxChanged()">Disable Checkbox!</button>
</div>
</template>
<script>
export default {
name: "App",
data: () => {
return {
checkboxState: true,
};
},
methods: {
checkboxChanged() {
this.checkboxState = !this.checkboxState;
},
},
watch: {
checkboxState() {
// this is fired when the checkboxState changes
console.log("fired when checkboxState changes");
},
},
};
</script>
note: the function name and the variable must have the same name for watchers to work.
Like this Sandbox

VueJS - template variables not reactive with data variable

I'm making a chat system with socket.io and VueJS, so customers can talk to an admin. But when a client connects to the server, the variable in the data() updates. But the template is not updating.
Here is my code:
<template>
<div>
<div class="chats" id="chat">
<div class="chat" v-for="chat in chats">
<b>{{ chat.clientName }}</b>
<p>ID: {{ chat.clientID }}</p>
<div class="jens-button">
<img src="/icons/chat-bubble.svg">
</div>
</div>
</div>
</div>
</template>
<script>
let io = require('socket.io-client/dist/socket.io.js');
let socket = io('http://127.0.0.1:3000');
export default {
name: 'Chats',
data() {
return {
chats: [],
}
},
mounted() {
this.getClients();
this.updateClients();
},
methods: {
getClients() {
socket.emit('get clients', true);
},
updateClients() {
socket.on('update clients', (clients) => {
this.chats = clients;
console.log(this.chats);
});
}
},
}
</script>
Then I get this, the box is empty:
But I need to get this, this will only appear when I force reload the page. I don't know what I'm doing wrong...
Oke, I've found out where the problem was, in another component I used plain javascript which brokes the whole reactive stuff.

How i can validate data() value without input with vee-validate

I have a button, for load files or add some text
After load it pushed in data() prop
How i can validate this prop, if them not have input
Im found only one solution - make watch for data props. and set validate in
Maybe exist more beautiful way?
I try validator.verify() - but it dont send error in main errorBag from validateAll
This is example
<div id="app">
<testc></testc>
</div>
<script type="text/x-template" id="test">
<div>
<input type="text" v-validate="'required'" name="test_vee">
{{errors.first('test_vee')}}
<hr>
<button #click="addRow">Add</button>
<input type="text" v-model="inputValue" name="test_input"/>
<hr>
{{rows}}
<hr>
{{errors.first('rows')}}
<button #click="validateForm">Validate</button>
</div>
</script>
and script
Vue.component('testc', {
template: '#test',
data() {
return {
inputValue: '',
rows: []
}
},
watch: {
rows: {
handler: function(newVal, oldVal) {
this.$validator.errors.remove('rows');
if (this.rows.length < 2) {
this.$validator.errors.add({
id: 'rows',
field: 'rows',
msg: 'Need 2 rows!',
});
}
}
}
},
methods: {
addRow: function() {
this.rows.push(this.inputValue);
this.inputValue = '';
},
validateForm: function(){
this.$validator.validateAll();
}
}
});
Vue.use(VeeValidate);
new Vue({
el: '#app'
})
https://codepen.io/gelid/pen/YBajER
First input in example: default validate - its ok
Second input: for add items - dont need validate or has self validate (not empty for example)
In data of component i have prop rows - it is need validate before ajax request to backend for save data

Reusable component/mixin for a form that is disabled during submit in Vue

I have many components that are basically a form that, on submit, makes a request to the server and disables its input elements until a response is received. I'd like to not have to care about this disabling every time and factor it out into something reusable. Is there a good way to do that?
For concreteness, here is a minimal example:
<form v-on:submit.prevent="send">
<fieldset :disabled="isDisabled">
<div>
<label>Name</label>
<input v-model="u.name">
</div>
<div>
<label>Email</label>
<input type="email" v-model="u.email">
</div>
</fieldset>
</form>
As you can see, handling this isDisabled state clutters up the component:
data () {
return {
u: {
name: '',
email: '',
},
isDisabled: false
}
},
methods: {
send: function () {
this.isDisabled = true
api.post('/users/create', {
name: this.u.name,
email: this.u.email
}).then(response => {
this.isDisabled = false
<do something>
}).catch(error => {
alert(error)
this.isDisabled = false
})
}
}
One idea was to make a generic Form component parametrized by the required fields and REST endpoint, passed in by props. However, the forms and their send functions vary considerably, and might also include conditional inputs so this seems difficult to me.
It sounds like you want a mixin, but all it would do is declare the isDisabled data item (which I would recommend you call saving so that it better indicates program state).
Since you set it to false in both the resolve and reject phases of the Promise, you can move it to the finally phase, which would help the perceived clutter a bit.
You could possibly have a directive in your mixin that would find all the form elements in the form and disable them when saving, and re-enable them afterward, so the markup in the template would just be
<form v-disable-elements="saving">
I'm quite happy with the way I ended up doing it. My form component is basically just
<template>
<form v-on:submit.prevent="send">
<fieldset :disabled="submitting">
<slot></slot>
<div class="submit">
<input type="submit" :value="submitText">
</div>
</fieldset>
</form>
</template>
plus some code for displaying error messages. The component is parametrized by props, notably the endpoint to send data to, the payload, and the text in the button.
With slots, the actual form lives in the parent component and the computation of the payload is also done there, it is easy to have forms that contain very different inputs, unlike my first idea of passing the fields, their types and their name in the payload themselves as props. For concreteness, I usually make a computed property like this:
formdata: function () {
return {
endpoint: '/events/create',
submitText: 'Create event',
payload: {
description: this.ev.description,
date: this.date
}
}
}
and pass it with
<sync-form v-bind="formdata">
The send method in the form component takes care of disabling/un-disabling, and emits an ok or err event depending on the response. Optionally, the parent component can pass in a prop that tells the form if the input is valid and can be submitted.
I created a FormContainer.vue which also passes the data as described here https://v2.vuejs.org/v2/guide/components-slots.html#Scoped-Slots
<template>
<form
#submit.prevent="submit">
<slot v-bind:formInfo="formInfo"></slot>
</form>
</template>
<script>
export default {
props: {
onSubmit: Function
},
data () {
return {
formInfo: {
submitting: false
}
}
},
methods: {
async submit () {
try {
this.formInfo.submitting = true
const response = await this.onSubmit()
this.$emit('onSuccess', response)
} catch (err) {
console.log('err', err)
this.$emit('onError', err)
}
this.formInfo.submitting = false
}
}
</script>
In a child I can do
<template>
<form-container
:onSubmit="onSubmit"
#onSuccess="onSuccess"
#onError="onError"
v-slot="{formInfo}">
<input type="text" v-model="email" />
<button>{{ formInfo.submitting ? 'Submitting...' : 'Submit' }}</button>
</form-container>
</template>
<script>
import FormContainer from './FormContainer'
export default {
components: {
FormContainer
},
data () {
return {
email: ''
}
},
methods: {
onSubmit () {
// Your axios or grahql call or whatever
},
onSuccess (res) {
// Whatever you want to do after the submit succeeded
console.log('Submitted!', res)
},
onError (err) {
// Whatever you want to do after the submit failed
console.log('Submit failed!', err)
}
}
</script>

How can I update value in input type text on vue.js 2?

My view blade laravel like this :
<form slot="search" class="navbar-search" action="{{url('search')}}">
<search-header-view></search-header-view>
</form>
The view blade laravel call vue component (search-header-view component)
My vue component(search-header-view component) like this :
<template>
<div class="form-group">
<div class="input-group">
<input type="text" class="form-control" placeholder="Search" name="q" autofocus v-model="keyword" :value="keyword">
<span class="input-group-btn">
<button class="btn btn-default" type="submit" ref="submitButton"><span class="fa fa-search"></span></button>
</span>
<ul v-if="!selected && keyword">
<li v-for="state in filteredStates" #click="select(state.name)">{{ state.name }}</li>
</ul>
</div>
</div>
</template>
<script>
export default {
name: 'SearchHeaderView',
components: { DropdownCategory },
data() {
return {
baseUrl: window.Laravel.baseUrl,
keyword: null,
selected: null,
filteredStates: []
}
},
watch: {
keyword(value) {
this.$store.dispatch('getProducts', { q: value })
.then(res => {
this.filteredStates = res.data;
})
}
},
methods: {
select: function(state) {
this.keyword = state
this.selected = state
this.$refs.submitButton.click();
},
input: function() {
this.selected = null
}
}
}
</script>
If I input keyword "product" in input text, it will show autocomplete : "product chelsea", "product liverpool", "product arsenal"
If I click "product chelsea", the url like this : http://myshop.dev/search?q=product
Should the url like this : http://myshop.dev/search?q=product+chelsea
I had add :value="keyword" in input type text to udpate value of input type text, but it does not work
How can I solve this problem?
Update
I had find the solution like this :
methods: {
select: function(state) {
this.keyword = state
this.selected = state
const self = this
setTimeout(function () {
self.$refs.submitButton.click()
}, 1500)
},
...
}
It works. But is this solution the best solution? or there is another better solution?
Instead of timeout you can use vue's nextTick function.
I didn't checked your code by executing but seems its problem regarding timings as when submit is pressed your value isn't updated.
so setTimeout is helping js to buy some time to update value, but its 1500 so its 1.5 second and its little longer and yes we can not identify how much time it will take each time so we tempted to put max possible time, still its not perfect solution
you can do something like this. replace your setTimeout with this one
const self = this
Vue.nextTick(function () {
// DOM updated
self.$refs.submitButton.click()
})
nextTick will let DOM updated and set values then it will execute your code.
It should work, let me know if it works or not.