Javascript KnockoutJS绑定问题-无法读取属性

Javascript KnockoutJS绑定问题-无法读取属性,javascript,knockout.js,asp.net-mvc-2,Javascript,Knockout.js,Asp.net Mvc 2,我有一个可能是简单的淘汰题,但我是一个完全的初学者。我被扔到这一页代码,其他人已经完成了,但从未完成 首次加载此页面时,将检索数据并正确加载主网格。当我尝试自动选择结果中的第一条记录,以便在网格下方填写详细列表时,问题就出现了 当这种情况发生时,我会收到以下消息 未捕获类型错误:无法处理绑定文本:函数{return selected.RequestLog.Timestamp},消息:无法读取未定义的属性“Timestamp” 下面是我正在使用的代码片段。返回的数据来自实体框架 var siteL

我有一个可能是简单的淘汰题,但我是一个完全的初学者。我被扔到这一页代码,其他人已经完成了,但从未完成

首次加载此页面时,将检索数据并正确加载主网格。当我尝试自动选择结果中的第一条记录,以便在网格下方填写详细列表时,问题就出现了

当这种情况发生时,我会收到以下消息

未捕获类型错误:无法处理绑定文本:函数{return selected.RequestLog.Timestamp},消息:无法读取未定义的属性“Timestamp”

下面是我正在使用的代码片段。返回的数据来自实体框架

var siteLogModel = function () {
            var self = this;

            self.errorList = ko.observableArray([]);
            self.selected = ko.observable();

            self.updateErrorList = function (page) {
                jQuery.ajax({
                    type: "POST",
                    url: "/Admin/ErrorPage",
                    data: { pageNum: page },
                    success: function (result) {
                        self.errorList(result);
                        self.selected(result[0]);

                        // Since we have success, add the click handler so we can get the details about a row by id.
                        //addRowHandlers();
                    },
                    error: function (result) {
                        jQuery("#status").text = result;
                    }
                });
            };
        };
这是加载数据后尝试进行的实际绑定。在绑定时,RequestLog似乎不存在,即使在self.selectedresult[0]行的上述函数中设置断点似乎没有问题

我认为这是一个范围问题,但我一辈子都想不出如何最好地解决它。任何帮助都将不胜感激

    <div class="param">
       <span>Time</span>
       <label data-bind="text: selected.RequestLog.Timestamp"></label>
    </div>
在ko计算绑定表达式时,您选择的observable没有.RequestLog属性。该错误来自javascript,而不是ko,尽管ko将异常包装在您看到的错误消息中。运行时,selected.RequestLog===undefined为true,您不能在undefined上调用任何内容。这就像一个空引用异常

如果在ajax调用完成之前调用applyBindings,这是有意义的

解决此问题的一种方法是改为执行计算:

<div class="param">
   <span>Time</span>
   <label data-bind="text: selectedRequestLogTimestamp"></label>
</div>

self.selectedRequestLogTimestamp = ko.computed(function() {
    var selected = self.selected();
    return selected && selected.RequestLog
        ? selected.RequestLog.TimeStamp
        : 'Still waiting on data...';
});
。。。你会得到同样的结果

为什么?

任何时候,通过执行self.prop=ko.observable之类的操作初始化可观察对象时,可观察对象的实际值都是未定义的。试一试:

self.prop1 = ko.observable();
var prop1Value = self.prop1();
if (typeof prop1Value === 'undefined') alert('It is undefined');
else alert('this alert will not pop up unless you initialize the observable');
因此,总结一下正在发生的事情:

您可以在viewmodel中使用等于undefined的值初始化选定的observable。 对viewmodel调用ko.applyBindings。 ko解析数据绑定属性,并尝试绑定。 ko获取文本:selected.RequestLog.Timestamp绑定。 ko调用selected,它返回undefined。 ko试图在未定义的情况下调用.RequestLog。 ko抛出一个错误,因为undefined没有.RequestLog属性。 所有这些都发生在ajax调用返回之前

对评论1的答复

是的,您可以在ajax成功事件之后调用applyBindings。然而,这通常不是你应该做的。如果您愿意,这里有一个例子可以说明如何做到这一点:

self.updateErrorList = function (page) {
    self.updateErrorPromise = jQuery.ajax({
        type: "POST",
        url: "/Admin/ErrorPage",
        data: { pageNum: page },
        success: function (result) {
            self.errorList(result);
            self.selected(result[0]);
        },
        error: function (result) {
            jQuery("#status").text = result;
        }
    });
};

jQuery(document).ready(function () {
    var vm = new siteLogModel();
    vm.updateErrorList(0);
    vm.updateErrorPromise.done(function() {
        ko.applyBindings(vm);
    });
});
另一种方法是在ajax调用完成之前进行绑定applyBindings,但将标记封装在if绑定中,如下所示:

<div class="param" data-bind="if: selected">
   <span>Time</span>
   <label data-bind="text: selected.RequestLog.Timestamp"></label>
</div>

您的结果对象看起来像什么?此外,您如何使用ObservalArray可能存在问题。没有必要向构造函数传递一个空数组,尽管它可能不会有什么坏处,您可以使用self.errorList.pushresult来添加值。Dan-这是一个令人惊讶的答案!非常感谢。我在原始文本中添加了document.ready函数,以显示如何调用applybindings。有没有一种方法可以简单地说“在调用完成后等待并调用applybindings”,而不是对每个绑定逐个属性执行?如果我正确地阅读了您的答案,那么我必须像您对self.selectedRequestLogTimestamp所做的那样,为我拥有的每个绑定编写一段代码。对吗?没错,我用一种方法更新了我的答案。有十几种不同的方法可以使用knockout。您可能还想查看if和ifnot绑定的文档。您可以将其他绑定封装在其中一个绑定中,以使ko延迟绑定,直到您选择的observable具有值。再次感谢您的帮助。为了回到您最初的建议,我必须询问并说明我的经验不足,self.selectedRequestLogTimestamp函数的确切位置是哪里?我假设在var siteLogModel=函数的内部。我尝试了这个,虽然我不再收到我的原始错误,但我从未看到任何数据返回,也从未显示默认文本。是的,这就是它应该去的地方。computed的工作原理几乎与observable类似——当没有传递参数时,它应该只返回一个值。老实说,如果不在JS中设置断点,我无法解释为什么不调用它。你把它作为viewmodel的最后一个成员了吗?没有。我只是一时兴起,试了一下。我将vm变量声明放在document.ready函数之外的页面级别。然后我在绑定中引用了vm.selected。到目前为止,这似乎是可行的。这对你有意义吗?还是你认为我这样做有失偏颇?
self.updateErrorList = function (page) {
    self.updateErrorPromise = jQuery.ajax({
        type: "POST",
        url: "/Admin/ErrorPage",
        data: { pageNum: page },
        success: function (result) {
            self.errorList(result);
            self.selected(result[0]);
        },
        error: function (result) {
            jQuery("#status").text = result;
        }
    });
};

jQuery(document).ready(function () {
    var vm = new siteLogModel();
    vm.updateErrorList(0);
    vm.updateErrorPromise.done(function() {
        ko.applyBindings(vm);
    });
});
<div class="param" data-bind="if: selected">
   <span>Time</span>
   <label data-bind="text: selected.RequestLog.Timestamp"></label>
</div>