2016-09-26 103 views
7

我有一个父/子组件设置,父母从服务器加载数据并通过道具传递给儿童。在这个孩子中,我想用父母收到的一些数据实例化一个jQuery日历。VueJs子组件道具没有立即更新

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

监听器正在被解雇,但如果我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() 
    } 
} 
+1

嗨,你可以提供一个简单的代码示例来重现该问题? – Nora

+2

如果你正在'监视'一个变量,并且'watch'内的值是未定义的,事实上它将它设置为undefined。 –

+0

感谢您的意见。用我发现的问题更新了我的问题。 – Chris

回答

3

据我所知,你不应该需要事件从父数据传递给孩子。

所有你需要的是,在子组件: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 
    } 
} 
+0

我通过道具传递数据,我的问题是关于对接收数据更改作出反应,因为我必须根据它进行初始化。计算属性不是去为那种事情imho的方式。 – Chris

+0

我已经更新了我的答案,包括其他建议。然而,imho计算的属性对于这种情况非常合适。您可以基于'someData'创建'isReady'或'isLoaded'计算属性。事实上,如果你需要对数据变化做出反应,计算出来的属性**就是要走的路。 –

+0

使用模板时,是的。在一个方法中,如果我检查'if(this.isLoaded)'并且当时没有加载,我想我有机会实例化日历。因此,我必须观察数据以获得第二次更新中所述的更改。效果很好。 – Chris