Stop typing text when element is focused - javascript

I have a component with typing text, this effect add/remove word and write new word:
<template>
<span
id="textCode"
contenteditable="true"
spellcheck="false"
style="width: 800px"
v-text="displayText.join('')"
/>
</template>
<script>
export default {
props: {
speed: {
type: Number,
default: 100
},
deleteSpeed: {
type: Number,
default: 30
},
nextWordInterval: {
type: Number,
default: 1200
},
words: {
type: Array,
default: null
}
},
data () {
return {
displayText: [],
currentWord: '',
wordIdx: 0,
timeoutSpeed: null,
isWaitingNextWord: false
}
},
mounted () {
this.start()
},
methods: {
start () {
if (this.words && this.words.length > 0 && !document.querySelector('#textCode').contains(document.activeElement)) {
this.currentWord = this.words[this.wordIdx].split('')
this.timeoutSpeed = this.speed
this.animate = setTimeout(this.type, this.timeoutSpeed)
}
},
type () {
if (this.currentWord.length > 0) {
this.displayText.push(this.currentWord.shift())
// if done typing, wait for a while
} else if (!this.isWaitingNextWord && this.currentWord.length === 0 && this.displayText.length === this.words[this.wordIdx].length) {
this.timeoutSpeed = this.nextWordInterval
this.isWaitingNextWord = true
// if done typing, then delete
} else if (this.currentWord.length === 0 && this.displayText.length > 0) {
this.timeoutSpeed = this.deleteSpeed
this.displayText.pop()
// if done typing & deleting
} else if (this.currentWord.length === 0 && this.displayText.length === 0) {
// change words
if (this.wordIdx < (this.words.length - 1)) {
this.wordIdx++
} else {
// reset
this.wordIdx = 0
}
this.timeoutSpeed = this.speed
this.isWaitingNextWord = false
this.currentWord = this.words[this.wordIdx].split('')
this.displayText.push(this.currentWord.shift())
}
setTimeout(this.type, this.timeoutSpeed)
}
}
}
</script>
I need stop typing text, when element span is focused. How I can do it? I try do in method start:
!document.querySelector('#textCode').contains(document.activeElement)
But is not work.. I checked. Maybe is there another method to solve this problem? Please help me resolve this issue. Thanks.

Related

Having value loop from 100 to 0 - Vuejs

I'm trying to have my power value go from 100 to 0 and back from 0 to 100. This will be for a power meter where the user will hit a button to stop it at a random value.
Just need help getting the loop working properly
export default {
data() {
return {
power: 100,
};
},
}
watch:{
power: {
handler(value) {
if (value == 100 || value > 0) {
setTimeout(() => {
this.power--;
}, 100);
} if (value == 0) {
setTimeout(() => {
this.power++;
}, 100);
}
},
immediate: true
},
}
Maybe you could find other solutions also, but this is what comes in my mind:
<template>
<section v-if="!showPart">
<!-- This part is shown when program is cycling looping numbers -->
<div v-if="power1">
{{power}}
</div>
<div v-else>
{{powerReverse}}
</div>
</section>
<div v-else>
<!-- This part is shown when user clicks to select a number -->
{{showValue}}
</div>
<button #click="stopFunc">{{textBtn}}</button>
</template>
<script>
export default {
name: "powerCompo",
data() {
return {
speed: 100, // speed of loop
power: 99, // start point of loop
power1: true, // for change view in increase and decrease states
powerReverse: 0, // for increasing loop after reach to "0"
showValue: "nothing selected", // showing the selected value
decrease: null, // for clearing "timeout" in decrease mode
increase: null, // for clearing "timeout" in increase mode
showPart: false, // for changing view between "loop" or "stop" modes
textBtn: "click for stop" // defines the text of btn
};
},
watch:{
power: {
handler(value) {
this.decrease = setTimeout(() => {
this.power--;
}, this.speed);
if (this.power === 0) {
clearTimeout(this.decrease);
console.log("decrease");
this.power1 = false;
this.powerReverse = 0;
this.powerReverse++;
}
},
immediate: true
},
powerReverse(newValue) {
this.increase = setTimeout(() => {
this.powerReverse++;
}, this.speed);
if (this.powerReverse === 100) {
clearTimeout(this.increase);
console.log("increase");
this.power1 = true;
this.power = 100;
this.power--;
}
}
},
methods: {
stopFunc: function ($event) {
/* This function is called each time the user clicks on button. Then if the text of button is "click for cycling again", it calls "resetFunc()" method and if not, it stops looping and shows the selected value. */
if ($event.target.innerText === "click for cycling again") {
this.resetFunc();
} else {
if (this.power1) {
this.showValue = this.power;
if (this.showValue === 100) {
this.showValue = 99;
this.power = 99;
}
} else {
this.showValue = this.powerReverse;
}
this.showPart = true;
clearTimeout(this.increase);
clearTimeout(this.decrease);
this.textBtn = "click for cycling again"
}
},
resetFunc: function () {
clearTimeout(this.increase);
clearTimeout(this.decrease);
this.textBtn = "click for stop"
this.showPart = false;
this.power= 100;
this.power1= true;
}
}
}
</script>
With the above component you can loop between [1, 99] inclusive.

