Javascript 为什么设置optionsValue会破坏淘汰更新?

Javascript 为什么设置optionsValue会破坏淘汰更新?,javascript,html,mvvm,knockout.js,Javascript,Html,Mvvm,Knockout.js,我一直在看淘汰赛教程,当我在玩一个教程的时候,有些东西让我困惑。这是我的HTML: <h2>Your seat reservations</h2> <table> <thead><tr> <th>Passenger name</th><th>Meal</th><th>Surcharge</th> </tr></th

我一直在看淘汰赛教程,当我在玩一个教程的时候,有些东西让我困惑。这是我的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>
当我运行此示例并从乘客下拉菜单中选择不同的“膳食”时,“附加费”值未更新。原因似乎是我将
选项value:'mealVal'
添加到
选择
数据绑定
属性中,当我删除该属性时,“附加费”确实会在选择新的下拉选项时更新。但是为什么添加
选项value
会破坏更新?所做的就是设置
select
列表的选项
value
属性,这对于表单提交非常有用-我不明白为什么它应该阻止Knockout自动更新

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


我认为,当您指定
选项:$root.availableMeals
,但不指定
选项值时,Knockout可以神奇地确定更改选择时在列表中所做的选择,并允许您从
availableMeals
访问对象,而不仅仅是放入
value
属性的字符串值。这似乎没有很好的文档记录。

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

何时使用
选项value
绑定 比如说,您的饭菜可能已经售完,您想在
availableMeals
中向服务器查询更新:

const availableMeals=ko.observearray([]);
const loadfines=()=>getfines()。然后(可用的菜单项);
const selectedMeal=ko.可观察(空);
加载膳食();
ko.applyBindings({loadfends,availableMeals,selectedMeal});
函数getfeeds(){
返回{
然后:函数(cb){
setTimeout(cb.bind(null,[{mealVal:“STD”,mealName:“Standard(sandwich)”,价格:0},{mealVal:“PRM”,mealName:“Premium(lobster)”,价格:34.95},{mealVal:“ULT”,mealName:“Ultimate(fullzebra)”,价格:290}),500);
}
}
}

刷新膳食
你已经选择了
没有选择

做一个选择,点击刷新,当新数据到达时,注意选择丢失。

好的,在查看淘汰代码后,我已经知道发生了什么-截至撰写本文时,这还没有记录

value
绑定在读取
select
元素的值时,不仅仅查看元素的DOM值

现在,
selectExtensions
所做的就是为
select
(及其子
object
)元素实现特殊的行为,这并不奇怪。这就是神奇发生的地方,因为正如代码中的注释所说:

    // Normally, SELECT elements and their OPTIONs can only take value of type 'string' (because the values
    // are stored on DOM attributes). ko.selectExtensions provides a way for SELECTs/OPTIONs to have values
    // that are arbitrary objects. This is very convenient when implementing things like cascading dropdowns.
因此,当值绑定尝试读取
select
元素时,会出现以下代码:

        case 'select':
            return element.selectedIndex >= 0 ? ko.selectExtensions.readValue(element.options[element.selectedIndex]) : undefined;
这基本上是说“好的,找到所选索引并再次使用此函数读取该索引处的
选项
元素。然后它读取
选项
元素并得出以下结论:

        case 'option':
            if (element[hasDomDataExpandoProperty] === true)
                return ko.utils.domData.get(element, ko.bindingHandlers.options.optionValueDomDataKey);
            return ko.utils.ieVersion <= 7
                ? (element.getAttributeNode('value') && element.getAttributeNode('value').specified ? element.value : element.text)
                : element.value;
是的,正如我所怀疑的,Knockout是在后台存储复杂的数据,但只有当您要求它存储复杂的JS对象时才这样做。这解释了为什么,当您不指定
optionsValue:[someStringValue]
,您的计算函数接收到复杂的膳食对象,而当您指定它时,您只需要传入基本字符串-Knockout只是从
选项
属性中提供字符串


就我个人而言,我认为这应该被清楚地记录下来,因为这是一个有点意外和特殊的行为,可能会让人困惑,即使它很方便。我会要求他们将其添加到文档中。

文档中有一个简短的段落解释
选项value
。”通常,您只希望使用optionsValue作为一种方法,以确保在更新可用选项集时KO可以正确保留选择。“如果您自己的更新回答了您的问题,我建议将其作为答案发布。如果您还有任何问题,请告诉我/我们还有什么尚不清楚。这仍然不清楚,因为它没有解释为什么在设置
optionsValue
时,
formattedPrice
函数会从传递整个用餐对象变为只传递
optionsValue
值。为什么它不能继续得到整个用餐对象?我看不出你在哪里用
绑定重写了
?是的,添加代码片段确实会有帮助。很高兴看到您深入到源代码中,并找到了确切的行为!就个人而言,我希望看到
选项值
选项平等比较程序
绑定所取代:传递一个
(a,b)->布尔值的函数
,该函数用于将
选项
的元素相匹配。
        case 'option':
            if (element[hasDomDataExpandoProperty] === true)
                return ko.utils.domData.get(element, ko.bindingHandlers.options.optionValueDomDataKey);
            return ko.utils.ieVersion <= 7
                ? (element.getAttributeNode('value') && element.getAttributeNode('value').specified ? element.value : element.text)
                : element.value;
    switch (ko.utils.tagNameLower(element)) {
        case 'option':
            if (typeof value === "string") {
                ko.utils.domData.set(element, ko.bindingHandlers.options.optionValueDomDataKey, undefined);
                if (hasDomDataExpandoProperty in element) { // IE <= 8 throws errors if you delete non-existent properties from a DOM node
                    delete element[hasDomDataExpandoProperty];
                }
                element.value = value;
            }
            else {
                // Store arbitrary object using DomData
                ko.utils.domData.set(element, ko.bindingHandlers.options.optionValueDomDataKey, value);
                element[hasDomDataExpandoProperty] = true;

                // Special treatment of numbers is just for backward compatibility. KO 1.2.1 wrote numerical values to element.value.
                element.value = typeof value === "number" ? value : "";
            }
            break;