Detecting change to Knockout view model

后端 未结 9 2269
情歌与酒
情歌与酒 2020-12-04 07:38

Sure this is a very easy question to answer but is there an easy way to determine if any property of a knockout view model has changed?

相关标签:
9条回答
  • 2020-12-04 07:48

    I had the same problem, i needed to observe any change on the viewModel, in order to send the data back to the server, If anyone still intersted, i did some research and this is the best solution iv'e managed to assemble:

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

    in order to use this 'global observer':

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

    Note that the callback given (in this case 'Ajax_Submit') will be fired on ANY change that occurs on the view model, so i think it's really recommended to make some sort of delay mechanism to send the entity only when the user finished to edit the properties:

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

    I'm new to JavaScript and the knockout framework, (only yestarday i started to work with this wonderfull framework), so don't get mad at me if i did something wrong.. (-:

    Hope this helps!

    0 讨论(0)
  • 2020-12-04 07:48

    I like Brett Green's solution. As someone pointed out, the isDirty comparison doesn't work with Date objects. I solved it by extending the subscribe method like this:

        observable.subscribe(function (newValue) {
                observable.isDirty(newValue != observable.originalValue);
    
                if (newValue instanceof Date) {
                    observable.isDirty(newValue.getTime() != observable.originalValue.getTime());
                }
            });
    
    0 讨论(0)
  • 2020-12-04 07:53

    Consider a view model as follows

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

    After you Bind your Data you can store the state using ko.toJS(myViewModel) function.

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

    You can declare a variable inside your view model as a computed observable like

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

    This computed function will be entered when there is change to any of the other observables inside the view model.

    Then you can compare the two view model states as:

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

    });

    **Note: This computed observable function is also executed the first time when the view model is initialized. **

    Hope this helps ! Cheers!!

    0 讨论(0)
  • 2020-12-04 07:54

    You might use the plugin below for this:

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

    The code for example will allow you to keep track of all changes within any viewModel:

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

    PS: As of now this will not work with subscribables nested within an array but a new version that supports it is on the way.

    Update: The sample code was upgraded to work with v1.2b which adds support for array items and subscribable-in-subscribable properties.

    0 讨论(0)
  • 2020-12-04 08:02

    You can subscribe to the properties that you want to monitor:

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

    This will alert when personName changes.

    Ok, so you want to know when anything changes in your model...

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

    (haven't tested it - but you should get the idea)

    0 讨论(0)
  • 2020-12-04 08:02

    I did this by taking a snapshot of the view model when the page loads, and then later comparing that snapshot to the current view model. I didn't care what properties changed, only if any changed.

    Take a snapshot:

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

    Compare later:

    if(originalViewModel != JSON.stringify(ko.toJS(viewModel))){
        // Something has changed, but we don't know what
    }
    
    0 讨论(0)
提交回复
热议问题