我的viewmodel如下,使用knockout.js绑定到我的页面。 Episodes是一个实体数组,每个实体都有一个布尔属性'ListenedTo'绑定到我页面上的复选框。
function episodesViewModel() {
this.episodes = ko.observableArray();
this.sync = function () {
repository.populateEpisodes(this);
}
this.save = function () {
repository.saveChanges();
}
//this.episodes.subscribe(function (episodes) {
// ko.utils.arrayForEach(episodes, function (episode) {
// episode.ListenedTo.subscribe(function () {
// window.repository.saveChanges();
// });
// });
//});
// perform an initial sync
this.sync();
}
存储库对象只是底层Breeze调用的包装器。
如果我将save()绑定到按钮单击,一切都按预期工作,微风看到有更改并回调到服务器。如果我取消注释创建订阅ListenedTo更改的行(并保存而无需单击按钮),则会发生奇怪的事情。
页面加载并填充后,第一次单击该复选框会自动按预期调用breeze saveChanges()。即使viewmodel中的值符合预期并且与复选框匹配,breeze hasChanges()也会返回false并且不会进行任何服务器调用。再次单击它,hasChanges现在为true,并进行服务器调用!?这就像Breeze一样落后,并没有将更改注册到viewmodel上的实体。
有什么想法吗?
答案 0 :(得分:4)
哦,我的。您的代码遇到大麻烦!在深入研究细节之前,跳出页面的可能性是,每次数组更改时,您可能会重复向episodes
数组中的每一集添加订阅。 YIKES!
在我继续讨论我的疑虑之前,我想承认你在听众解雇时与manager.hasChanges
的价值之间存在不一致的具体经历。那是真的。 KO订阅在EntityManager
听到变更之前就开始了......这就是为什么报告错误的原因。有关变更的消息尚未到来让经理听到。
我也注意到了这一点。事实上,如果您查看TodoSample中的 viewModel.js ,您会看到我们通过添加setTimeout
来解决这个问题,让Breeze有机会听到改变:
// listen for changes with Breeze PropertyChanged event item.entityAspect.propertyChanged.subscribe(function () { if (suspendItemSave) { return; } // give EntityManager time to hear the change setTimeout(saveIfModified, 0); function saveIfModified() { if (item.entityAspect.entityState.isModified()) { dataservice.saveChanges(); } } });
我希望我知道另一种方式。这只是KO和Breeze之间的时间问题。我们还没弄清楚如何精细化它。不确定我们能不能。
让我们回来吧。当episode
属性更改为时,listenedTo
是否应该保存,无论它出现在哪个视图中?如果您的答案为“是”,那么您有一个应用程序业务规则确实属于模型,你真的想要听一些情节改变。
但如果答案是“否”......如果保存应该由“复选框”触发,而不是对象的更改,那么这是一个UI规则...一个查看规则...而你应该使用KO来收听复选框,而不是收集剧集属性。
让我们继续第一种情况并声明您的应用程序规则如下:“总是在listenedTo
更改时保存,无论是什么原因导致其更改”。
我知道在TodoSample中我们演示了一种直接监听实体以更改其属性(其任何属性)的方法。这很强大而且很直观。
我开始相信这不是最安全的方式。在具有共享同一实体的多个ViewModel的应用程序中,它可能会发生内存泄漏。这个“Todo”应用程序只有一个屏幕,所以这不是一个真正的担心。但在一个更大的应用程序......我会担心。
所以我建议不要听取Episode
属性的更改。而是收听EntityManager
!请查看最近发布的Breeze SPA template中 datacontext.js 中的此片段。
function configureManagerToSaveModifiedItemImmediately() { manager.entityChanged.subscribe(entityStateChanged); function entityStateChanged(args) { if (args.entityAction === breeze.EntityAction.EntityStateChange) { var entity = args.entity; if (entity.entityAspect.entityState.isModified()) { saveEntity(entity); } } } }
请注意它如何侦听EntityManager缓存的任何实体中的状态更改。它只对转换到“已修改”状态感兴趣。检测到后,它会保存实体。
现在这对你来说可能太宽泛了。但您可以想象注册适合您应用程序特定需求的额外过滤逻辑。
与自动保存有关的新问题。无论您如何触发保存,此问题都很重要。
用户可以非常快速地点击。她必须以比处理它们更快的速度触发保存请求。 Breeze(默认情况下)在等待服务器从挂起的保存操作返回结果时,不会再让EntityManager
保存。它会引发异常。
它必须等待,因为它无法将实体的状态从更改更改为未更改,直到它知道保存是否成功。如果保存失败,您希望将实体保持在“未保存”状态。
查看manager.enableSaveQueuing(true)
的 datacontext.js 的顶部。这是不微风的原生特征。这是插件的一个功能, Scripts / breeze.savequeuing.js 你想要加载这个插件。在撰写Breeze SPA template
你可能认为这个微风很复杂。实际上,并不是Breeze引入了复杂功能。当实体改变其添加并发症的状态时,您希望触发保存。
我并不是说你错了。我说这种方法是Breeze提供的机会,它在实施过程中需要注意。
如果没有微风,你就会有时间跟踪实体状态。因此,您唯一真正安全的选择是根据对复选框的更改来触发保存...在这种情况下,工作量并不比KO绑定到复选框更复杂。
嗯......好吧......由于你绊倒的Breeze / KO计时问题需要setTimeout
开局,这有点复杂。但我希望你明白我的意思。
答案 1 :(得分:0)
很难知道没有更多信息(例如,使用了什么其他框架,什么触发VM实例化代码,哪里是KO.ApplyBind) 您的视图是在创建VM时正确构建的吗?
我现在正在使用Durandal,它首先创建VM,然后创建视图。尝试在Durandal的激活事件期间(或者像实例化代码更早)创建附加订阅,导致KO view / viewModel订阅被丢弃,因为它们被发现无效。我不得不在Durandal后来的viewAttached事件中创建我的订阅
稍后尝试将您的订阅放入工作流程中。