在开始之前,我可以断言我不是网络开发人员。我正在尝试修复我们的小型Web应用程序(用于配置嵌入式设备)中的以下错误,因为编写它的网络流畅的开发人员不再需要指针。我会尽力解释我能做些什么。
此特定网页设置为允许用户配置和监控音频处理设备上的值。当用户更改页面上的值时,页面会向设备发送RPC请求以在其中设置值。当通过其他方式(例如,通过输入屏幕手动)更改设备上的值时,将向网页发送JSON通知,并通过JavaScript更新相关控件。该网页使用Bootstrap作为UI,并使用Knockout确保每当值(我们称之为"参数"在设备上)发生更改时,相关控件都会更新。
我遇到的问题是组合框(即通过HTML中的<select>
和<option>
标记构建的控件)有时在通过JavaScript更改时不会显示相应的值。如果用户选择组合框中的项目,并且稍后在设备上更改相应的值(并通过JavaScript传播回网页),则组合框将不再能够显示用户最初选择的项目 - 而是,它将显示第一个项目。
例如,使用包含项目A,B,C,D和E的组合框:
用户选择:D。
从设备向网页发送值时:
如果用户没有与组合框进行交互,则不会发生这种情况 - 在这种情况下,设备通知页面的任何值都将正确显示。
我没有足够的经验知道所有血腥细节如何在幕后工作,但我已经进行了足够深入的研究以了解以下内容:
data-bind
属性中,foreach
设置为$data.widgetData.options
等等。我不知道这是否相关。value
属性完全匹配。我通过创建Knockout observable的另一个手动订阅并在更改时注销该值来确认这一点。selected
属性,并从之前选择的任何选项中删除。在两种情况下都会正确发生这种情况(当组合框显示正确的当前值时,以及当它没有时)。当用户通过用户界面选择项目时,这也会正确发生,这让我相信它不是问题的原因。简而言之:一旦用户选择了一个项目至少一次,并且以编程方式对组合框进行了进一步更改,所有与HTML相关的修改出现与以前完全相同 - 除了组合框没有显示用户选择的一个项目的正确内容。
当页面上出现组合框时,HTML最终会看起来像这样:
<select
class="form-control"
data-bind="
attr: {
'data-panelid': panelData.__hierarchyId,
'data-paramtrigger': widgetData.paramTrigger,
'data-widgetref': widgetData.ref,
'data-menuRef': widgetData.menuRef,
'data-widgetNotifyType': widgetData.notifyType
},
disable: ($data.widgetData.disabled ? $data.widgetData.disabled : false),
foreach: $data.widgetData.options
"
data-panelid="system-1-mpx-1"
data-widgetref="dspx.enum.8"
>
<option data-bind="
attr: {
'data-paramtrigger': $parents[0].widgetData.paramTrigger,
'value': $data.name,
'selected': ($root.remoteParameterObservables[$parents[0].widgetData.ref]() == $data.name ? 'selected' : null),
'data-notifyType': $parents[0].widgetData.notifyType
},
text: ($data.webName ? webName : ($data.displayName ? displayName : name))
"
value="none">none</option>
<!-- ... etc ... -->
</select>
(为了便于阅读,添加了空格/换行符。)
同样,我并不是100%确定这个具体对我们的设置有多少,以及Knockout需要多少是一部分。
组合框可能有这样的表现吗?很可能我们的Javascript代码中存在导致问题的东西,但由于我不熟悉这些类型的Web技术,我想首先看看原因是否可能是某些原因更明显。
修改
对于上下文,这是Javascript的精简版本,用于处理来自设备的值更改通知:
// Called when a notification is received from the device.
var onNotify = function(data)
{
// If we have a KO observable for the parameter on the device with this name
// (eg. "system.param.1"):
if (remoteViewModel.remoteParameterObservables.hasOwnProperty(data.name))
{
try
{
// Set the value of the observable.
remoteViewModel.remoteParameterObservables[data.name](data.value);
}
catch (e)
{
console.debug('Param `' + param + '` on remoteViewModel.remoteParameterObservables is not a knockout observable');
}
}
}
// ...
// Elsewhere, when creating KO observables, I inserted this for debugging purposes.
// This function always prints out the value I expect, whenever the observable is changed.
if ( parameterName === "param.im.testing.with" )
{
observableForThisParam.subscribe(function(newValue)
{
console.log("Parameter was updated with a new value:", newValue);
});
}