如何防止子组件中更新的数据更新VueJS中父的数据[2.6.14]

海军陆战队

我正在使用 vuejs 2.6.14 并遇到以下问题:

来自子组件的修改数据也会更新父组件中的数据,而无需在代码中使用任何 $emit。

这与通常的“如何从父级更新子级数据/如何从子级更新父级数据”相反

这是我更详细的代码:

我有一个名为 Testing.vue 的父组件,它将一个 JSON 对象(“userData”)传递给一个子组件 GeneralData.vue。

这是父级的代码:

<template>
  <div id="testing-compo">
    <div style="margin-top: 1rem; margin-bottom: 1rem; max-width: 15rem">
          <label
            class="sr-only"
            for="inline-form-input-username"
            style="margin-top: 1rem; margin-bottom: 1rem"
            >Account settings for :</label
          >
          <b-form-input
            v-model="username"
            id="inline-form-input-username"
            placeholder="Username"
            :state="usernameIsValid"
          ></b-form-input>
        </div>
    <b-button class="button" variant="outline-primary" 
    @click="callFakeUser">
    Populate fake user
    </b-button>
    <GeneralData :userData="user" />
  </div>
</template>
<script>
export default {
  name: "Testing",
  components: {
    GeneralData,
  },
  data() {
    return {
      user: null,
      username: null,
    };
  },
  computed: {
    usernameIsValid: function () {
      if (this.username != null && this.username.length >= 4) {
        return true;
      } else if (this.username != null) {
        return false;
      }

      return null;
    },
  },
  methods: {
    async callFakeUser() {
      userServices.getFakeUser().then((res) => {
        this.user = res;
        console.log(this.user);
      });
    },
</script>

一个非常简单的测试组件,它调用 userServices.getFakeUser(),异步返回一个 JSON 对象。

对于孩子:

<template>
  <div id="general-compo">
    <!-- AGE -->
    <div class="mt-2">
      <label for="text-age">Age</label>
      <div>
        <b-form-input
          v-model="userAge"
          placeholder="+18 only"
          class="w-25 p-1"
          type="number"
        >
        </b-form-input>
      </div>
    </div>
    <!-- LANGUAGES -->
    <div class="mt-2">
      <label for="lang-list-id">Language(s)</label>
      <div
        v-for="langKey in userLangsCount"
        :key="langKey"
        style="display: flex; flex-direction: row"
      >
        <b-form-input
          readonly
          :placeholder="userLangs[langKey - 1]"
          style="max-width: 50%; margin-top: 0.5rem"
          disabled
        ></b-form-input>

        **This form is set to read only, for display purposes only**

        <b-button
          variant="outline-danger"
          @click="removeLang(langKey)"
          style="margin-top: 0.5rem; margin-left: 1rem"
          >Remove</b-button
        >

        **This button removes a language from the userLangs array by calling removeLang(langKey)**

      </div>
      <div style="display: flex; flex-direction: row">
        <b-form-input
          v-model="userCurrentLang"
          list="langlist-id"
          placeholder="Add Language"
          style="max-width: 50%; margin-top: 0.5rem"
        ></b-form-input>
        <datalist id="langlist-id">
          <option>Manual Option</option>
          <option v-for="lang in langList" :key="lang.name">
            {{ lang.name }}
          </option>
        </datalist>
        <b-button
          :disabled="addLangBtnDisabled"
          variant="outline-primary"
          @click="addLang()"
          style="margin-top: 0.5rem; margin-left: 1rem"
          >Add</b-button
        >
      </div>
    </div>
  </div>
</template>
<script>
import langList from "../assets/langList";
export default {
  name: "GeneralData",
  components: {},
  props: {
    userData: Object,
  },
  data() {
    return {
      userAge: null,
      langList: langList,
      userLangs: [],
      userCurrentLang: null,
    };
  },
  watch: {
    //Updating tabs with fetched values
    userData: function () {
      this.userLangs = this.userData.general.langs;
      this.userAge = this.userData.general.age
    },
  },
  computed: {

    **userGeneral is supposed to represent the data equivalent of userData.general, it is therefore computed from the user input, its value is updated each time this.userAge or this.userLangs changes**

    userGeneral: function () {
      //user data in data() have been filled with userData values
      return {
        age: this.userAge,
        langs: this.userLangs,
      };
    },

**returns the amount of languages spoken by the user to display them in a v-for loop**
    userLangsCount: function () {
      if (this.userLangs) {
        return this.userLangs.length;
      }
      return 0;
    },

**gets a list of languages name from the original JSON list for display purposes**
    langNameList: function () {
      let namelist = [];
      for (let i = 0; i < this.langList.length; i++) {
        namelist.push(langList[i].name);
      }
      return namelist;
    },

**returns true or false depending on whether entered language is in original list**
    addLangBtnDisabled: function () {
      for (let i = 0; i < this.langList.length; i++) {
        if (this.userCurrentLang == langList[i].name) {
          return false;
        }
      }
      return true;
    },
  },
  methods: {
    addLang() {
      this.userLangs.push(this.userCurrentLang);
      this.userCurrentLang = null;
    },
    removeLang(key) {
      this.userLangs.splice(key - 1, 1);
    },
  }
}
</script>

以下是在 Testing.vue 中更新 this.user 后,数据在浏览器中的 vuejs dev 工具中的外观:

Testing.vue 中的数据:

user : {
 general:{"age":22,"langs":["French"]}
}

GeneralData.vue 中的数据:

userData : {
  general:{"age":22,"langs":["French"]}
}

userAge : 22

userLangs : ["French"]

userGeneral : 
{
  general:{"age":22,"langs":["French"]}
}

到目前为止一切顺利吧?

那么这里就是问题发生的地方,如果我更改表单中的年龄字段,userAge 会增加,userGeneral.age 会更新,但 userData.general.age 不会。这是预期的,因为 userGeneral.age 是从 this.userAge 计算出来的,而 userData 是一个道具,所以它不应该被改变为一个好的做法(无论如何都不是方法集 userData.general.age = xxx)。但是,如果我点击语言列表中法语旁边的删除按钮,this.userLangs 会按原样更新,现在是 [],this.userGeneral.langs 会更新为 [],因为它是直接从前者计算出来的。而且 userData.general.langs ... 也更新为 [] ,这对我来说真的没有意义。

更糟糕的是,在父级 Testing.vue 中,user.general.langs 现在也设置为 []。

所以不知何故,this.userLangs 更新了道具 this.userData,并且这个道具更新了它在父组件中的原始发件人用户,尽管没有涉及任何类型的 $emit。

我不希望这种情况发生,因为我不认为它应该以这种方式发生,因此是一个 hasard,但也因为我想稍后设置一个“保存”按钮,允许用户一次修改他的所有值。

我尝试过的:在移除/添加按钮上的@click 元素上设置各种 .prevent、.stop,在调用自身的方法中,添加 e.preventDefault(修改 addLang 和 removeLang 以发送 $event 元素) ,这些尝试都没有解决任何问题。

希望我没有正确实现 .prevent 部分,有人可以帮助我阻止这个令人讨厌的逆流问题。

刹车

这里问题的解决方案是 lang 是一个作为引用传递的数组,因此突变会冒泡到父级。我们可以只分配一个副本,而不是分配原始数组

userData: function () {       this.userLangs = [...this.userData.general.langs];       this.userAge = this.userData.general.age     }

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

试图了解C ++ 14中的§7.2/ 6

MatDialog关闭并在Angular 6中更新数据库中的数据后如何刷新组件?

es6 / 7 React14中的StaticProptypes

vuejs中父组件的数据变化不会更新子组件

Jboss Redhat EAP中基于ojdbc14的war文件在ojdbc6中的部署问题?

如何在MATLAB中创建模式为[1 2 5 6 9 10 13 14 17 18 ....]的向量?

如何生成数字序列1,2,5,6,9,10,13,14,

无法理解[basic.link] / 6 C ++ 14示例中的声明#3

Errno 14 PYCURL错误6; 无法解析Cloudera Manager 7.x升级中的主机

VueJS:更改子组件内的数据并更新父组件的数据。

反应Native 23.1意外令牌(14:6)

如何将从 http 获取的数据从 Mat-Dialog 组件传递到 Angular 6 中的父组件?

如何将异步数据传递给对象中的子组件(Angular6)

查询中的MySQL NOW()+ 14或CURDATE()+ 14

如何从angular2中的子组件更新父组件

在Entity Framework 6中更新子对象

C ++ 14中的“ constexpr”

Angular 6中组件之间的数据共享

css第n个孩子选择器,用于孩子2、3、6、7、10、11、14、15等

如何在6个Kendo UI下拉列表框中更新数据

如何使用EF 6将大数据插入或更新到varchar(max)中

如何在子组件中过滤道具后更新父数据?

ODOO 14 中的 XML 显示父对象

每当它在子组件中更新时更新父元素中的数据

ojdbc14.jar与ojdbc6.jar

将Optional(Set([[6“,” 14“]))转换为[String]

Vaadin 14,6 AppLayout 与状态栏?

(VueJS) 从子组件更新父数据

vuejs从子组件更新父数据