简体   繁体   中英

Vue.js form not reactive

So I have simple calculator. User picks font, then size and by typing words, price calculating.

Problem is that some elements in this form not reactive, sorry about terminology I'm new to Vue.js.

Thi is my HTML

<div id="app">

    <ul>
        <li v-for="font in fontTypes" v-on:click="fontType(font)">{{ font.name }}</li>
    </ul>

    <ul>
        <li v-for="currentFont in currentFonts" v-on:click="fontSize(currentFont)">{{ currentFont.size }}</li>
    </ul>

    <input type="text" v-model="result">

    <div id="result">
        <p>{{ finalPrice }}</p>
    </div>

</div>

This is JS:

var formatter = new Intl.NumberFormat('lt-LT', {
        style: 'currency',
        currency: 'EUR',
        minimumFractionDigits: 2
    });

    var vm = new Vue({

        el: '#app',
        data: {
            result: '',
            currentFonts: '',
            selectedFont: '',
            selectedSize: '',
            selectedFontSizePrice: '',
            fontTypes: [
                { name: 'Font 1', params: [ {size: '2.5', price: '3.10'}, {size: '3', price: '5'} ] },
                { name: 'Font 2', params: [ {size: '3', price: '4'}, {size: '4', price: '7'} ] },
                { name: 'Font 3', params: [ {size: '7', price: '45'}, {size: '8', price: '50'} ] }
            ]
        },
        computed: {
            finalPrice: function() {
                var letterCount = this.result.replace(/\s/g, "").length;
                var letterPrice = this.selectedFontSizePrice;
                var price = letterCount * letterPrice;
                return formatter.format(price);
            }
        },
        methods: {
            fontType: function(selectedFont) {
                this.selectedFont = selectedFont.name;
                this.currentFonts = selectedFont.params;
            },
            fontSize: function(selectedSize) {
                this.selectedSize = selectedSize.size;
                this.selectedFontSizePrice = selectedSize.price;
            }
        }

    });

selectedFontSizePrice is not reactive. When I change font (selectedFont) selectedFontSizePrice remains same. I need some kind of clearing if parent selector changes, or even better select first value from list and recalculate finalPrice. Or maybe I'm doing something completely wrong here?

Not sure if I understand the problem, but if you want to update the 'selectedFontSizePrice' value when select a fontType , I think you should call the function fontSize (as far as I can see is the one that modifies the value of 'selectedFontSizePrice' ) from inside the function fontType

        methods: {
            fontType: function(selectedFont) {
                this.selectedFont = selectedFont.name;
                this.currentFonts = selectedFont.params;
                this.fontSize(selectedFont.params[0]);  //select first value
            },
            fontSize: function(selectedSize) {
                this.selectedSize = selectedSize.size;
                this.selectedFontSizePrice = selectedSize.price;
            }
        }

One of the things Vue allows you to do is to write your own components easily. It looks like where someone would normally use a select element, you are choosing to use an unordered list element. You can easily write your own unordered list element that would act like a select by defining a small Vue component.

Vue.component('list-select', {
  props: ["items"],
  template:"<ul class='list-select'><li :class='selectClass(item)' v-for='item in items' :value='item.value' @click='onClick(item)'>{{item.text}}</li>", 
  data(){
    return {
      value: null,
      selectedItem: null
    }
  },
  methods:{
    selectClass(item){
      return {
        selected: item == this.selectedItem
      }
    },
    onClick(item){
      this.selectedItem = item;
      this.$emit('input', item.value);
    }
  }
});

Then, you can use this component in your Vue.

<div id="app">
    <list-select :items="types" v-model="selectedFont" @input="selectedSize = null"></list-select>
    <list-select v-if="selectedFont" :items="sizes" v-model="selectedPrice"></list-select>

    <input type="text" v-model="result">

    <div id="result">
        <p>{{ finalPrice }}</p>
    </div>
</div>

And here is the Vue definition.

var vm = new Vue({
  el: '#app',
  data: {
    result: '',
    selectedFont: null,
    selectedPrice: null,
    fontTypes: [
      { name: 'Font 1', params: [ {size: '2.5', price: '3.10'}, {size: '3', price: '5'} ] },
      { name: 'Font 2', params: [ {size: '3', price: '4'}, {size: '4', price: '7'} ] },
      { name: 'Font 3', params: [ {size: '7', price: '45'}, {size: '8', price: '50'} ] }
    ]
  },
  computed: {
    types(){
      return this.fontTypes.map((type) => {
        return {text: type.name, value: type.params};
      });
    },
    sizes(){
      return this.selectedFont.map((size) => {
        return {text: size.size, value: size.price};
      });
    },
    finalPrice: function() {
      if (this.result.length <= 0)
        return formatter.format(0);

      var letterCount = this.result.replace(/\s/g, "").length;
      return formatter.format(letterCount * this.selectedPrice);
    }
  }
});

This approach makes everything completely reactive and uses Vue idiomatically. Here is a working example .

change this method:

fontType: function(selectedFont) {
    this.selectedFont = selectedFont.name;
    this.currentFonts = selectedFont.params;
}

to:

fontType: function(selectedFont) {
    this.selectedFont = selectedFont.name;
    this.currentFonts = [];
    selectedFont.params.forEach(function(p){
        this.currentFonts.push(p);
    }.bind(this));
}

Array just don't react with '=' operation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM