How might I cancel a change to an observable array w/ Knockout 3.0?
Asked Answered
B

2

7

I have a change occurring to an array. I am using Sanderson's latest array subscription method to catch the add/delete change. In this subscription is where I intend to bundle and send my request over the wire. If the request fails for any reason I want to be able to cancel any possible changes to the collection. I have verified that this subscription is hit before the change propagates so I assume there would be a way to say "STOP DON'T DO IT" however I can't figure out how.

As my example...

self.SourceData = ko.observableArray(data);
self.SourceData.subscribe(function(changes) {
    var isAllGood = true;
    ko.utils.arrayForEach(changes, function(ch) {
        if (ch.value == doesNotMeetMyCondition) isAllGood = false;
    });
    if (!isAllGood) <STOP DON'T DO IT>
}, null, 'arrayChange');

When inspecting 'this' I do see the ko.subscription object with the standard [callback, dispose, disposeCallback, target] but nothing seems to amount to STOP DON'T DO IT.

Any thoughts would be quite helpful. Thanks.

Becket answered 19/12, 2013 at 20:47 Comment(1)
You subscribe method called, when your observableArray has changed (added or deleted items), yes? If it is true, maybe you should to add two methods add/delete, and make validation inside them?Micmac
M
4

There is not currently a way to stop the change from going through.

You could potentially subscribe to the beforeChange event and cache a copy of the array before the change. Something like:

self.SourceData.subscribe(function(value) {
    /store copy, in case it needs to be restored
    self.beforeSourceData = value && value.slice();
}, null, "beforeChange");
Meany answered 19/12, 2013 at 21:13 Comment(1)
Yeah, which isn't far from how I am currently handling it. I did like the idea of keeping my requests to the server directly attached to the change of the object(s) I'm sending over as they change. Thanks for taking a moment to share the idea.Becket
E
2

You can use another approach - create extender to validate your array on change. I've created simplified example that checks simple array of integers for specified upper boundary. Fiddle: http://jsfiddle.net/pkutakov/n2APg/1/ Code:

ko.extenders.checkValue=function (value, condition){
    var result=ko.computed({
        read: value,
        write: function (newValue){
            var current=value();
            var canSave=true;
            ko.utils.arrayForEach(newValue, function(ch) {
                if (ch>condition)
                    canSave=false;
            });
            if (canSave)
            {
                value(newValue);
                value.notifySubscribers(newValue);
            }
            else
                value.notifySubscribers(value());
        }
    }).extend({ notify: 'always'});
    result(value());

    //return the new computed observable
    return result;
}

function AppViewModel(data)
{
    self=this;
    self.Data=ko.observableArray(data).extend({ checkValue: 100});
}

var mydata=[10, 11];
ko.applyBindings(new AppViewModel(mydata));
Eufemiaeugen answered 20/12, 2013 at 8:6 Comment(3)
Very nice use of the extender to alter the input. I did a little addition of the same subscribe logic to test what order events occurred. I found that the extender does, in fact, occur before the subscription however the 'changes' returned to the subscription were now void of the 3.0 change inspection method. jsfiddle.net/n2APg/4Becket
It just adds one line: myVM.Data.push(13);Ibbison
But it fails with this error: Error: Object doesn't support property or method 'push'. When I take off the "extend" part of the observableArray, it works correctly. Why does this solution break my ability to programmatically push an element? Is there any way to get this functionality back?Ibbison

© 2022 - 2024 — McMap. All rights reserved.