【问题标题】:Detecting change to Knockout view model检测对 Knockout 视图模型的更改
【发布时间】:2012-05-24 06:16:54
【问题描述】:

当然,这是一个很容易回答的问题,但是有没有一种简单的方法可以确定淘汰视图模型的任何属性是否发生了变化?

【问题讨论】:

标签: javascript knockout.js


【解决方案1】:

您可以订阅要监控的属性:

myViewModel.personName.subscribe(function(newValue) {
    alert("The person's new name is " + newValue); 
});

这将在 personName 更改时发出警报。

好的,所以您想知道模型何时发生任何变化...

var viewModel = … // define your viewModel

var changeLog = new Array();  

function catchChanges(property, value){
    changeLog.push({property: property, value: value});
    viewModel.isDirty = true;
}

function initialiseViewModel()
{
    // loop through all the properties in the model
    for (var property in viewModel) {

        if (viewModel.hasOwnProperty(property)) { 

            // if they're observable
            if(viewModel[property].subscribe){

                // subscribe to changes
                viewModel[property].subscribe(function(value) {
                    catchChanges(property, value);
                });
            }
        }
    }
    viewModel.isDirty = false;
}

function resetViewModel() {
    changeLog = new Array();  
    viewModel.isDirty = false;
}

(还没有测试过 - 但你应该明白)

【讨论】:

  • 感谢您的回复 - 是的,正在寻找可以检测任何属性的任何变化的东西,最好使用结果属性 - 一些方法?
  • 好的 - 刚到家 - 我会为你整理一段通用代码 - 我猜你想要某种日志记录或 Model.IsDirty 解决方案?
  • 这就是我的想法,是的,再次感谢您的帮助。
  • 很容易理解的答案
【解决方案2】:

使用扩展器:

ko.extenders.trackChange = function (target, track) {
    if (track) {
        target.isDirty = ko.observable(false);
        target.originalValue = target();
        target.setOriginalValue = function(startingValue) {
            target.originalValue = startingValue; 
        };
        target.subscribe(function (newValue) {
            // use != not !== so numbers will equate naturally
            target.isDirty(newValue != target.originalValue);
        });
    }
    return target;
};

然后:

self.MyProperty= ko.observable("Property Value").extend({ trackChange: true });

现在您可以像这样检查:

self.MyProperty.isDirty()

您还可以编写一些通用的 viewModel 遍历,看看是否有任何变化:

self.isDirty = ko.computed(function () {
    for (key in self) {
        if (self.hasOwnProperty(key) && ko.isObservable(self[key]) && typeof self[key].isDirty === 'function' && self[key].isDirty()) {
            return true;
        }
    }
});

...然后只检查 viewModel 级别

self.isDirty()

