#vue.js #input
#vue.js #ввод
Вопрос:
У меня есть 4 входа диапазона. Каждый из них имеет минимальное число 0, максимальное число 10. В общей сложности они не могут составлять более 22.
Одним из способов решения этой проблемы было бы отключить все входные данные, как только они достигнут 22, и добавить кнопку сброса. Я бы счел более удобным для пользователя разрешить уменьшение диапазонов после достижения максимального значения вместо полного сброса.
Я попытался отключить, если оно меньше или равно 0, но скроллер все еще находился под контролем.
Проверьте комментарии к песочнице здесь, если это проще, но родительский класс такой, как показано ниже:
<template>
<div class="vote">
<div class="vote__title">Left: <span>{{ hmLeft }}</span> votes</div>
<div class="vote__body">
<div v-for="user in activeInnerPoll" :key="user._id">
<userVoteFor :hmLeft="hmLeft" @cntCount="cntCount" :id="user._id"/>
</div>
</div>
</div>
</template>
<script>
import { mapGetters } from "vuex"
import userVoteFor from "@/components/userVoteFor";
export default {
name: "Vote.vue",
components: {
userVoteFor
},
data(){
return {
votes: 22,
objRes: {} // that's where we write what id of a user and how many counts
}
},
computed: {
...mapGetters("polls", ["activeInnerPoll"]), // array of objects {_id : "some_id", cnt: 0}
hmLeft(){ // how much left, counter which tells how many votes left
let sum = 0;
for(let key in this.objRes){
sum = this.objRes[key];
}
return this.votes - sum;
}
},
methods: {
cntCount(id, cnt){ // emit for children, gets id and cnt of input-range and sets to result obj
this.objRes[id] = parseInt(cnt);
}
}
}
</script>
<style scoped lang="scss">
@import "@/assets/vars.scss";
@import "@/assets/base.scss";
.vote{
amp;__title{
@include center;
margin-top: 15px;
span{
font-size: 20px;
margin: 0 5px;
color: $pink;
}
}
}
</style>
Дочерний класс здесь:
<template>
<div class="vote__component">
<label class="vote__component__label" :for="id">{{ playerNameById( id )}}</label>
<input @input="check($event)" // thought maybe something to do with event ?
:disabled="disable"
class="vote__component__input"
:id="id"
type="range"
min="0"
max="10"
step="1"
v-model="cnt">
<div class="vote__component__res">{{ cnt }}</div>
</div>
</template>
<script>
import { mapGetters } from "vuex";
export default {
name: "userVoteFor.vue",
props: {
id: {
type: String,
required: true
},
hmLeft: {
type: Number,
required: true
}
},
emits: ["cntCount"],
data() {
return {
cnt: 0,
disable: false,
lastVal: 0
}
},
computed: {
...mapGetters("user", ["playerNameById"]) // gets map object which stores names for user by id
},
methods: {
check(e){
console.log(e);
if(this.hmLeft <= 0) { //HERE IS APART WHERE I THINK SHOULD BE WRITTEN LOGIC if hmLeft <= 0 then ... , else write cnt in resObj and computed var will calc how many votes left
this.lastVal = this.cnt;
this.cnt = this.lastVal;
}
else this.$emit("cntCount", this.id, this.cnt);
}
}
}
</script>
<style scoped lang="scss">
.vote__component{
width: 80%;
margin: 10px auto;
position: relative;
display: flex;
justify-content: right;
padding: 10px 0;
font-size: 15px;
amp;__input{
margin-left: auto;
width: 60%;
margin-right: 20px;
}
amp;__res{
position: absolute;
top: 20%;
right: 0;
}
amp;__label{
}
}
</style>
Ответ №1:
Я бы реализовал это с помощью метода watch
and get
и set
computed
.
Массив значений будет обновляться с помощью вычисляемого. Это упрощает подключение к a v-model
и позволяет нам поддерживать реактивность с исходным массивом.
Затем часы используются для вычисления доступного общего количества. Затем, для получения бонусных очков, мы можем использовать общее количество, чтобы отрегулировать ширину ввода, чтобы размер шага оставался неизменным.
Несмотря на то, что для этого используется composition Api, вы можете реализовать это с помощью data
watch
и computed
классическим способом
const makeRange = (max, vals, index) => {
const defaultMax = 10;
const num = Vue.computed({
get: () => vals[index],
set: value => vals[index] = Number(value)
});
const total = Vue.computed(() => vals.reduce((a, b) => a b, 0), vals);
const style = Vue.computed(() => {
return `width: ${(numMax.value * 12 20)}px`
})
const numMax = Vue.computed(() => {
return Math.min(defaultMax, (num.value max - total.value))
}, total);
return {num, numMax, style};
};
const app = Vue.createApp({
setup() {
const vals = Vue.reactive([5, 5, 5])
const max = 22;
const ranges = vals.map((v,i)=>makeRange(max, vals, i));
// helpers for visualising
const total = Vue.computed(() => vals.reduce((a, b) => a b, 0), vals);
const totalLeft = Vue.computed(() => max - total.value , total.value);
return {ranges, vals, totalLeft, total, max};
}
}).mount('#app');
<script src="https://unpkg.com/vue@3.0.2/dist/vue.global.prod.js"></script>
<div id="app">
<li v-for="range in ranges">
<input
:style="range.style.value"
type="range" min="0"
:max="range.numMax.value"
v-model="range.num.value"
>
value: {{range.num.value}}
max: {{range.numMax.value}}
</li>
<li>{{ vals.join(' ') }} = {{ total }}</li>
<li>max is {{ max }} , minus total {{total }} is {{ totalLeft }}</li>
</div>