源为空/未定义时的 KnockoutJS 绑定
Posted
技术标签:
【中文标题】源为空/未定义时的 KnockoutJS 绑定【英文标题】:KnockoutJS binding when source is null/undefined 【发布时间】:2012-07-08 09:44:23 【问题描述】:是否有更短/更简洁的方法来进行空/未定义测试?
<select data-bind="options: SelectedBusinessLine() ? SelectedBusinessLine().Clusters() : [],
optionsText: 'Title',
value: SelectedCluster,
optionsCaption: 'Select Cluster..'">
</select>
代替
data-bind="options: SelectedBusinessLine() ? SelectedBusinessLine().Clusters() : [],
我愿意
data-bind="options: SelectedBusinessLine().Clusters(),
给予或接受()
或者至少是一个更简单的空运算符检查'??'选定的业务线 ?? []
或绑定参数来自动检查 null 或静默失败。
如果可能的话,有什么想法吗?
【问题讨论】:
【参考方案1】:This page 提供了几种解决方案。相关部分是这个:
防止空对象
如果你有一个包含对象的 observable,并且你想绑定到该对象的属性,那么你需要小心它是否有可能是 null 或 undefined。你可以这样写你的绑定:
<span data-bind="text: selectedItem() ? selectedItem().name() : 'unknown'"></span>
有很多方法可以处理这个问题。首选方法是简单地使用模板绑定:
var viewModel =
items: ko.observableArray(),
selectedItem: ko.observable()
;
<ul data-bind="template: name: 'editorTmpl', data: selectedItem "></ul>
<script id="editorTmpl" type="text/html">
<li>
<input data-bind="value: name" />
</li>
</script>
使用这个方法,如果selectedItem
为空,那么它就不会渲染任何东西。因此,您不会像在原始绑定中那样看到未知。但是,它确实具有简化绑定的额外好处,因为您现在可以直接指定属性名称,而不是 selectedItem().name
。这是最简单的解决方案。
只是为了探索一些选项,这里有一些替代方案:
您可以像我们之前一样使用计算的 observable。
viewModel.selectedItemName = ko.computed(function()
var selected = this.selected();
return selected ? selected.name() : 'unknown';
, viewModel);
但是,这又给我们的视图模型增加了一些我们可能不想要的膨胀,我们可能不得不对许多属性重复此操作。
您可以使用自定义绑定,例如:
<div data-bind="safeText: value: selectedItem, property: 'name', default: 'unknown' "></div>
ko.bindingHandlers.safeText =
update: function(element, valueAccessor, allBindingsAccessor)
var options = ko.utils.unwrapObservable(valueAccessor()),
value = ko.utils.unwrapObservable(options.value),
property = ko.utils.unwrapObservable(options.property),
fallback = ko.utils.unwrapObservable(options.default) || "",
text;
text = value ? (options.property ? value[property] : value) : fallback;
ko.bindingHandlers.text.update(element, function() return text; );
;
这比原版好吗?我会说可能不会。它确实避免了我们绑定中的 javascript,但它仍然非常冗长。
另一个选择是创建一个增强的 observable,它提供了一种访问属性的安全方法,同时仍然允许实际值为 null。可能看起来像:
ko.safeObservable = function(initialValue)
var result = ko.observable(initialValue);
result.safe = ko.dependentObservable(function()
return result() || ;
);
return result;
;
所以,这只是一个 observable,它还公开了一个名为 safe 的计算 observable,它总是返回一个空对象,但实际的 observable 可以继续存储 null。
现在,您可以像这样绑定到它:
<div data-bind="text: selectedItem.safe().name"></div>
当它为null时你不会看到未知值,但是当selectedItem
为null时它至少不会导致错误。
我确实认为在这种情况下首选的选项是使用模板绑定,尤其是当您有许多此类属性要绑定时。
【讨论】:
模板的唯一问题是有时我想将选择框显示为空的选项而不是隐藏它。我认为框架应该为我们提供一种方法,让我们不使用 if null else 代码来膨胀源代码.. 这里的模板示例实际上并没有工作 - 如果模板的“数据”部分为空,那么我会遇到绑定错误。我可以通过为模板绑定应用“if”属性来解决这个问题: @DrewMiller,我认为处理空对象处理的是 jquery 模板引擎。当使用本机淘汰模板引擎时,if 属性可以解决问题。 仅供参考 ko.dependentObservable is deprecated ko.computed 现在。 最佳答案见下方“with”【参考方案2】:另一个答案引用的excellent page 中未提及的一种方法是使用with
<div data-bind="with: selecteditem">
<form>
<fieldset>
<div>
<label>first name</label>
<input data-bind="value: firstname"></input>
</div>
<div>
<label>lasst name</label>
<input data-bind="value: lastname"></input>
</div>
</fieldset>
<div>
<a href="#" data-bind="click: $root.savechanges">Save</a>
</div>
</form>
</div>
如果selecteditem
为空,则整个 UI 将消失。
【讨论】:
(这是我们想要的) 最简单,最优雅。谢谢! 这应该是公认的答案。当前接受的答案太复杂,无法使这样一个简单的要求起作用。这是with
的核心功能之一。
如果您需要访问上述“范围”内的内容,请使用$parent
@Simon_Weaver 我打算避免使用 ifnot 复制 UI:VM is null ' 绑定,而且我没有明确定义的 VM(因为它有很多属性),但只是 mapping.fromJS 是够了。【参考方案3】:
“With”有效(可能其他人也有效......)
但是使用“With”,即使里面有条件,角色 UI 也会消失/出现......例如......我需要设置一个状态(真/假)按钮,但前提是状态不是空...
<!-- ko ifnot: Status() == null -->
<span data-bind="if: Status">
<a class="rk-button rk-red-action" data-bind="click: changeStatus"><i class="rk-ico rk-ico-save"></i>Desativar</a>
</span>
<span data-bind="ifnot: Status">
<a class="rk-button rk-black-action" data-bind="click: changeStatus"><i class="rk-ico rk-ico-save"></i>Ativar</a>
</span>
<!-- /ko -->
这行得通。在这种情况下。
但有时就像 Simon_Weaver 所说的,只是“With”的作品!
【讨论】:
我从来不知道ifnot
:-)
如果你的Status
observable 是一个计算出的 observable 并且你也启用了 ko.options.deferUpdates = true;` 时要小心,因为你仍然可以得到空错误 - ***.com/questions/43341484【参考方案4】:
我更喜欢这种方法
创建自定义绑定
ko.bindingHandlers.safeText =
update: function (element, valueAccessor, allBindingsAccessor)
try
var tryGetValue = valueAccessor()();
ko.bindingHandlers.text.update(element, valueAccessor());
catch (e)
ko.bindingHandlers.text.update(element, function () return ""; );
;
用法
data-bind="safeText: function() return my().nested.object.property;
您需要添加的唯一额外内容是使用 'function() return ... ' 包装原始值
但是,这将停止值调用下的所有错误。您可以通过仅查找“未定义”异常来改进自定义绑定。您可能还想通过添加默认文本选项来改进此绑定。
【讨论】:
【参考方案5】:这些解决方案中的大多数在我设置属性的特定情况下都不起作用:
<div role="combobox" data-bind="attr:
'aria-activedescendant': activedescendant() ? activedescendant().id : null
">
...
</div>
template
和 with
绑定在这里不起作用,因为如果我没有活跃的后代,那么我的 div 将为空。对于我的解决方案,我创建了一个可观察的方法:
ko.observable.fn.get = function (propertyName, defaultValue)
var
self = this(),
propertyValue;
if (self != null)
propertyValue = ko.unwrap(self[propertyName]);
return propertyValue || defaultValue;
这允许我将绑定更改为:
<div role="combobox" data-bind="attr:
'aria-activedescendant': activedescendant.get('id')">
...
</div>
【讨论】:
【参考方案6】:上述大多数解决方案对我来说都不是开箱即用的,因为我只想将其应用于 foreach
中的单个元素,所以我稍微修改了接受答案的方法:
<span data-bind="text: ((typeof info).localeCompare('undefined')) ? info : ''"></span>
【讨论】:
以上是关于源为空/未定义时的 KnockoutJS 绑定的主要内容,如果未能解决你的问题,请参考以下文章
“未捕获的 TypeError:无法处理绑定”与 KnockoutJS