Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
128 views
in Technique[技术] by (71.8m points)

vue.js - Multiple range inputs related on number from parent

I have 4 range inputs. Each of them has min number 0, max number 10. In Total they can't sum to more than 22.

One way to approach this would be to disable all inputs once they hit 22 and add a reset button. I would find it to be more user-friendly to allow the ranges to be decremented after the max is reached instead of a whole reset.

I tried disabling if it's less or equal 0, but the scroller was still under control.

Check the comments on the sandbox here if it easier , but the parent class is as below:

<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{
    &__title{
      @include center;
      margin-top: 15px;
      span{
        font-size: 20px;
        margin: 0 5px;
        color: $pink;
      }
  }
}
</style>

Child class here:

<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;
    &__input{
      margin-left: auto;
      width: 60%;
      margin-right: 20px;
    }
    &__res{
      position: absolute;
      top: 20%;
      right: 0;
    }
    &__label{
    }
  }
</style>
question from:https://stackoverflow.com/questions/65891801/multiple-range-inputs-related-on-number-from-parent

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The way I'd implement this is by using a watch and the get and set method of computed.

The array of values would be updated via a computed. This makes it easy to hook into a v-model and allows us to maintain reactivity with the original array.

The watch is then used to compute the total that is available. Then, for bonus points, we can use the total to adjust the width of the input so the step size remains consistent.

Even though this is using the composition Api, you can implement that using data, watch and computed the classical way

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/[email protected]/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>

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...