【问题标题】:knockout computed not updating while change in view model being made from ajax从 ajax 更改视图模型时计算出的淘汰赛未更新
【发布时间】:2016-05-13 07:56:04
【问题描述】:

我对淘汰赛 Js 的了解有多少,计算值会根据视图模型进行更新,但在我的情况下,它并没有发生。所以基本上我有一个单选按钮,可以关闭和打开并更改数据库中的日期,ajax 调用返回并推送视图模型中的新日期,以便数据更改。

这就是总结。但我想要的是,在更新单选按钮时,我希望 html 的一部分根据单选按钮更改为活动或禁用。

首先是 HTML 代码。

<div class="col-sm-4">
  <p>
    <span data-bind="text : $data.basketStatusValue"></span>
  </p>
</div>
<div class="col-sm-4">
  <div class="on_off">
    <input type="checkbox"  data-bind="bootstrapSwitchOn: {
        tocall: $root.changeActiveBasketStatus
    }" />
  </div>
</div>

这是JS代码。

function MoneyInvestedViewModel(root /* root not needed */, money) {
    var self = this;
    self.ID = money.ID;
    self.ORIG_ID = money.ORIG_ID;
    self.Available = money.Available;
    self.basketStatusValue = ko.computed (function () {
        if (self.Available == '9999-12-01') {
            return "Active";
        } else {
            return "Disabled";
        }
    });
};

接下来是更新视图模型 moneyInvested 的代码。所以复选框可以打开或关闭。

self.changeActiveBasketStatus = function (bindingContext) {
    console.log(bindingContext);
    var Id = bindingContext.$data.ORIG_ID;
    var Available = bindingContext.$data.Available;
    if (Available == '9999-12-01') {
        $.ajax({
            type: 'POST',
            url: BASEURL + 'index.php/moneyexchange/changeBasketStatus/' + auth + "/" + Id + "/" + 1,
            contentType: 'application/json; charset=utf-8'
        })
        .done(function (newAvailableDate) {
            bindingContext.$data.Available = newAvailableDate;
        })
        .fail(function (jqXHR, textStatus, errorThrown) {
            self.errorMessage(errorThrown);
        })
        .always(function (data){  
        });
    } else {
        $.ajax({
            type: 'POST',
            url: BASEURL + 'index.php/moneyexchange/changeBasketStatus/' + auth + "/" + Id + "/" + 0,
            contentType: 'application/json; charset=utf-8'
        })
        .done(function (newAvailableDate) {
            bindingContext.$data.Available = newAvailableDate;
        })
        .fail(function (jqXHR, textStatus, errorThrown) {
            self.errorMessage(errorThrown);
        })
        .always(function (data) {
        });
    }
};

所以基本上问题是当所有这些更新完成时,计算出来的 self.basketStatusValue 没有得到更新。所以当我点击复选框时,它不显示活动,或关闭禁用,复选框工作正常,只有 html $data.basketStatusValue 没有通过计算函数更新。

如果需要,这里是复选框的代码。

(function ($) {
    ko.bindingHandlers.bootstrapSwitchOn = {
        init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
            var options = ko.utils.unwrapObservable(valueAccessor());
            var tocall = ko.utils.unwrapObservable(options.tocall);

            $elem = $(element);
            $(element).bootstrapSwitch();
            $(element).bootstrapSwitch('setState', bindingContext.$data.Available === '9999-12-01' ? true : false); // Set intial state
            $elem.on('switch-change', function (e, data) {
                tocall(bindingContext);
                // valueAccessor()(data.value);
            });
        },
        update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        }
    };
})(jQuery);

总而言之,我要做的就是让$data.basketStatusValue 在复选框打开或关闭时“激活”或“禁用”。

