javascript - 为什么设置 optionsValue 会中断 Knockout 更新?

标签 javascript html mvvm knockout.js

我一直在浏览 Knockout 教程,当我在玩一个教程时,我感到困惑。这是我的 HTML:

<h2>Your seat reservations</h2>

<table>
    <thead><tr>
        <th>Passenger name</th><th>Meal</th><th>Surcharge</th>
    </tr></thead>
    <tbody data-bind="foreach: seats">
        <tr>
            <td><input data-bind="value: name" /></td>
            <td><select data-bind="options: $root.availableMeals, optionsValue: 'mealVal', optionsText: 'mealName', value: meal"></select></td>
            <td data-bind="text: formattedPrice"></td>
        </tr>    
    </tbody>
</table>

<button data-bind="click: addSeat">Reserve another seat</button>

...这是我的 JavaScript:

// Class to represent a row in the seat reservations grid
function SeatReservation(name, initialMeal) {
    var self = this;
    self.name = name;
    self.meal = ko.observable(initialMeal);

    self.formattedPrice = ko.computed(function() {
        var price = self.meal().price;
        return price ? "$" + price.toFixed(2) : "None";        
    });
}

// Overall viewmodel for this screen, along with initial state
function ReservationsViewModel() {
    var self = this;

    // Non-editable catalog data - would come from the server
    self.availableMeals = [
        { mealVal: "STD", mealName: "Standard (sandwich)", price: 0 },
        { mealVal: "PRM", mealName: "Premium (lobster)", price: 34.95 },
        { mealVal: "ULT", mealName: "Ultimate (whole zebra)", price: 290 }
    ];    

    // Editable data
    self.seats = ko.observableArray([
        new SeatReservation("Steve", self.availableMeals[0]),
        new SeatReservation("Bert", self.availableMeals[0])
    ]);

    // Operations
    self.addSeat = function() {
        self.seats.push(new SeatReservation("", self.availableMeals[0]));
    }
}

ko.applyBindings(new ReservationsViewModel());

当我运行此示例并从下拉菜单中为乘客选择不同的“膳食”时,“附加费”值更新。这样做的原因似乎是我将 optionsValue: 'mealVal' 添加到 selectdata-bind 属性中,当我删除也就是说,当选择新的下拉选项时,“附加费”确实会更新。但为什么添加 optionsValue 会中断更新?所做的只是设置 select 列表的选项 value 属性,这对于表单提交非常有用 - 我不明白为什么它应该阻止 Knockout 自动更新。

更新:经过进一步调查,我发现 formattedPrice fn 仍在被调用,但是 self.meal() 是现在解析为值字符串,例如 PRM 而不是整个 meal 对象。但这是为什么呢?文档说 optionsValue 在 HTML 中设置了 value 属性,但没有说明任何关于更改 View 模型行为的内容。

我认为发生的事情是,当您指定 options: $root.availableMeals,但不指定 optionsValue 时,Knockout 会神奇地确定列表中的选择您在更改选择时所做的,并允许您从 availableMeals 访问 object 而不仅仅是放入 value 的字符串值> 属性。这似乎没有很好的记录。

最佳答案

我认为您了解发生了什么以及为什么它会破坏您的代码,但仍在寻找关于何时实际需要使用 optionsValue 以及何时不需要的解释。

何时使用optionsValue 绑定(bind)

假设您的餐食可能已售罄,您希望通过服务器检查 availableMeals 中的更新:

const availableMeals = ko.observableArray([]);
const loadMeals = () => getMeals().then(availableMeals);
const selectedMeal = ko.observable(null);

loadMeals();

ko.applyBindings({ loadMeals, availableMeals, selectedMeal });

function getMeals() {
  return {
    then: function(cb) {
      setTimeout(cb.bind(null, [{ mealVal: "STD", mealName: "Standard (sandwich)", price: 0 }, { mealVal: "PRM", mealName: "Premium (lobster)", price: 34.95 }, { mealVal: "ULT", mealName: "Ultimate (whole zebra)", price: 290 }]), 500);  
    }
  }

}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

