检测Knockout视图模型的更改

时间:2012-05-16 16:26:08

标签: javascript knockout.js

当然这是一个非常容易回答的问题,但有一种简单的方法可以确定淘汰视图模型的任何属性是否已经改变?

9 个答案:

答案 0 :(得分:96)

使用扩展程序:

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()

答案 1 :(得分:48)

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

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;
}

(尚未测试过 - 但你应该明白这一点)

答案 2 :(得分:5)

考虑使用Knockout-Validation plug-in

它实现了以下内容:

  

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

     

yourProperty.originalValue - 因此您可以检查值是否真的发生了变化。

与其他验证的东西一起派上用场!

干杯

答案 3 :(得分:3)

您可以使用下面的插件:

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

例如,代码将允许您跟踪任何viewModel中的所有更改:

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

PS:截至目前,这不适用于嵌套在数组中的可订阅者,但是支持它的新版本正在进行中。

更新:示例代码已升级为与v1.2b一起使用,后者增加了对数组项和可订阅的可订阅属性的支持。

答案 4 :(得分:2)

我有同样的问题,我需要观察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和淘汰框架的新手,(只是我开始使用这个精彩的框架),所以如果我做错了,不要生我的气。( - :

希望这有帮助!

答案 5 :(得分:2)

我已经改编了@Brett Green代码并对其进行了扩展,以便我们可以使用AcceptChanges,将模型标记为不脏并且有更好的方法将模型标记为可跟踪。这是代码:

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

ko.track(viewModel);

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

答案 6 :(得分:1)

我通过在页面加载时拍摄视图模型的快照,然后将该快照与当前视图模型进行比较来完成此操作。我不关心什么属性改变了,只要有任何改变。

拍摄快照:

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

稍后比较:

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

答案 7 :(得分:1)

考虑如下视图模型

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.
  }

});

**注意:此计算的可观察函数也在第一次初始化视图模型时执行。 **

希望这有帮助! 干杯!!

答案 8 :(得分:0)

我喜欢布雷特·格林的解决方案。正如有人指出的那样,isDirty比较不适用于Date对象。我通过扩展如下的subscription方法解决了这个问题:

    observable.subscribe(function (newValue) {
            observable.isDirty(newValue != observable.originalValue);

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