在Knockout中使用带有绑定的$ root内部绑定时,未定义属性

时间:2013-01-22 00:34:19

标签: knockout.js

我很确定我在上下文中遗漏了一些东西,但我无法弄清楚是什么。

我有以下ViewModel:

var ViewModel = function(){
    var self = this;
    self.person = ko.observable();
    self.isPerson = ko.observable();

    self.person.subscribe(function(value){
        self.isPerson('firstName' in value);
    });
};

var vm = new ViewModel();
var personA = { };
var personB = { firstName: ko.observable("hello") };

vm.person(personA);
ko.applyBindings(vm);
setTimeout(function(){ vm.person(personB); }, 1000);

以及以下视图:

<span data-bind="with: person">
    <!-- ko if: $root.isPerson -->
        <span data-bind="text: firstName"></span> 
    <!-- /ko -->    
</span>

JSFiddle

一旦超时执行,我希望firstName在视图中显示,但是,我收到以下错误:

 firstName is not defined;

如果我从viewModel中的personB开始,它就可以了。如果我将if语句移到with语句之上,它就可以工作。

在这种情况下,我做错了什么?

更新了JSFiddle

1 个答案:

答案 0 :(得分:3)

我不认为你做错了什么。问题是在更新绑定到“with”之前,正在应用“if”绑定到root.isEmployee。所以代码看到对isEmployee的更新,然后从那里重新评估视图,但当前上下文仍然是老人(因为该订阅尚未触发)。

通过http://jsfiddle.net/drdamour/X6pC9/2/中的自定义绑定证明,更新接收到2个事件,一次使用旧值导致isEmployee更新,第二次使用更新后的新值。第二次更新来自触发的“with”绑定订阅。订阅“with”绑定发生在applyBindings调用期间,该调用发生在您的模型订阅之后。

你可以使用$ data.PropertyName技巧来处理undefined不会导致问题。阿拉:http://jsfiddle.net/drdamour/X6pC9/1/

<span data-bind="with: person">
    <span data-bind="text: firstName"></span> 
    <!-- ko if: $root.isEmployee -->
        <span data-bind="text: $data.employeeId"></span> 
        <span data-bind="text: $data.employer"></span> 
    <!-- /ko -->    
</span>

解决此问题的正确方法是让PersonVM计算出isEmployee,这样就不会绑定到root。见:http://jsfiddle.net/drdamour/eVXTF/1/

<span data-bind="with: person">
    <span data-bind="text: firstName"></span> 
    <!-- ko if: isEmployee -->
        <span data-bind="text: $data.employeeId"></span> 
        <span data-bind="text: $data.employer"></span> 
    <!-- /ko -->    
</span>

var ViewModel = function(){
    var self = this;
    self.person = ko.observable();
};

var PersonVM = function()
{
    var self = this;
    this.firstName = ko.observable();
    this.employeeId = ko.observable();
    this.employer = ko.observable();

    self.isEmployee = ko.computed(function(){return self.employer() != null});
}

var vm = new ViewModel();
var customer = new PersonVM();
customer.firstName("John");

var employee = new PersonVM();
employee.firstName("Bill");
employee.employeeId(123);
employee.employer("ACME");

vm.person(customer);
ko.applyBindings(vm);
setTimeout(function(){ vm.person(employee); }, 3000);

计算优先于订阅方法,因为它为您处理订阅链,并抽象您远离必须管理所有这些。