【问题标题】:knockout.js how to set selected option after confirm() cancelled within a ko.computed write在ko.computed写入中取消confirm()后,knockout.js如何设置选定的选项
【发布时间】:2018-06-01 00:44:48
【问题描述】:

我有一个带有选项和默认文本的选择器元素:

self._selected = ko.observable();
self.option = ko.computed({
    read:function(){
        return self._selected;
    },
    write: function(data){
        if(data){
            if(confirm('are you sure?')){
                self._selected(data);
            }else{
                //reset
            }
        }
    }
});

<select data-bind="options: options, value:option, optionsCaption: 'choose ...'"></select>

这个问题:

  • 选择“一个”
  • 在确认点击取消
  • 所选选项“一个”仍处于焦点中

应该是“选择...”

jsbin here,仅在 chrome 上测试过

【问题讨论】:

    标签: javascript knockout.js knockout-3.0 computed-observable


    【解决方案1】:

    这里有一个不对称:

    当您更改选择框的值时,DOM 会立即更新并随后进行剔除(当然,剔除取决于 DOM 更改事件)。因此,当您的代码询问“您确定吗?”时,DOM 已经有了新值。

    现在,当您将该值写入绑定到value: 的可观察对象时,视图模型的状态不会改变。并且敲除仅在可观察对象发生更改时更新 DOM。所以 DOM 停留在选定的值,而你的 viewmodel 中的绑定值是不同的。


    最简单的方法是将旧值保存在变量中,始终将新值写入 observable,如果用户单击“否”,则只需恢复旧值。这样一来,不对称性就被打破了,DOM 和 viewmodel 保持同步。

    var AppData = function(params) {
        var self = {};
        var selected = ko.observable();
        
        self.options = ko.observableArray(params.options);  
        self.option = ko.computed({
            read: selected,
            write: function(value) {
                var oldValue = selected();
                selected(value);
                if (value !== oldValue && !confirm('are you sure?')) {
                    selected(oldValue);
                }
            }
        });
      
        return self;
    };
    
    // ----------------------------------------------------------------------
    ko.applyBindings(new AppData({
      options: ['one','two','three']
    }));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
    
    <select data-bind="options: options, value: option, optionsCaption: 'Select...'"></select>
    
    <hr>
    <pre data-bind="text: ko.toJSON($root, null, 2)"></pre>

    这是要求值更改确认的淘汰赛扩展程序的完美候选者。这样我们就可以将它重新用于不同的 observables 并保持 viewmodel 干净。

    ko.extenders.confirmChange = function (target, message) {
        return ko.pureComputed({
            read: target,
            write: function(newValue) {
                var oldValue = target();
                target(newValue);
                if (newValue !== oldValue && !confirm(message)){
                    target(oldValue);
                }
            }
        });
    };
    
    // ----------------------------------------------------------------------
    var AppData = function(params) {
        var self = this;
        
        self.options = ko.observableArray(params.options);  
        self.option = ko.observable().extend({confirmChange: 'are you sure?'});
    };
    
    // ----------------------------------------------------------------------
    ko.applyBindings(new AppData({
      options: ['one','two','three']
    }));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
    
    <select data-bind="options: options, value: option, optionsCaption: 'Select...'"></select>
    
    <hr>
    <pre data-bind="text: ko.toJSON($root, null, 2)"></pre>

    【讨论】:

    【解决方案2】:

    问题在于底层变量的值没有改变,所以没有事件告诉 Knockout 它的value 与视图模型不同步。

    对于普通的 observable,您可以调用 valueHasMutated 来指示发生了一些隐匿的变化,但计算似乎没有。但他们确实有notifySubscribers。其实你的例子很像this example in the docs

    这是一个工作示例:

    function vm() {
      const self = {};
    
      self.options = ko.observableArray(['one', 'two', 'three']);
      self._selected = ko.observable();
      self.option = ko.pureComputed({
        read: self._selected,
        write: function(data) {
          if (data) {
            if (confirm('are you sure?')) {
              self._selected(data);
            } else {
              self.option.notifySubscribers(self._selected());
            }
          }
        }
      });
    
      return self;
    }
    
    ko.applyBindings(vm());
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
    <select data-bind="options: options, value:option, optionsCaption: 'choose ...'"></select>
    <div data-bind="text:_selected"></div>
    <div data-bind="text:option"></div>

    【讨论】:

    • 奇怪,当我尝试使用.notifySubscribers() 时什么也没做。
    • 你是在option 还是_selected 上打电话的?
    • 啊,我不应该在_selected 上调用它。当然,该值没有订阅者。
    猜你喜欢
    • 2020-03-01
    • 2015-03-22
    • 1970-01-01
    • 2012-09-24
    • 2013-10-10
    • 2017-06-29
    • 2014-02-18
    • 2011-08-22
    • 2015-09-23
    相关资源
    最近更新 更多