25

我有一个父/子组件设置,其中父级从服务器加载数据并通过道具将其传递给子级。在孩子中,我想用它从父母那里收到的一些数据来实例化一个 jQuery 日历。

为了在设置日历之前等待数据,我在父级中广播了一个事件,我在子级中设置了事件侦听器。

侦听器正在孩子中被解雇,但如果 I this.$log('theProp'),它是未定义的。但是,如果我使用 VueJs devtools 检查组件,就会发现父/子关系存在并且子级同时收到了道具。

该道具在孩子上定义为动态道具:the-prop="theProp"。由于孩子最终确实收到了道具,我假设我的设置是正确的,但似乎有某种延迟。父级在 ajax 调用的返回函数中设置道具,并再次:它正在工作,只是看起来有一点延迟。

我还尝试watch在孩子的道具上注册一个监听器,这样我就可以设置日历并确保道具在那里。但是,监视侦听器会触发,但this.$log('theProp')仍未定义。

如果我将数据与广播调用一起传递,就像this.$broadcast('dataLoaded', theData)孩子收到它一样。但是这样做似乎是错误的,因为我基本上是在构建自己的道具处理程序。

我没有发布任何代码,因为组件相当大,并且 VueJs devtools 告诉我父/子情况正在工作。

我错过了一些信息吗?在父母中设置一个值和接收它的孩子之间是否有轻微的延迟?在孩子中等待父母数据的正确方法是什么?

通常,当您只是将数据渲染到模板中时,时间并不重要,因为数据已绑定到模板。但在这种情况下,我真的需要数据来设置日历,否则会出错。

谢谢。

编辑1:这是一个jsfiddle:https ://jsfiddle.net/dr3djo0u/1/ 似乎确认广播后数据不可用。然而,观察者确实可以工作,尽管我几乎可以发誓,当我设置那个测试用例时,它有时会this.$log('someData')返回。undefined

但我想我的问题可能出在其他地方,我今晚去看看,我现在没有这个项目。

编辑2:做了更多的测试。我的问题是a)事件侦听器似乎没有立即接收数据,b)如果已经存在(例如,当来自父级时),我也试图在route.data回调中初始化日历,但是在someData组件已准备就绪,因此它也无法在那里工作。

我的解决方案现在是这样的:

// works when the child route is loaded directly and parent finishes loading someData
watch: {
    someData() {
        this.initCalendar();
    }
},

// works when navigating from parent (data already loaded)
ready() {
    if (this.someData && this.someData.length) {
        this.initCalendar()
    }
}
4

5 回答 5

50

据我所知,您不需要事件来将数据从父母传递给孩子。

您只需要在子组件中:props: ['theProp']

并且在父组件中使用子组件时:<child :theProp="someData"></child>

现在,无论您更改父组件中的任何位置someData,子组件都会做出相应的反应。

你不需要事件,你不需要“观看”,你不需要“准备好”。


例如:在 AJAX 调用之后,在父级的“准备就绪”中,您加载了一些数据:

// at the parent component

data: function () {
  return {
    someData: {}
  }
},

ready: function () {
  var vm = this;
  $.get(url, function(response) {
    vm.someData = response;
  });
}

现在,您不需要其他任何东西来将数据传递给 child。它已经在孩子身上了theProp

你真正需要做的是,在孩子身上,有一些东西可以对自己财产上的数据变化做出反应。theProp

在界面中:

<div v-if="theProp.id > 0">
  Loaded!
</div>

或者在 JavaScript 代码中:

// at the child component

computed: {
  // using a computed property based on theProp's value
  awesomeDate: function() {
    if (!this.theProp || (this.theProp.length === 0)) {
      return false;
    }
    if (!this.initialized) {
      this.initCalendar();
    }
    return this.theProp.someThing;
  }
}

更新 1

您还可以在父级中,有条件地渲染子级:

<child v-if="dataLoaded" :theProp="someData"></child>

dataLoaded在数据可用时设置为 true。

更新 2

或者您的问题可能与变更检测警告有关

也许你正在一个对象中创建一个新属性......

vm.someObject.someProperty = someValue

......当你应该做......

vm.$set('someObject.someProperty', someValue)

...在其他“警告”中。

更新 3

在 VueJS 2 中,您不仅限于模板。您可以使用渲染函数并编写您想要的最复杂的渲染逻辑。

