вычисляемый набор свойств не вызывается в Vue - PullRequest
10 голосов
/ 09 июля 2020

Согласно документации, я должен иметь возможность использовать вычисленные свойства как v-model в Vue, пока я определяю методы get / set, но в моем случае это не работает:

export default{

    template: `
      <form class="add-upload" @submit.prevent="return false">
        <label><input type="checkbox" v-model="options.test" /> test </label>
      </form>
    `,

    computed: {

      options: {

        get(){
          console.log('get');
          return {test: false};
        },

        set(value){
          console.log('set');
        },

      },

    }

}

По-видимому, set не вызывается, когда я проверяю / снимаю флажок ввода. Но get вызывается при отображении компонента ...

Ответы [ 6 ]

4 голосов
/ 13 июля 2020

Edit : После прочтения комментариев, в которых вы полагаетесь на localstorage, я могу только предложить вам принять подход Vuex и использовать библиотеку постоянства для обработки localstorage. ( https://www.npmjs.com/package/vuex-persist) Таким образом, ваше локальное хранилище всегда будет связано с вашим приложением, и вам не придется каждый раз связываться с getItem / setItem.

Глядя на ваш подход, я предполагаю, что вы есть причины использовать вычисляемое свойство вместо свойства данных.

Проблема возникает из-за того, что вычисляемое свойство возвращает объект, определенный нигде, кроме как в обработчике get. Что бы вы ни пытались, вы не сможете управлять этим объектом в обработчике set.

get и set должны быть связаны с общей ссылкой. Свойство данных, как многие предлагали, или источник истины в вашем приложении (очень хороший пример - экземпляр Vuex).

Таким образом, ваш v-model будет безупречно работать с set обработчиком ваше вычисленное свойство.

Вот рабочая скрипка, демонстрирующая объяснение:

С Vuex

const store = new Vuex.Store({
  state: {
    // your options object is predefined in the store so Vue knows about its structure already
    options: {
      isChecked: false
    }
  },
  mutations: {
    // the mutation handler assigning the new value
    setIsCheck(state, payload) {
      state.options.isChecked = payload;
    }
  }
});

new Vue({
  store: store,
  el: "#app",
  computed: {
    options: {
      get() {
        // Here we return the options object as depicted in your snippet
        return this.$store.state.options;
      },
      set(checked) {
        // Here we use the checked property returned by the input and we commit a Vuex mutation which will mutate the state
        this.$store.commit("setIsCheck", checked);
      }
    }
  }
})
body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
}

h2 {
  font-weight: bold;
  margin-bottom: 15px;
}
<div id="app">
  <h2>isChecked: {{ options.isChecked }}</h2>
  <input type="checkbox" v-model="options.isChecked" />
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/vuex@2.0.0"></script>

Со свойством данных

new Vue({
  el: "#app",
  data: {
    options: {
      isChecked: false
    }
  },
  computed: {
    computedOptions: {
      get() {
        return this.options;
      },
      set(checked) {
        this.options.isChecked = checked;
      }
    }
  }
})
body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
}

h2 {
  font-weight: bold;
  margin-bottom: 15px;
}
<div id="app">
  <h2>isChecked: {{ computedOptions.isChecked }}</h2>
  <input type="checkbox" v-model="computedOptions.isChecked" />
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

Ваш подход немного особенный ИМХО, но, опять же, у вас должны быть причины для этого.

4 голосов
/ 13 июля 2020

Вместо computed геттера / сеттера используйте свойство локальных данных, инициализированное целевым localStorage элементом; и глубокий наблюдатель (который обнаруживает изменения в любом субсвойстве), который устанавливает localStorage при изменении. Это позволяет вам по-прежнему использовать v-model с опорой локальных данных, наблюдая за изменениями подсвойств объекта.

Шаги:

  1. Объявление локальных данных prop (с именем options), который инициализируется текущим значением localStorage:
export default {
  data() {
    return {
      options: {}
    }
  },
  mounted() {
    const myData = localStorage.getItem('my-data')
    this.options = myData ? JSON.parse(myData) : {}
  },
}
Объявите часы в опоре данных (options), установив deep=true и handler для функции, которая устанавливает localStorage с новым значением:
export default {
  watch: {
    options: {
      deep: true,
      handler(options) {
        localStorage.setItem('my-data', JSON.stringify(options))
      }
    }
  },
}

демонстрация

2 голосов
/ 14 июля 2020

Очень простое объяснение здесь, в коде. вычисляемые свойства зависят от других данных / реактивных переменных. Если бы только тогда, когда реактивные свойства изменили свои значения и если это же свойство использовалось для вычисления некоторых других вычисляемых свойств, тогда вычисленное свойство стало бы реактивным.

таким образом мы должны установить значения и получить методы setter и getter. *

