我有一个父/子组件设置,其中父级正在从服务器加载数据并通过props将其传递给子级。在孩子中,我想实例化一个jQuery日历,其中包含从父母那里收到的一些数据。
为了在设置日历之前等待数据,我在父母中广播了一个事件,我在孩子中设置了事件监听器。
听众正在被解雇,但是如果我this.$log('theProp')
,那么它就是未定义的。但是,如果我使用VueJs devtools检查组件,那么父/子关系就在那里,而孩子在此期间已收到道具。
道具在孩子身上被定义为动态道具:the-prop="theProp"
。由于孩子最终收到道具,我假设我的设置是正确的,但似乎有某种延迟。父母在ajax调用的return函数中设置了props,并再次:它正在工作,只是稍有延迟。
我还尝试在孩子的道具上注册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()
}
}
答案 0 :(得分:18)
据我所知,您不需要事件将数据从父级传递给子级。
您需要的只是,在子组件中: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;
});
}
现在,您不需要任何其他内容将数据传递给孩子。它已经在孩子身上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
或许您的问题与change detection caveat
有关也许你正在对象中创建一个新属性......
vm.someObject.someProperty = someValue
......什么时候应该......
vm.$set('someObject.someProperty', someValue)
......以及其他“警告”。
更新3
在VueJS 2中,您不仅限于模板。您可以使用render function并编写所需的最复杂的渲染逻辑。
更新4 (关于OP的编辑2 )
也许您可以删除ready
并使用immediate
选项,因此您的初始化位于一个位置:
watch: {
someData: {
handler: function (someData) {
// check someData and eventually call
this.initCalendar();
},
immediate: true
}
}
答案 1 :(得分:2)
这是因为 Vue父级和子级生命周期挂钩 中的棘手行为。
通常是父组件 fire created()
钩子,然后是mount()
钩子,但是当存在子组件时,情况并非完全如此:父 fires created()
,然后他的孩子先解雇created()
,然后解雇mount()
,只有在加载孩子的mount()
钩子之后,父对象才按照here的说明加载他的mount()
。这就是为什么未加载子组件中的prop的原因。
使用mounted()
钩子代替created()
答案 2 :(得分:0)
在仅使用
props
数据的子组件中,无需将props
的值重新分配给data
,这将导致更新错误!
答案 3 :(得分:0)
Vue 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也无法将其绑定。
答案 4 :(得分: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();
});
}
}