【讨论】:

  • 我建议改用target.isDirty(!(newValue >= target.originalValue && newValue <= target.originalValue));!= 不适用于 Date 对象。
  • 感谢这个示例 - 我一直在尝试寻找一种方法来进行更改跟踪,以便可以将原始值和更改后的值显示给用户。如果有人正在寻找相同的东西,这里有一个小提琴:jsfiddle.net/dKk9P
  • 如果你还没有财产的价值怎么办?
  • @TDaver 似乎无法将 null 值用作原始值并变为 0。似乎只有 0 是问题所在。我实施的快速修复是空检查: if (newValue === null && target.originalValue !== null) target.isDirty(true); else if (newValue !== null && target.originalValue === null) target.isDirty(true);否则 target.isDirty(!(newValue >= target.originalValue && newValue
  • 优秀的解决方案。如果您还需要在事件发生后重置标志(例如,如果用户“保存”数据),您还需要: target.resetDirty= function () { var value = target();目标.setOriginalValue(值); target.notifySubscribers(值); };
【解决方案3】:

我遇到了同样的问题,我需要观察 viewModel 上的任何变化,以便将数据发送回服务器, 如果有人仍然感兴趣,我做了一些研究,这是我设法组装的最佳解决方案:

function GlobalObserver(viewModel, callback) {  
    var self = this;
    viewModel.allChangesObserver = ko.computed(function() {
        self.viewModelRaw = ko.mapping.toJS(viewModel);
    });
    viewModel.allChangesObserver.subscribe(function() {
        callback(self.viewModelRaw);
    });
    self.dispose = function() {
        if (viewModel.allChangesObserver)
            viewModel.allChangesObserver.dispose();
        delete viewModel.allChangesObserver;
    };
};

为了使用这个“全球观察者”:

function updateEntireViewModel() {
    var rawViewModel = Ajax_GetItemEntity(); //fetch the json object..    
    //enter validation code here, to ensure entity is correct.
    if (koGlobalObserver)
        koGlobalObserver.dispose(); //If already observing the older ViewModel, stop doing that!
    var viewModel = ko.mapping.fromJS(rawViewModel);        
    koGlobalObserver = new GlobalObserver(viewModel, Ajax_Submit);
    ko.applyBindings(viewModel [ ,optional dom element]);   
}

请注意,给定的回调(在本例中为“Ajax_Submit”)将在视图模型上发生的任何更改时触发,因此我认为确实建议使用某种延迟机制仅在用户发送实体时完成编辑属性:

var _entitiesUpdateTimers = {};

function Ajax_Submit(entity) { 
    var key = entity.ID; //or whatever uniquely related to the current view model..
    if (typeof _entitiesUpdateTimers[key] !== 'undefined')
        clearTimeout(_entitiesUpdateTimers[key]);    
    _entitiesUpdateTimers[key] = 
        setTimeout(function() { SendEntityFunction(entity); }, 500);           
}

我是 JavaScript 和淘汰框架的新手,(直到昨天我才开始使用这个美妙的框架),所以如果我做错了什么,请不要生我的气.. (-:

希望这会有所帮助!

【讨论】:

  • 有趣的是,您正在使用淘汰赛来获得更快的使用体验。然后在您提交给服务器的客户端上的每一次更改上:)您最好使用常规绑定,更简单,更容易;)
【解决方案4】:

您可以使用以下插件:

https://github.com/ZiadJ/knockoutjs-reactor

例如,该代码将允许您跟踪任何视图模型中的所有更改:

ko.watch(someViewModel, { depth: -1 }, function(parents, child) { 
    alert('New value is: ' + child());
});

PS:到目前为止,这不适用于嵌套在数组中的可订阅,但支持它的新版本正在开发中。

更新:示例代码已升级为使用 v1.2b,它增加了对数组项和 subscribable-in-subscribable 属性的支持。

【讨论】:

  • 嘿,只是想知道......你有没有开始构建数组功能?
  • 我对 knockout.reactor.latest.js 做了一些快速更改,希望这能解决阵列问题,但我还没有时间测试它。因此,请随时自行检查,如果您仍有问题,请告诉我。
  • 太棒了,我实际上一直在研究类似的功能,并且很快就会在博客上介绍它,所以也许我们可以比较一下。
  • 最新的代码现在应该可以使用了。我很想将它作为一个稳定版本发布,但我宁愿更加谨慎而不是更少,先等待一些反馈。
  • 如何获取更改的属性的名称?在 FireBug 中,params 为空 [],但是,modifiedProperty 具有新值。我只是不知道哪个属性发生了变化。
【解决方案5】:

我已经修改了@Brett Green 代码并对其进行了扩展,以便我们可以拥有 AcceptChanges,将模型标记为不脏,并以更好的方式将模型标记为可跟踪。代码如下:

var viewModel = {
    name: ko.observable()   
};

ko.track(viewModel);

http://jsfiddle.net/david_freire/3HZEu/2/

【讨论】:

  • 小提琴死了。
  • 一定是临时的。
【解决方案6】:

我这样做是通过在页面加载时拍摄视图模型的快照,然后将该快照与当前视图模型进行比较。我不在乎哪些属性发生了变化,只要有变化。

拍摄快照:

var originalViewModel = JSON.stringify(ko.toJS(viewModel));

稍后比较:

if(originalViewModel != JSON.stringify(ko.toJS(viewModel))){
    // Something has changed, but we don't know what
}

【讨论】:

  • 供将来参考,JSON.stringify(ko.toJS(VM))ko.toJSON(vm) 相同。
【解决方案7】:

考虑如下视图模型

function myViewModel(){
    var that = this;
    that.Name = ko.observable();
    that.OldState = ko.observable();
    that.NewState = ko.observable();

    that.dirtyCalcultions - ko.computed(function(){
    // Code to execute when state of an observable changes.
});
}

绑定数据后,您可以使用 ko.toJS(myViewModel) 函数存储状态。

myViewModel.Name("test");
myViewModel.OldState(ko.toJS(myViewModel));

您可以在视图模型中将变量声明为计算的 observable,例如

that.dirtyCalculations = ko.computed(function () {});

当视图模型中的任何其他可观察对象发生变化时,将输入此计算函数。

然后您可以将两个视图模型状态比较为:

that.dirtyCalculations = ko.computed(function () {
  that.NewState(that);

  //Compare old state to new state
  if(that.OldState().Name == that.NewState().Name()){
       // View model states are same.
  }
  else{
      // View model states are different.
  }

});

**注意:这个计算出来的 observable 函数也会在视图模型初始化的时候执行。 **

希望这会有所帮助! 干杯!!

【讨论】:

    【解决方案8】:

    考虑使用Knockout-Validation plug-in

    它实现了以下功能:

    yourProperty.isModified() - 检查用户是否修改了值。

    yourProperty.originalValue - 这样您就可以检查该值是否真的改变了。

    以及其他派上用场的验证内容!

    干杯

    【讨论】:

      【解决方案9】:

      我喜欢 Brett Green 的解决方案。正如有人指出的那样, isDirty 比较不适用于 Date 对象。我通过像这样扩展订阅方法来解决它:

          observable.subscribe(function (newValue) {
                  observable.isDirty(newValue != observable.originalValue);
      
                  if (newValue instanceof Date) {
                      observable.isDirty(newValue.getTime() != observable.originalValue.getTime());
                  }
              });
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-01-03
        • 1970-01-01
        • 2015-11-01
        • 1970-01-01
        • 2017-04-11
        • 1970-01-01
        • 2016-11-10
        • 2017-05-04
        相关资源
        最近更新 更多