Vue - दीप वस्तुओं की एक सरणी देख रहे हैं और परिवर्तन की गणना कर रहे हैं?


108

मेरे पास एक सरणी peopleहै, जिसमें ऑब्जेक्ट्स निम्नानुसार हैं:

इससे पहले

[
  {id: 0, name: 'Bob', age: 27},
  {id: 1, name: 'Frank', age: 32},
  {id: 2, name: 'Joe', age: 38}
]

यह बदल सकता है:

उपरांत

[
  {id: 0, name: 'Bob', age: 27},
  {id: 1, name: 'Frank', age: 33},
  {id: 2, name: 'Joe', age: 38}
]

नोटिस फ्रैंक सिर्फ 33 साल का हो गया।

मेरे पास एक ऐप है जहां मैं लोगों को सरणी देखने की कोशिश कर रहा हूं और जब कोई भी मान बदलता है तो परिवर्तन लॉग करें:

<style>
input {
  display: block;
}
</style>

<div id="app">
  <input type="text" v-for="(person, index) in people" v-model="people[index].age" />
</div>

<script>
new Vue({
  el: '#app',
  data: {
    people: [
      {id: 0, name: 'Bob', age: 27},
      {id: 1, name: 'Frank', age: 32},
      {id: 2, name: 'Joe', age: 38}
    ]
  },
  watch: {
    people: {
      handler: function (val, oldVal) {
        // Return the object that changed
        var changed = val.filter( function( p, idx ) {
          return Object.keys(p).some( function( prop ) {
            return p[prop] !== oldVal[idx][prop];
          })
        })
        // Log it
        console.log(changed)
      },
      deep: true
    }
  }
})
</script>

मैंने इस सवाल के आधार पर कहा कि मैंने कल सरणी तुलना के बारे में पूछा और सबसे तेज काम करने वाले उत्तर का चयन किया।

इसलिए, इस बिंदु पर मुझे इसका परिणाम देखने की उम्मीद है: { id: 1, name: 'Frank', age: 33 }

लेकिन सभी कंसोल में वापस मिल जाता है (यह ध्यान में रखते हुए कि मेरे पास एक घटक है):

[Vue warn]: Error in watcher "people" 
(found in anonymous component - use the "name" option for better debugging messages.)

और मेरे द्वारा बनाए गए कोडपैन में , परिणाम एक खाली सरणी है और परिवर्तित वस्तु नहीं है जो कि बदल गई है जो कि मुझे उम्मीद थी।

अगर कोई सुझाव दे सकता है कि ऐसा क्यों हो रहा है या जहां मैं यहां गलत हो गया हूं तो यह बहुत सराहना की जाएगी, बहुत धन्यवाद!

जवाबों:


136

पुराने मूल्य और नए मूल्य के बीच आपके तुलनात्मक कार्य में कुछ समस्या है। बेहतर होगा कि चीजों को इतना जटिल न करें, क्योंकि यह बाद में आपके डिबगिंग प्रयास को बढ़ा देगा। आपको इसे सरल रखना चाहिए।

सबसे अच्छा तरीका यह है कि person-componentप्रत्येक व्यक्ति को उसके अपने घटक के अंदर अलग से देखा और बनाया जा सके , जैसा कि नीचे दिखाया गया है:

<person-component :person="person" v-for="person in people"></person-component>

व्यक्ति घटक के अंदर देखने के लिए कृपया एक कार्यकारी उदाहरण नीचे देखें। यदि आप इसे अभिभावक की ओर से संभालना चाहते हैं, तो आप $emitएक घटना idको संशोधित व्यक्ति को भेजने के लिए उपयोग कर सकते हैं ।

Vue.component('person-component', {
    props: ["person"],
    template: `
        <div class="person">
            {{person.name}}
            <input type='text' v-model='person.age'/>
        </div>`,
    watch: {
        person: {
            handler: function(newValue) {
                console.log("Person with ID:" + newValue.id + " modified")
                console.log("New age: " + newValue.age)
            },
            deep: true
        }
    }
});

new Vue({
    el: '#app',
    data: {
        people: [
          {id: 0, name: 'Bob', age: 27},
          {id: 1, name: 'Frank', age: 32},
          {id: 2, name: 'Joe', age: 38}
        ]
    }
});
<script src="https://unpkg.com/vue@2.1.5/dist/vue.js"></script>
<body>
    <div id="app">
        <p>List of people:</p>
        <person-component :person="person" v-for="person in people"></person-component>
    </div>
</body>


यह वास्तव में एक कार्यशील समाधान है लेकिन यह पूरी तरह से मेरे उपयोग के मामले के अनुसार नहीं है। आप देखते हैं, वास्तविकता में मेरे पास ऐप और एक घटक है, घटक vue- सामग्री तालिका का उपयोग करता है और इन-लाइन में मूल्यों को संपादित करने की क्षमता के साथ डेटा को सूचीबद्ध करता है। मैं मूल्यों में से एक को बदलने की कोशिश कर रहा हूं, फिर जांचें कि इस मामले में क्या बदल गया है, यह वास्तव में सरणियों से पहले और बाद की तुलना करता है कि यह देखने के लिए कि क्या अंतर है। क्या मैं समस्या को हल करने के लिए आपके समाधान को लागू कर सकता हूं? वास्तव में मैं ऐसा करने की संभावना कर सकता हूं, लेकिन यह महसूस कर सकता हूं कि यह इस बात के प्रवाह के खिलाफ काम कर रहा है कि इस संबंध में क्या सामग्री उपलब्ध है
क्रेग वैन टोनर