<select data-bind="options: availableMeals, 
                   value: selectedMeal,
                   optionsText: 'mealName'"></select>
                   
<button data-bind="click: loadMeals">refresh meals</button>

<div data-bind="with: selectedMeal">
  You've selected <em data-bind="text: mealName"></em>
</div>

<div data-bind="ifnot: selectedMeal">No selection</div>

<p>Make a selection, click on refresh and notice the selection is lost when new data arrives.</p>

当您替换 availableMeals 中的对象时会发生什么:

  • Knockout 重新呈现选择框的选项
  • Knockout 检查 selectedMeal() === mealObject 的新值
  • Knockout 在selectedMeal 中没有找到对象,默认第一个选项
  • Knockout 将新对象的引用写入 selectedMeal

问题:您丢失了 UI 选择,因为它指向的对象不再位于可用选项中。

optionsValue 助您一臂之力!

optionsValue 让我们可以解决这个问题。我们没有存储对可能随时被替换的对象的引用,而是存储了一个原始值,即mealVal中的字符串,它允许我们检查不同 API 调用之间的相等性! Knockout 现在会做类似的事情:

selection = newObjects.find(o => o["mealVal"] === selectedMeal());

让我们看看实际效果:

const availableMeals = ko.observableArray([]);
const loadMeals = () => getMeals().then(availableMeals);
const selectedMeal = ko.observable(null);

loadMeals();

ko.applyBindings({ loadMeals, availableMeals, selectedMeal });

function getMeals() {
  return {
    then: function(cb) {
      setTimeout(cb.bind(null, [{ mealVal: "STD", mealName: "Standard (sandwich)", price: 0 }, { mealVal: "PRM", mealName: "Premium (lobster)", price: 34.95 }, { mealVal: "ULT", mealName: "Ultimate (whole zebra)", price: 290 }]), 500);  
    }
  }

}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

<select data-bind="options: availableMeals, 
                   value: selectedMeal,
                   optionsText: 'mealName',
                   optionsValue: 'mealVal'"></select>
                   
<button data-bind="click: loadMeals">refresh meals</button>

<div data-bind="if: selectedMeal">
  You've selected <em data-bind="text: selectedMeal"></em>
</div>

<div data-bind="ifnot: selectedMeal">No selection</div>

<p>Make a selection, click on refresh and notice the selection is lost when new data arrives.</p>

optionsValue 的缺点

请注意我必须如何重写 with 绑定(bind)?突然之间,我们的 View 模型中只有 meal 的一个属性可用,这是非常有限的。如果您希望您的应用程序能够更新其数据,那么您必须在此处执行一些额外的工作。您的两个选择:

  1. 独立存储您选择的字符串(散列)实际对象,或者
  2. 拥有一个 View 模型存储库,当新的服务器数据到达时,映射到现有实例以确保您保持选择状态。

如果有帮助,我可以添加代码片段来更好地解释这两种方法

关于javascript - 为什么设置 optionsValue 会中断 Knockout 更新?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51135005/

相关文章:

javascript - Html5 Canvas Javascript 库?

javascript - 尝试访问指令中的数据时 AngularJs 崩溃

python - Django 1.8 不使用我的自定义登录页面

javascript - 通过 Javascript 运行 HTML

c# - 在 ViewModel SelectedItem 中实现 iNotifypropertychanged 后,在 listview 上不起作用

javascript - 将大文件从nodejs上传到另一台服务器

javascript - 停止 IE/Edge 窗口调整为 iframe 尺寸

html - CSS 显示 :inline does not work on <li> element

mvvm - 尽管正在观察MediatorLiveData onChanged,但未调用

c# - 仅当数据更改时才允许用户保存的更好方法?