【问题讨论】:

    标签: ajax knockout.js


    【解决方案1】:

    你可以用来解决这个问题的一个肮脏的技巧是清空整个 observable 并用新数据推送它。但老实说,这不是正确的使用方式。我现在假设那是一个数组,但你也可以删除 observables。只需输入 observable 名称而不是 YourArray()。

    self.refresh = function(){
            var data = YourArray().slice(0);
            YourArray.removeAll(); 
            self.YourArray(data);
            };
    

    并将这个函数放在完成函数之后

    .done(function(newAvailableDate) {
       bindingContext.$data.Available = newAvailableDate;
      // here self.refresh();
     })
    

    【讨论】:

    • 这里的教训是,你不应该在其中写出带有“老实说这不是正确的使用方式”的答案。 OP 可能会复制/粘贴您的代码并使用它 - 要么是因为他们没有费心阅读您编写的文本,要么因为他们不在乎它是错误的。尽量不要树立坏榜样。
    【解决方案2】:

    您的绑定处理程序错误,让我们从它开始。

    应该:

    • init() 中的元素上设置引导开关
    • update()中的更改做出反应
    • 绑定到 observabe(不要使用回调函数)。在我们的例子中,observable 应该包含复选框状态,即truefalse
    • 适时妥善处理 Bootstrap 小部件。

    所以,这样会更好:

    (function ($) {
        ko.bindingHandlers.bootstrapSwitch = {
            init: function (element, valueAccessor) {
                var options = valueAccessor();
    
                // set up bootstrap in init()
                $(element).bootstrapSwitch().on('switch-change', function (e, data) {
                    options.value(data.value);
                });
    
                // see http://knockoutjs.com/documentation/custom-bindings-disposal.html
                ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
                    $(element).bootstrapSwitch("destroy");
                });
            },
            update: function(element, valueAccessor) {
                var options = valueAccessor();
    
                // react to change in update()
                $(element).bootstrapSwitch('setState', options.value());
            }
        };
    })(jQuery);
    

    接下来我们需要相应地设置视图模型。

    • Available 需要是可观察的。视图的变化取决于它。
    • 我们需要一个返回 truefalse 的 observable,具体取决于 Available
    • 我们需要一个返回 "Active""Disabled" 的 observable,这取决于它
    • 我们需要一个函数来更新服务器的变化(通过订阅)

    像这样:

    function MoneyInvestedViewModel(money) {
        var self = this;
        self.ID = money.ID;
        self.ORIG_ID = money.ORIG_ID;
        self.Available = ko.observable(money.Available);
        self.errorMessage = ko.observable();
    
        self.BasketStatus = ko.computed(function () {
            return self.Available() == '9999-12-01';
        });
        self.BasketStatusText = ko.computed(function () {
            return self.basketStatus() ? "Active" : "Disabled";
        });
    
        // BEWARE: this is not actually correct (cicular dependency)
        self.BasketStatus.subscribe(function () {
            $.ajax({
                type: 'POST',
                url: BASEURL + 'index.php/moneyexchange/changeBasketStatus/' + auth + "/" + self.ID + "/" + 1,
                contentType: 'application/json; charset=utf-8'
            })
            .done(function (newAvailableDate) {
                self.Available(newAvailableDate);
            })
            .fail(function (jqXHR, textStatus, errorThrown) {
                self.errorMessage(errorThrown);
            });
        };
    }
    

    注意:订阅正确的 observable 以使用正确的值更新服务器。从您的问题中不清楚服务器更新取决于什么值。

    现在将视图绑定到它很简单:

    <div class="col-sm-4">
      <p><span data-bind="text: BasketStatusText"></span></p>
    </div>
    <div class="col-sm-4">
      <div class="on_off">
        <input type="checkbox"  data-bind="bootstrapSwitch: {
            value: BasketStatus
        }" />
      </div>
    </div>
    

    【讨论】:

    • 感谢您更好地向我解释,我很感激! :D
    【解决方案3】:
    if(self.Available == '9999-12-01'){ return "Active"; }else{ return "Disabled";}
    

    这一行的问题在于它不查找任何可观察或计算的值,因此它不会导致计算的值永远被更新。

    你需要让 self.Available 一个 observable 然后做self.Available() 这就是计算的工作方式,当它们的任何可观察/计算的依赖关系发生变化时,它们会被重新计算。 Knockout 不会像您一样看到简单的属性更新。

    【讨论】:

    • 那么我怎样才能在我的代码上写呢?如果我有更好的书面解释会很好,因为我对它很陌生
    • 我试着让它像这样 self.Available = ko.observable(money.Available);然后让 self.Available() == '9999-12-10' 等但没有工作
    • 只创建一次可观察对象。你能发布你更新的代码吗?
    猜你喜欢
    • 2015-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-02
    相关资源
    最近更新 更多