2
वैसे, यह समझाने के लिए समय निकालने के लिए धन्यवाद, इससे मुझे Vue के बारे में और जानने में मदद मिली जिसकी मैं सराहना करता हूं!
क्रेग वैन टोनर

इसे समझने में मुझे थोड़ा समय लगा लेकिन आप बिल्कुल सही हैं, यह एक आकर्षण की तरह काम करता है और चीजों को करने का सही तरीका है यदि आप भ्रम और आगे के मुद्दों से बचना चाहते हैं :)
क्रेग वैन टोनर

1
मैंने इसे भी नोटिस किया था और एक ही विचार था लेकिन जो वस्तु में निहित है वह भी मूल्य सूचकांक है जिसमें मूल्य होता है, गेटर्स और सेटर होते हैं लेकिन तुलना में यह उन्हें अवहेलना करता है, एक बेहतर समझ की कमी के लिए मुझे लगता है कि यह करता है किसी भी प्रोटोटाइप पर मूल्यांकन नहीं। अन्य उत्तरों में से एक यह कारण प्रदान करता है कि यह काम क्यों नहीं करेगा, यह इसलिए है क्योंकि newVal और oldVal एक ही बात थे, यह थोड़ा जटिल है, लेकिन कुछ स्थानों पर कुछ thats को संबोधित किया गया है, फिर भी एक और उत्तर आसान निर्माण के लिए एक सभ्य काम प्रदान करता है तुलना उद्देश्यों के लिए एक अपरिवर्तनीय वस्तु।
क्रेग वैन टोनर

1
अंत में, हालांकि, आपका तरीका एक नज़र में समझना आसान है और मूल्य में परिवर्तन होने पर जो उपलब्ध है उसके संदर्भ में अधिक लचीलापन प्रदान करता है। यह मुझे Vue में सरल रखने के लाभों को समझने में बहुत मदद मिली है लेकिन इसके साथ थोड़ा अटक गया है जैसा कि आपने मेरे अन्य प्रश्न में देखा था। बहुत धन्यवाद! :)
क्रेग वैन टोनर

21

मैंने आपकी समस्या को हल करने के लिए इसका कार्यान्वयन बदल दिया है, मैंने पुराने परिवर्तनों को ट्रैक करने और इसके साथ तुलना करने के लिए एक वस्तु बनाई है। आप इसका उपयोग अपनी समस्या को हल करने के लिए कर सकते हैं।

यहां मैंने एक विधि बनाई, जिसमें पुराने मूल्य को एक अलग चर में संग्रहीत किया जाएगा और, जो तब एक घड़ी में उपयोग किया जाएगा।

new Vue({
  methods: {
    setValue: function() {
      this.$data.oldPeople = _.cloneDeep(this.$data.people);
    },
  },
  mounted() {
    this.setValue();
  },
  el: '#app',
  data: {
    people: [
      {id: 0, name: 'Bob', age: 27},
      {id: 1, name: 'Frank', age: 32},
      {id: 2, name: 'Joe', age: 38}
    ],
    oldPeople: []
  },
  watch: {
    people: {
      handler: function (after, before) {
        // Return the object that changed
        var vm = this;
        let changed = after.filter( function( p, idx ) {
          return Object.keys(p).some( function( prop ) {
            return p[prop] !== vm.$data.oldPeople[idx][prop];
          })
        })
        // Log it
        vm.setValue();
        console.log(changed)
      },
      deep: true,
    }
  }
})

अपडेटेड कोडपेन देखें


इसलिए जब यह माउंट किया जाता है तो डेटा की एक प्रति संग्रहीत करता है और इसके खिलाफ तुलना करने के लिए इसका उपयोग करता है। दिलचस्प है, लेकिन मेरा उपयोग मामला अधिक जटिल होगा और मैं अनिश्चित हूं कि कैसे वस्तुओं को जोड़ने और सरणी से हटाने पर काम करेगा, @Quirk ने समस्या को हल करने के लिए अच्छे लिंक भी प्रदान किए। लेकिन मुझे नहीं पता था कि आप इसका उपयोग कर सकते हैं vm.$data, धन्यवाद!
क्रेग वैन टोनर

हां, और मैं घड़ी के बाद इसे अपडेट कर रहा हूं, फिर से विधि को कॉल करके, इसके द्वारा यदि आप मूल मूल्य पर वापस जाते हैं, तो यह भी परिवर्तन को ट्रैक करेगा।
विप्रलोक