VueJs not rerendering after new element is added to an array

I have set a watcher to check if the dropdown value is equals to "select", if the value is equal then a new array "option" is created with one element in it. I have a button that will add a value to the array that the watcher created when the dropdown value is equal to select. The button function is working but the input field that should loop the array doesn't re-render after the button click.
I am sorry I am terrible at explaining things.
here is my code
<template lang="pug">
.formHolder
.quest
input(type="text" placeholder="Question" v-model="tempForm.tempQuestions[0].question")
input(type="text" placeholder="Description" v-model="tempForm.tempQuestions[0].description")
select(name="category" v-model="tempForm.tempQuestions[0].type")
option(value="text") text
option(value="select") Dropdown
option(value="number") Number
this is the input field that will loop the array
.option(v-for="(option, index) in tempForm.tempQuestions[0].options")
input(type="text" placeholder="Option" v-model="tempForm.tempQuestions[0].options[index]")
this button adds a value to the array
button(v-if="tempForm.tempQuestions[0].type === 'select'" #click="addItems") Add Options
</template>
<script>
// # is an alias to /src
import HelloWorld from "#/components/HelloWorld.vue";
export default {
name: "HomeView",
components: {
HelloWorld,
},
data() {
return {
questions: 0,
tempForm: {
tempQuestions: [
{
question: null,
description: null,
type: null,
},
],
},
form: {
questions: [],
},
};
},
methods: {
addQuestion() {
this.tempForm.tempQuestions.push({
question: null,
description: null,
type: null,
});
this.questions++;
this.readerNos++;
},
addOptionArray() {
this.tempForm.tempQuestions[0].options.push("");
},
//////// this is the function
addItems() {
console.log(this.tempForm.tempQuestions[0].options);
let length = this.tempForm.tempQuestions[0].options.length;
console.log(length);
this.$set(this.tempForm.tempQuestions[0].options, length, "");
},
},
watch: {
tempForm: {
handler: function () {
this.tempForm.tempQuestions.forEach((question) => {
if (question.type === "select") {
question.options = [];
question.options.push("");
} else if (question.type === "text") {
if (question.options) {
delete question.options;
}
} else if (question.type === "number") {
if (question.options) {
delete question.options;
}
}
});
},
deep: true,
},
},
};
</script>
You should use Vue.$set() when adding an empty options array - otherwise it won't be reactive and there will be no rerendering of the options in the template:
watch:
{
tempForm:
{
handler()
{
this.tempForm.tempQuestions.forEach((question) =>
{
if (question.type === "select")
{
this.$set(question, 'options', []);
question.options.push("");
}
else if (question.type === "text")
{
if (question.options)
{
question.options.splice();
}
}
else if (question.type === "number")
{
if (question.options)
{
question.options.splice();
}
}
});
},
deep: true,
},
},

VueJs v-select edit selected via methods

What im trying to achieve is:
when i select the option "Alle openstaande" (result_id = 0), i want to select all these result_ids: [-1,-2,-3,-5,-7,-8,-11,-12] and deselect result_id 0.
var mergedResults returns a array of the result_ids that need to be selected
i cant figure out how i can add to the currently selected options via the methods
my blade file contains:
<dd-results
naam="resultcodes-select"
v-model="selected.resultcodes"
:select-all="true"
:multiple="true"
:openstaande="true"
:include-ids="true"
:project-id="selected.project"
title="#lang('rapportage.resultcode')"
:include-negative="true"
return-key="result_id"
>
</dd-results>
my vue file:
<template>
<span>
<label v-if="title">{{title}}</label>
<v-select
:options="results"
label="naam"
:placeholder="placeholderText"
:close-on-select="closeOnSelectValue"
:disabled="isDisabled"
:multiple="multiple"
:value="value"
#input="handleInput($event)"
:loading="isLoading"
:reduce="reduceKey"
>
<div slot="no-options">{{geenOptiesTekst}}</div>
</v-select>
</span>
</template>
<script>
import vSelect from "vue-select";
import Language from "../../classes/Language";
export default {
name: 'dd-results',
components: {
'v-select': vSelect
},
props: {
placeholder: {
type: String,
default: null
},
closeOnSelect: {
type: Boolean,
default: true
},
isDisabled: {
type: Boolean,
default: false,
},
// Meerdere waarden selecteren
multiple: {
type: Boolean,
default: false
},
openstaande: {
type: Boolean,
default: false
},
value: {
default: null
},
// Wel/geen 'Alle projecten' placeholder
selectAll: {
type: Boolean,
default: false
},
projectId: {
default: null,
required: true
},
title: {
type: String,
default: null
},
includeIds: {
type: Boolean,
default: false
},
// Wel / niet negatieve resultcodes tonen
includeNegative: {
type: Boolean,
default: false
},
// Als gevuld: alleen deze waarde teruggeven, anders volledig object
returnKey: {
type: String,
default: null,
},
excludeTypes: {
type: Array,
}
},
data() {
return {
language: new Language,
results: [],
isLoading: false,
}
},
created() {
var self = this;
this.language.set(lan);
},
methods: {
reduceKey(option) {
return this.returnKey != null ? option[this.returnKey] : option;
},
handleInput($event) {
this.$emit('input', $event);
if($event.includes(0)) {
this.addOpenstaande($event);
}
},
addOpenstaande(value) {
var openstaandeResult = [-1,-2,-3,-5,-7,-8,-11,-12];
var mergedResults = [... new Set (value.concat(openstaandeResult))];
const removeIndex = mergedResults.indexOf(0);
if (removeIndex > -1) {
mergedResults.splice(removeIndex, 1);
}
this.reset(mergedResults);
console.log(mergedResults);
},
reset(selected=false) {
// 1. verwijder de geselecteerde itemboxes
console.log(this.$el,$(this.$el).find('.vs__selected'));
$(this.$el).find('.vs__selected').remove();
setTimeout(() => {
// 2. herstel de placeholder
$(this.$el).find('.vs__selected-options input').attr('placeholder', this.placeholderText);
}, 100);
if(selected !== false) {
}
}
},
computed: {
// Tekst in dropdown bij geen opties
geenOptiesTekst() {
return this.language.trans('app', 'no_results');
},
// Bepaal placeholder tekst. Is afhankelijk van selectAll optie. Eigen placeholder overruled altijd.
placeholderText() {
if(this.placeholder != null) {
return this.placeholder;
}
else if(this.selectAll) {
return this.language.trans('rapportage', 'resultcode_alle');
} else {
return this.language.trans('rapportage', 'resultcode_selecteren');
}
},
// Bij mogelijkheid tot selecteren van meerdere projecten, standaard niet sluiten na selectie
closeOnSelectValue() {
if(this.multiple) {
return false;
} else {
return this.closeOnSelect;
}
}
},
watch: {
'projectId'(newVal) {
if(this.value != null) {
if(this.value.length != null) {
this.value.length = 0;
}
}
var self = this;
if(newVal != null) {
self.isLoading = true;
// Results opvragen en dropdown vullen
axios.get('/projecten/'+newVal+'/results').then(result => {
self.results = Object.values(result.data);
console.log(self.results);
for (var i = 0; i < self.results.length; i++) {
// Als negatieven gefilterd moeten en index negatief is: verwijderen
if(self.results[i].result_id < 0 && !this.includeNegative) {
self.results.splice(i, 1);
i--; // Index terugzetten door verwijderen item
continue;
}
// Als type in excludeTypes: verwijderen
if (this.excludeTypes) {
if (this.excludeTypes.indexOf(self.results[i].type) > -1) {
self.results.splice(i, 1);
i--;
continue;
}
}
}
for (var i = 0; i < self.results.length; i++) {
if (self.includeIds) {
// verander "naam" in "(result_id) naam"
self.results[i].naam = "(" + self.results[i].result_id + ") " + self.results[i].naam;
}
}
if(this.multiple === true && this.openstaande) {
self.results.unshift({
result_id: 0,
naam: this.language.trans('app', 'alle_openstaande')
})
}
}).catch(error => {
console.log(error);
}).then(data => {
self.isLoading = false;
})
} else {
this.results = [];
}
}
}
}
</script>
<style>
select:required:invalid {
color: gray;
}
.vs--disabled .vs__dropdown-toggle, .vs--disabled .vs__clear, .vs--disabled .vs__search, .vs--disabled .vs__selected, .vs--disabled .vs__open-indicator {
background-color: #D3D3D3;
color: #808080;
}
option {
color: black;
}
.vs__dropdown-toggle {
background: #fff;
}
</style>
i was able to make this work via the $emit in handleInput($event). (it seems so obvious now but i had no idea what this.$emit('input', $event); did at the time)
i changed these three methods:
handleInput($event) {
this.$emit('input', $event);
if($event.includes(0)) {
this.addOpenstaande($event);
}
},
addOpenstaande(value) {
var openstaandeResult = [-1,-2,-3,-5,-7,-8,-11,-12];
var mergedResults = [... new Set (value.concat(openstaandeResult))];
const removeIndex = mergedResults.indexOf(0);
if (removeIndex > -1) {
mergedResults.splice(removeIndex, 1);
}
this.$emit('input', mergedResults);
},
reset(selected=false) {
// 1. verwijder de geselecteerde itemboxes
$(this.$el).find('.vs__selected').remove();
setTimeout(() => {
// 2. herstel de placeholder
$(this.$el).find('.vs__selected-options input').attr('placeholder', this.placeholderText);
}, 100);
}

mutate vuex store state outside mutation handlers

I created a custom timer component, but I got errors about vuex store.
This component starts a timer when it's created and put into the store, then each minute it increments the time. If the person leaves the page like the timer is in the store, the time is saved and when the person comes back to the page, the timer resume by itself.
This is my component
<template>
<div>
<v-icon v-if="timer.visibility" :color="timeColor" #click="changeTimer">
{{ $icons.clock }}
</v-icon>
</div>
</template>
<script>
export default {
props: {
value: {
type: Number,
required: true
},
id: {
type: Number,
default: null
},
type: {
type: String,
default: null
}
},
data() {
return {
timer: {
actif: false,
visibility: false,
timeOut: null,
dateDebut: null,
id: 0
}
}
},
computed: {
listeTimer() {
return this.$store.state.listeTimerPointage
},
timeColor() {
return this.timer.actif ? 'green' : 'red'
},
count: {
get() {
return this.value
},
set(val) {
this.$emit('input', val)
}
}
},
beforeDestroy() {
this.stopTimer()
},
created() {
let precedentTimer = null
if (this.id > 0) {
if (this.listeTimer.length > 0) {
precedentTimer = this.listeTimer.filter((f) => f.id === this.id)
}
} else {
this.timer.id = 0
}
this.startTimer(precedentTimer)
},
methods: {
// Start le timer
startTimer(precedentTimer) {
if (precedentTimer !== null) {
this.timer = precedentTimer
this.count = precedentTimer.count
} else {
this.count = 1
this.timer.dateDebut = new Date()
if (this.timer.timeOut === null) {
this.timer.actif = true
this.timer.visibility = true
this.timer.timeOut = setTimeout(() => {
this.timerBoucle()
}, 60000)
}
}
// this.listeTimer.push(this.timer)
this.$store.commit('addListeTimerPointage', this.timer)
},
// Arrete le timer
stopTimer() {
this.timer.actif = false
clearTimeout(this.timer.timeOut)
},
// Met en place la boucle toute les 1 minutes
timerBoucle() {
const now = new Date()
const diff = now - this.timer.dateDebut
this.count += Math.round(diff / 60000)
this.timer.dateDebut = new Date()
this.timer.timeOut = setTimeout(() => {
this.timerBoucle()
}, 60000)
},
// Modifie l'état du timer
changeTimer() {
this.timer.actif = !this.timer.actif
if (!this.timer.actif) {
clearTimeout(this.timer.timeOut)
} else {
this.timer.dateDebut = new Date()
this.timer.timeOut = setTimeout(() => {
this.timerBoucle()
}, 60000)
}
}
}
}
</script>
I indeed mutate a state, but I don't think I change the state directly
And this is the store:
addListeTimerPointage(state, data) {
state.listeTimerPointage.push(data)
},
deleteTimer(state, data) {
const newArray = state.listeTimerPointage.filter(
(item) => item.id !== data
)
state.listeTimerPointage = newArray
}
Thanks for your help

Getter returning same value as the input value before action finish in Vue

I created a component and a module for a functionality X and I'm using Vuex for state management. The code works for the every first time insertion, but after this the Getter function always returns the same value of the input before the action ends and commit the mutation.
For example:
1 - everything is [0,0,0] and Getter is [0,0,0]. Insert 9 in the first position and the value is inserted.
2 - On the second time, the check if the value inserted is equal of what is on the state returns true, so we had to remove this verification.
By the way, the state continues to be modified without the action commit the changes to mutation, and when we look to the Getter (who retrieves the value from the state) the value inserted is already returned by the Getter.
Someone know how to fix this?
Here is the code
component:
Vue.component('profundidade-cell', {
data: function () {
return {
valorProfundidade: [0, 0, 0],
id: this.face + '-' + this.dente,
ids: [
this.face + '-profundidade-sondagem-' + this.dente + '-1',
this.face + '-profundidade-sondagem-' + this.dente + '-2',
this.face + '-profundidade-sondagem-' + this.dente + '-3'
],
changedId: null,
valorInserido: null,
valorAnt: null,
}
},
props: {
arcada: String,
sextante: String,
dente: String,
face: String,
face_json: String,
max_length: Number,
min_value: Number,
max_value: Number,
},
computed: {
profundidadeSondagem() {
return store.getters['profundidadeSondagem/profundidadeSondagem']({arcada: this.arcada,
sextante: this.sextante, dente: "dente_" + this.dente, face: this.face_json});
},
presente() {
return store.getters.dentePresente({arcada: this.arcada, sextante: this.sextante,
dente: "dente_" + this.dente});
}
},
created: function() {
this.debouncedAlterarProfundidade = _.debounce(this.alterarValorProfundidadeSondagem, 400);
this.$root.$on("vuex-carregado", this.carregar);
},
methods: {
getValue(e) {
this.changedId = e.target.id;
this.valorInserido = e.target.value;
this.debouncedAlterarProfundidade();
},
alterarValorProfundidadeSondagem() {
let modelRefs = {};
let patologia = {
arcada: this.arcada,
sextante: this.sextante,
dente: "dente_" + this.dente,
face: this.face_json,
valor: this.valorProfundidade,
modelRefs: modelRefs,
id: this.changedId,
valorInserido: this.valorInserido,
};
store.dispatch('profundidadeSondagem/MODIFY', patologia).catch(() => {
this.valorProfundidade = this.profundidadeSondagem;
})
},
carregar(){
this.valorProfundidade = this.profundidadeSondagem;
}
},
template: `
<div>
<input type="text" :id=ids[0] v-on:input.prevent="getValue($event)" :maxlength=max_length v-model=valorProfundidade[0] class="periograma-input col l4" v-bind:class="{ 'invisible': !presente }" />
<input type="text" :id=ids[1] v-on:input.prevent="getValue($event)" :maxlength=max_length v-model=valorProfundidade[1] class="periograma-input col l4" v-bind:class="{ 'invisible': !presente }" />
<input type="text" :id=ids[2] v-on:input.prevent="getValue($event)" :maxlength=max_length v-model=valorProfundidade[2] class="periograma-input col l4" v-bind:class="{ 'invisible': !presente }" />
</div>
`
});
module:
const moduleProfundidadeSondagem = {
namespaced: true,
actions: {
MODIFY({commit, dispatch, getters, rootGetters}, obj) {
let patologia = {
face: rootGetters.getNomeFace(obj.face),
dente: rootGetters.getNomeDente(obj.dente),
local: "FACE",
ficha: this.state.idPeriograma,
descricao: obj.valor.toString(),
paciente: this.state.idPaciente,
tipo: 'PROFUNDIDADE_DE_SONDAGEM'
};
if(obj.valor) != getters.profundidadeSondagem(obj)) {
let reg = new RegExp(/([0-9],[0-9],[0-9])/);
let param = null;
return new Promise((resolve, reject) => {
if(obj.valor[0] == 0 && obj.valor[1] == 0 && obj.valor[2] == 0) {
param = axios.delete('/patologia', {data: patologia});
} else if (reg.test(obj.valor)){
param = axios.post('/patologia', patologia);
}
if(param != null) {
param.then(function (response) {
if(response.status == 200 || response.status == 201 && response.data) {
commit('armazenarProfundidadeSondagem', obj);
let dentes_data = {};
let face = '';
if (obj.arcada == 'arcada_superior' && obj.face == 'face_lingual_palatal') {
face = 'palatina'
} else {
face = obj.face.split('_')[1];
}
let classe_canvas = rootGetters.getNomeClasseCanvas(obj, face);
drawGraph(rootGetters.prepareDentesData(obj, face, dentes_data), face,
classe_canvas);
resolve();
}
}).catch(error => {
store.dispatch('mensagemAlerta/ALERTA', {
tipo: 'error',
mensagem: 'Não foi possível cadastrar o nível de sondagem'
});
reject(error);
});
}
})
}
},
RESET_PROFUNDIDADE_SONDAGEM({commit}, ob) {
commit('RESET_ALL', ob);
}
},
getters: {
profundidadeSondagem: (state, getters, rootState) => obj => {
return rootState[obj.arcada][obj.sextante][obj.dente][obj.face].profundidade_sondagem;
}
},
my guess is your issue lies here:
TL;DR - you pass your getter as valor to your action.
only a guess - as i cant debug your code
store.dispatch('profundidadeSondagem/MODIFY', patologia).catch(() => {
this.valorProfundidade = this.profundidadeSondagem;
})
as you assign a reference from your rootState[...] and thus you change the properties of the rootState[...] in your component past the first run.
So your code then behaves like:
let patologia = {
arcada: this.arcada,
sextante: this.sextante,
dente: "dente_" + this.dente,
face: this.face_json,
// valor: this.valorProfundidade,
///* same as */ valor: this.profundidadeSondagem,
/* same as */ valor: this.$store.getters['profundidadeSondagem/profundidadeSondagem'](...),
modelRefs: modelRefs,
id: this.changedId,
valorInserido: this.valorInserido,
};
A solution can be to this.valorProfundidade = this.profundidadeSondagem.slice(); as long it is an array or Object.assign({},...) - so you prevent the references

Categories