Knockout似乎并不喜欢bind中的object.property

时间:2015-07-25 01:42:54

标签: knockout.js knockout-3.0

在我的Knockout.js模板中,如果我可以访问视图模型上对象的属性会很方便:

<span data-bind="text: account.shortName"></span>

这不起作用。该元素为空白。但是,我可以这样做:

<div data-bind="with: account">
  <span data-bind="text: shortName"></span>
</div>

这有什么办法吗?我必须在任何地方使用with,以及过多的元素吗?

2 个答案:

答案 0 :(得分:3)

如果account是可观察的,那么你真的应该像使用它一样使用with绑定,或者使用计算的observable来访问该属性。当然,它有点冗长,但必须这样做。

使用像someObservable().someProperty这样的表达式只会导致头痛和混乱,应该避免。例如,如果您确实使用了这个并且someProperty恰好是可观察的,那么当someObservable发生变化时,您可能会注意到某些事情是不正确的。绑定将更新为使用新值的someProperty,我希望您能看到原因。

您可以通过创建一个函数来更安全地创建计算的observable。

ko.observable.fn.property = function (name) {
    return ko.computed({
        read: function () {
            var parentValue = this();
            if (parentValue)
                return ko.utils.unwrapObservable(parentValue[name]);
        },
        write: function (value) {
            var parentValue = this(), property;
            if (parentValue) {
                property = parentValue[name];
                if (ko.isWriteableObservable(property))
                    property(value);
            }
        },
        owner: this
    });
};

然后你可以在你的绑定中使用它:

<span data-bind="text: account.property('shortName')"></span>

答案 1 :(得分:2)

杰夫的答案很好,但您也可以通过将绑定更改为:

来实现
text: account() && account().shortName

我发现这与杰夫在他的回答中提到的相反,即使shortName是可观察的,因为绑定是implemented inside of a computed observable,这也有效。换句话说,text绑定的值实现为一种匿名计算。

这是一个片段,显示它有效,从没有值的account observable开始,然后随着时间的推移更新。

var vm = {
  account: ko.observable()
};

$(function() {
  ko.applyBindings(vm);

  // After a second, set account
  setTimeout(function() {
    var account = {
      shortName: ko.observable('Initial account')
    };
    vm.account(account);

    var newAccount = {
      shortName: ko.observable('New account')
    }

    // After another second, change account
    setTimeout(function() {
      vm.account(newAccount);

      // After another second, change shortName within the new account
      setTimeout(function() {
        newAccount.shortName('New shortName value in the new account');
      }, 1000);
    }, 1000);
  }, 1000);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

<body>
  <h1>Account Binding Test</h1>
  <span data-bind="text: account() && account().shortName"></span>
</body>