ओह्ह, मैंने यह नहीं देखा कि वहाँ छिपना, बहुत मायने रखता है और इससे निपटने के लिए एक कम जटिल तरीका है (जैसा कि गीथब पर समाधान के विपरीत)।
क्रेग वैन टोनर

और हां, यदि आप मूल सरणी से कुछ जोड़ या हटा रहे हैं, तो बस विधि को फिर से कॉल करें और आप फिर से समाधान के साथ जाने के लिए अच्छे हैं।
विप्रलोक

1
_.cloneDeep () ने वास्तव में मेरे मामले में मदद की। धन्यवाद!! वास्तव में मददगार!
क्रिस्टियाना परेरा

18

यह अच्छी तरह से परिभाषित व्यवहार है। आप किसी बदले हुए ऑब्जेक्ट के लिए पुराना मान प्राप्त नहीं कर सकते । ऐसा इसलिए है क्योंकि दोनों है newValऔर oldValएक ही वस्तु को देखें। Vue आपके द्वारा म्यूट किए गए ऑब्जेक्ट की एक पुरानी प्रतिलिपि नहीं रखेगा।

यदि आपने वस्तु को दूसरे के साथ बदल दिया होता, तो Vue आपको सही संदर्भ प्रदान करता।

डॉक्सNote में अनुभाग पढ़ें । ( vm.$watch)

इस पर अधिक यहाँ और यहाँ


3
ओह मेरी टोपी, बहुत बहुत धन्यवाद! यह एक मुश्किल है ... मैं पूरी तरह से वैल और ओल्डवैल से अलग होने की उम्मीद करता हूं, लेकिन उनका निरीक्षण करने के बाद मैं देखता हूं कि यह नई सरणी की दो प्रतियां हैं, यह पहले इसका ट्रैक नहीं रखता है। थोड़ा और पढ़ें और एक ही गलतफहमी के बारे में यह अनुत्तरित एसओ प्रश्न पाया गया: stackoverflow.com/questions/35991494/…
क्रेग वैन टोनर

5

यह वही है जो मैं एक वस्तु को देखने के लिए उपयोग करता हूं। मेरी आवश्यकता वस्तु के बाल क्षेत्रों को देख रही थी।

new Vue({
    el: "#myElement",
    data:{
        entity: {
            properties: []
        }
    },
    watch:{
        'entity.properties': {
            handler: function (after, before) {
                // Changes detected.    
            },
            deep: true
        }
    }
});

मुझे विश्वास है कि आप उस गुफा की समझ को याद कर रहे होंगे जो कि stackoverflow.com/a/41136186/2110294 में वर्णित थी । बस स्पष्ट होने के लिए, यह प्रश्न का हल नहीं है और यह काम नहीं करेगा क्योंकि आपने कुछ स्थितियों में उम्मीद की होगी।
क्रेग वैन टोनर

यह वही है जो मैं देख रहा था! धन्यवाद
जयदीप Shil

यहाँ वही, वास्तव में मुझे क्या चाहिए !! धन्यवाद।
गुंटर

4

घटक समाधान और गहरे-क्लोन समाधान के अपने फायदे हैं, लेकिन इसके मुद्दे भी हैं:

  1. कभी-कभी आप सार डेटा में परिवर्तनों को ट्रैक करना चाहते हैं - यह हमेशा उस डेटा के चारों ओर घटक बनाने के लिए समझ में नहीं आता है।

  2. हर बार जब आप कोई बदलाव करते हैं तो आपके पूरे डेटा स्ट्रक्चर को डीप-क्लोनिंग करना बहुत महंगा हो सकता है।

मुझे लगता है कि एक बेहतर तरीका है। यदि आप किसी सूची में सभी आइटम देखना चाहते हैं और यह जानना चाहते हैं कि सूची में कौन सी वस्तु बदली गई है, तो आप प्रत्येक आइटम पर अलग से कस्टम वॉचर्स सेट कर सकते हैं, जैसे:

var vm = new Vue({
  data: {
    list: [
      {name: 'obj1 to watch'},
      {name: 'obj2 to watch'},
    ],
  },
  methods: {
    handleChange (newVal) {
      // Handle changes here!
      console.log(newVal);
    },
  },
  created () {
    this.list.forEach((val) => {
      this.$watch(() => val, this.handleChange, {deep: true});
    });
  },
});

इस संरचना के साथ, handleChange()विशिष्ट सूची आइटम प्राप्त होगा जो बदल गया है - वहां से आप अपनी पसंद के अनुसार कोई भी काम कर सकते हैं।

यदि आपने अपनी सूची में आइटम जोड़ / हटा रहे हैं (केवल पहले से ही आइटम में हेरफेर करने के बजाय), तो मैंने यहां एक और अधिक जटिल परिदृश्य का दस्तावेजीकरण किया है ।


धन्यवाद एरिक, आप मान्य बिंदु प्रस्तुत करते हैं और प्रदान की गई कार्यप्रणाली सबसे निश्चित रूप से उपयोगी है यदि प्रश्न के समाधान के रूप में कार्यान्वित किया जाता है।
क्रेग वैन टोनर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.