new Vue({
  el: '#app',
  data: {
    message: 'Use computed property on input',
    foo:0,
    isChecked:true
  },
  computed:{
   bar:{
    get: function(){
        return this.foo;
    },
   set: function(val){
     this.foo = val;
    }
   },
   
    check:{
    get: function(){
        return this.isChecked;
    },
   set: function(val){
     this.isChecked = val;
    }
   }
  }
})
<script src="https://unpkg.com/vue"></script>

<div id="app">
  <p>{{ message }} Text</p>
  <input type="text" v-model="bar" /> 
  {{bar}}

<br/>
 <p>{{ message }} Checkbox</p>
    <input type="checkbox" v-model="check" /> 
    
    {{check}}
</div>
2 голосов
/ 13 июля 2020

Возвращаемое значение Vue вычисленных свойств не становится реактивным автоматически. Поскольку вы возвращаете простой объект и назначаете свойству в вычисленное свойство, сеттер не сработает.

У вас есть две проблемы, которые вам нужно решить, одна решение проблемы - сохранить реактивную версию вычисленного значения свойства (см. Vue.observable()). Следующая проблема немного более тонкая, мне нужно знать , почему вы хотите подключиться к сеттеру. Я могу предположить, что без дополнительной информации вы действительно хотите вызвать побочные эффекты. В этом случае вы должны следить за изменением значения (см. vm.$watch()).

Вот как я бы написал этот компонент на основе предположений выше.

export default {
  template: `
      <form class="add-upload" @submit.prevent="return false">
        <label><input type="checkbox" v-model="options.test" /> test </label>
      </form>
    `,
  computed: {
    options(vm) {
      return (
        vm._internalOptions ||
        (vm._internalOptions = Vue.observable({ test: false }))
      )
    },
  },
  watch: {
    "options.test"(value, previousValue) {
      console.log("set")
    },
  },
}

Если вам нужно чтобы вызвать побочные эффекты на основе чего-либо, изменяющегося на options, вы можете внимательно это посмотреть. Самая большая оговорка заключается в том, что объект должен быть реактивным (решается с помощью Vue.observable() или определяется в параметре data).

export default {
  watch: {
    options: {
      handler(value, previousValue) {
        console.log("set")
      },
      deep: true,
    },
  },
}
2 голосов
/ 12 июля 2020

Похоже, проблема заключается как в наличии options, так и в возвращаемом значении геттера.

Вы можете попробовать следующее:

let options;

try {
  options = JSON.parse(localStorage.getItem("options"));
}
catch(e) {
  // default values
  options = { test: true };
}

function saveOptions(updates) {
  localStorage.setItem("options", JSON.stringify({ ...options, ...updates }));
}

export default{
  template: `
    <form class="add-upload" @submit.prevent="return false">
      <label><input type="checkbox" v-model="test" /> test </label>
    </form>`,
  computed: {
    test: {
      get() {
        console.log('get');
        return options.test;
      },
      set(value) {
        console.log('set', value);
        saveOptions({ test: value });
      },
    },
  }
}

Надеюсь, это поможет.

2 голосов
/ 12 июля 2020

Я не знаю, есть ли здесь метод вычисляемого множества, но есть несколько других подходов к решению проблемы.

Если вам нужен единственный получатель для изменения данных, вы можете использовать метод, основанный на событиях, для установки данных. Это мой любимый метод:

export default {
  template: `
      <form class="add-upload" @submit.prevent="">
        <label for="test"> test </label>
        {{options.test}}
        <input id="test" type="checkbox" v-model="options.test" @input="setOptions({test: !options.test})"/>
      </form>
    `,
  data() {
    return {
      optionsData: {
        test: false
      }
    }
  },
  computed: {
    options: {
      get() {
        return this.optionsData;
      },
    },
  },
  methods: {
    setOptions(options) {
      this.$set(this, "optionsData", { ...this.optionsData, ...options })
    }
  }
}

Если вы на самом деле ничего не делаете в get / set, вы можете просто использовать опцию data

export default {
  template: `
      <form class="add-upload" @submit.prevent="">
        <label for="test"> test </label>
        {{options.test}}
        <input id="test" type="checkbox" v-model="options.test" />
      </form>
    `,
  data() {
    return {
      options: {
        test: false
      }
    }
  }
}

Тогда есть также опция получить / установить для каждого свойства

export default {
  template: `
      <form class="add-upload" @submit.prevent="">
        <label for="test"> test </label>
        {{test}}
        <input id="test" type="checkbox" v-model="test" />
      </form>
    `,
  data() {
    return {
      optionsData: {
        test: false
      }
    }
  },
  computed: {
    test: {
      get() {
        return this.optionsData.test;
      },
      set(value) {
        this.optionsData.test = value
      }
    },
  },
}
...