等待Firebase .set().then()完成后再执行$digest()
嘿,我懂你踩的这个坑!核心问题其实很明确:Firebase返回的是原生ES6 Promise,而非Angular封装的$q Promise——原生Promise的回调会运行在Angular的变更检测周期之外,所以哪怕你调用this.resolve()更新了父控制器的模型,Angular也感知不到这些变化,自然不会触发$digest()。
你之前尝试用$q让save()返回Promise但没效果,大概率是没把Firebase的Promise真正纳入Angular的上下文里。下面给你几个实用的解决方案:
方案1:手动用$scope.$apply触发变更检测
这是最直接的方式,把this.resolve()的调用包裹在$scope.$apply()中,强制Angular执行变更检测:
// 记得在控制器里注入$scope this.save = () => { const chorePath = `chores/${member.$id}/...`; firebaseRef.set(this.myData).then(() => { $scope.$apply(() => { this.resolve(); }); }); };
$scope.$apply()会把回调内的代码纳入Angular上下文,执行后自动触发$digest(),模型变更就能被正常检测到了。
方案2:用$q把Firebase Promise转换成Angular Promise
通过$q.resolve()将原生Promise包装成Angular的$q Promise,这样它的then回调会自动运行在Angular的变更检测周期内:
// 注入$q到控制器中 this.save = () => { const chorePath = `chores/${member.$id}/...`; return $q.resolve(firebaseRef.set(this.myData)).then(this.resolve); };
这种写法下,then里的this.resolve()执行时,Angular会自动处理变更检测,无需手动调用$apply。
方案3:用$timeout间接触发变更检测
$timeout的回调会自动触发$digest(),哪怕设置延迟为0也生效。可以用它来包裹this.resolve():
// 注入$timeout到控制器中 this.save = () => { const chorePath = `chores/${member.$id}/...`; firebaseRef.set(this.myData).then(() => { $timeout(() => { this.resolve(); }); }); };
这个方案和$apply效果类似,但写法更简洁,还能避免手动调用$apply可能出现的“digest already in progress”错误。
为什么之前用$q没效果?
如果你只是让save()返回一个新的$q Promise,但没把Firebase的Promise关联进去,那回调还是会跑在Angular上下文之外。必须用$q.resolve()包裹Firebase的原生Promise,才能让后续的then回调进入Angular的变更检测流程。
内容的提问来源于stack exchange,提问作者sonicblis