更新 4(关于 OP 的编辑 2

也许您可以删除ready并使用immediate选项,因此您的初始化在一个地方:

watch: {
  someData: {
    handler: function (someData) {
      // check someData and eventually call
      this.initCalendar();
    },
    immediate: true
  }
}
于 2016-09-27T19:33:02.387 回答
6

这是因为Vue Parent 和 Child 生命周期钩子中的棘手行为。

通常父组件触发 created()钩子然后mount()钩子,但是当有子组件时,情况并非如此:父组件触发 created(),然后他的子组件触发created(),然后mount()并且只有在子组件的mount()钩子被加载之后,父组件才会加载他mount()的解释here。这就是为什么不加载子组件中的道具的原因。

改用mounted()钩子created()

像那样https://jsfiddle.net/stanimirsp5/xnwcvL59/1/

于 2020-06-24T12:15:14.073 回答
1

视图 3

好的,所以我花了大约 1.5 小时试图找出如何将道具从父母传递给孩子:

孩子

<!-- Template -->
<template>
  <input type="hidden" name="_csrf_token" :value="csrfToken">
  <span>
    {{ csrfToken }}
  </span>
</template>

<!-- Script -->
<script>
export default {
  props: [
    "csrfToken"
  ]
}
</script>

家长

<!-- Template -->
<template>
  <form @submit.prevent="submitTestMailForm" v-bind:action="formActionUrl" ref="form" method="POST">
...
    <CsrfTokenInputComponent :csrf-token="csrfToken"/>
...
  </form>
</template>

<!-- Script -->
<script>
...
export default {
  data(){
    return {
      ...
      csrfToken : "",
    }
  },
  methods: {
    /**
     * @description will handle submission of the form
     */
    submitTestMailForm(){
      let csrfRequestPromise = this.getCsrfToken();

      let ajaxFormData = {
        receiver     : this.emailInput,
        messageTitle : this.titleInput,
        messageBody  : this.bodyTextArea,
        _csrf_token  : this.csrfToken,
      };

      csrfRequestPromise.then( (response) => {
        let csrfTokenResponseDto = CsrfTokenResponseDto.fromAxiosResponse(response);
        this.csrfToken           = csrfTokenResponseDto.csrToken;

        this.axios({
          method : "POST",
          url    : SymfonyRoutes.SEND_TEST_MAIL,
          data   : ajaxFormData,
        }).then( (response) => {
          // handle with some popover
        })
      });
    },
    /**
     * @description will return the csrf token which is required upon submitting the form (Internal Symfony Validation Logic)
     */
    getCsrfToken(){
      ...
      return promise;
    }
  },
  components: {
    CsrfTokenInputComponent
  }
}
</script>

长话短说

这就是您需要将道具传递给孩子的方式

    <CsrfTokenInputComponent :csrf-token="csrfToken"/>

喜欢这样

    <CsrfTokenInputComponent csrf-token="csrfToken"/>

即使我的 IDE 一直告诉我yeap i can navigate with that prop to child- vue 无法绑定它。

于 2020-12-13T12:24:33.150 回答
1

解决方案(测试正常)

在子组件中只使用props数据,不需要重新赋值propsdata否则会导致更新错误!

vue子组件props更新bug&解决方案

在此处输入图像描述

https://forum.vuejs.org/t/child-component-is-not-updated-when-parent-component-model-changes/18283?u=xgqfrms

于 2019-07-17T05:28:02.213 回答
0

问题不是如何用 props 传递数据,而是如何几乎同时做两件事。

我有一个用户帐户组件,可以编辑用户(带有用户 ID)和添加用户(不带 ID)。子组件显示用户<->公司分配的复选框,并且在保存用户帐户时需要用户 ID 来准备 API 调用。

重要的是在保存用户帐户之前显示子组件,以便可以在保存用户并获取 id 之前选择事物。所以它一开始没有用户ID:ID作为'null'传递给子组件。

它会在用户被存储并获取 id 时更新。但是此时,孩子需要很短的时间才能将新的 id 放入其模型中。如果您在子组件中调用依赖于刚刚更改的数据的函数,则该函数可能会在数据更新之前执行。

对于这种情况,nextTick() 是你的朋友。

import { nextTick } from 'vue';

...

saveAccount() {
      axios.post(URL, this.userModel).then((result)) {
        // our model gets an id when persisted
        this.userModel.id=result.data.id;
        nextTick( () => {
          this.$refs.childComponent.doSomething();
        });
      }
    }
于 2021-03-05T12:59:31.567 回答