如何让模板等待路由加载控制器依赖的数据?解决渲染数据未定义问题
解决Ember模板等待路由加载依赖数据的问题
这个问题在Ember开发里太常见了——异步数据加载和模板渲染的顺序冲突,导致未定义属性调用报错。咱们一步步拆解问题,再给出几个实用的解决方案:
为什么会出现这个问题?
你当前的代码里,路由的setupController里用了this.store.find,这是个异步操作(返回Promise)。代码执行顺序是:
- 路由触发
setupController,发起异步请求(步骤2) - 模板开始渲染,调用控制器的
thisIsIt计算属性(步骤1),此时underdata还没被设置(步骤3是异步回调里的代码,还没执行) - 异步请求完成,执行
then回调,设置underdata(步骤3)
因为异步回调是在同步代码之后执行的,所以模板渲染时underdata还是undefined,调用get("length")自然就报错了。
解决方案1:让路由等待数据加载完成再渲染模板(最规范的做法)
Ember的路由天生支持等待Promise完成后再渲染模板,你只需要把数据请求移到model钩子并返回Promise即可:
// 修改后的路由代码 model() { // 返回异步请求的Promise,路由会等待它resolved后再继续 return this.store.find('underdata', {id: 1}); }, setupController(controller, model) { // 此时model已经是加载完成的underdata数据了 controller.set("underdata", model); }
这样路由会等到model钩子的Promise完成后,才会渲染模板,模板渲染时underdata已经存在,计算属性就能正常获取length了。
解决方案2:给计算属性加安全判断(适合非必需数据场景)
如果不想让路由等待(比如页面有其他不需要该数据的部分要提前渲染),可以在计算属性里先判断underdata是否存在,再调用get:
// 修改后的控制器代码 thisIsIt: function() { const underdata = this.get("underdata"); // 如果underdata不存在,返回默认值(比如0或者false,根据你的业务需求) return underdata ? underdata.get("length") : 0; }.property('underdata.length') // 把依赖改成underdata.length,更精准触发重计算
另外,把计算属性的依赖从underdata改成underdata.length,这样当underdata加载完成、length更新时,计算属性会自动重新计算,不需要等到整个underdata对象变化。
解决方案3:在模板里加条件判断(灵活控制渲染时机)
你也可以直接在模板里先判断underdata是否存在,再渲染相关内容:
{{! 修改后的模板代码 }} {{#if underdata}} {{#if thisIsIt}}Hello{{/if}} {{/if}}
或者用and助手简化:
{{#if (and underdata thisIsIt)}}Hello{{/if}}
这样模板会等到underdata被设置后,才会去计算thisIsIt并渲染Hello,避免了未定义属性的调用。
内容的提问来源于stack exchange,提问作者Tom Smykowski
相关产品推荐
相关产品推荐

