Breeze.js EntityManager未检测到更改

时间:2013-03-03 13:01:28

标签: javascript mvvm knockout.js breeze

我的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上的实体。

有什么想法吗?

2 个答案:

答案 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缓存的任何实体中的状态更改。它只对转换到“已修改”状态感兴趣。检测到后,它会保存实体。

现在这对你来说可能太宽泛了。但您可以想象注册适合您应用程序特定需求的额外过滤逻辑。

SaveQueuing

与自动保存有关的新问题。无论您如何触发保存,此问题都很重要。

用户可以非常快速地点击。她必须以比处理它们更快的速度触发保存请求。 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事件中创建我的订阅

稍后尝试将您的订阅放入工作流程中。