简体   繁体   English

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

[英]KnockoutJS Binding Issue - Cannot read property

I have what is likely a simple Knockout question but I'm a complete beginner with it. 我有一个简单的Knockout问题,但我是一个完全的初学者。 I was tossed this page of code that someone else has worked on but never finished. 我扔了这个代码页,其他人已经工作但从未完成。

When this page first loads, the data is retrieved and the main grid loads properly. 首次加载此页面时,将检索数据并正确加载主网格。 The problem comes in when I attempt to auto-select the first record in the results so that a detail list gets filled out below the grid. 当我尝试自动选择结果中的第一条记录以便在网格下方填写详细列表时,会出现问题。

When that happens, I receive the following message. 当发生这种情况时,我收到以下消息。

Uncaught TypeError: Unable to process binding "text: function (){return selected.RequestLog.Timestamp }" , Message: Cannot read property 'Timestamp' of undefined 未捕获的TypeError:无法处理绑定“text:function(){return selected.RequestLog.Timestamp}”,消息:无法读取未定义的属性“Timestamp”

Here is the code snippets with which I'm working. 这是我正在使用的代码片段。 The data coming back is from Entity Framework. 返回的数据来自实体框架。

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;
                    }
                });
            };
        };

This is the actual binding that tries to happen after the data is loaded. 这是在加载数据后尝试发生的实际绑定。 RequestLog does not seem to exist at binding time, even though it does seem to be ok if I set a breakpoint in the above function on the line self.selected(result[0]). RequestLog似乎并不在结合时存在,即使它似乎是确定的,如果我在上面的函数中设置断点上self.selected线(结果[0])。

I think this is a scope problem but I can't for the life of me think of how best to fix it. 我认为这是一个范围问题,但我不能为我的生活考虑如何最好地解决它。 Any help would be appreciated. 任何帮助,将不胜感激。

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

UPDATE: Here is the document ready portion. 更新:这是文档就绪部分。

jQuery(document).ready(function () {

            var vm = new siteLogModel();
            vm.updateErrorList(0);
            ko.applyBindings(vm);
        });

Your selected observable does not have a .RequestLog property at the time ko is evaluating the binding expression. 在ko评估绑定表达式时,您selected observable没有.RequestLog属性。 That error is coming from javascript, not ko (though ko wraps the exception in the error message you see). 该错误来自javascript,而不是ko(虽然ko在您看到的错误消息中包含异常)。 When running, selected.RequestLog === undefined is true, and you can't invoke anything on undefined. 运行时, selected.RequestLog === undefined为true,你不能在undefined上调用任何东西。 It's like a null reference exception. 它就像一个空引用异常。

It makes sense if you are calling applyBindings before the ajax call finishes. 如果在ajax调用完成之前调用applyBindings ,则有意义。

One way to fix this by doing a computed instead: 通过执行计算来解决此问题的一种方法:

<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...';
});

With the above, nothing is ever being invoked on an undefined variable. 有了上述内容,在未定义的变量上不会调用任何内容。 Your label will display "Still waiting on data" until the ajax call finishes, then it will populate with the timestamp as soon as you invoke self.selected(result[0]) . 在ajax调用完成之前,您的标签将显示“仍在等待数据”,然后只要您调用self.selected(result[0]) ,它就会填充时间戳。

Another way to solve it is by keeping your binding the same, but by giving the selected observable an initial value. 解决它的另一种方法是保持绑定相同,但通过给选定的observable一个初始值。 You can leave all of your html as-is, and just to this: 您可以将所有html保留为原样,只是为此:

self.selected = ko.observable({
    RequestLog: {
        TimeStamp: 'Still waiting on data'
    }
});

... and you will end up with the same result. ......最终会得到相同的结果。

Why? 为什么?

Any time you initialize an observable by doing something like self.prop = ko.observable() , the actual value of the observable is undefined . 通过执行self.prop = ko.observable()类的self.prop = ko.observable()初始化observable时,observable的实际值是undefined Try it out: 试试看:

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');

So to summarize what is happening: 总结一下发生了什么:

  1. You initialize your selected observable with a value equal to undefined in your viewmodel. 在视图模型中使用等于undefined的值初始化选定的observable。
  2. You call ko.applyBindings against the viewmodel. 您针对viewmodel调用ko.applyBindings。
  3. ko parses the data-bind attributes, and tries to bind. ko解析data-bind属性,并尝试绑定。
  4. ko gets to the text: selected.RequestLog.Timestamp binding. ko到text: selected.RequestLog.Timestamp绑定。
  5. ko invokes selected(), which returns undefined. ko调用selected(),返回undefined。
  6. ko tries to invoke .RequestLog on undefined. ko尝试在undefined上调用.RequestLog。
  7. ko throws an error, because undefined does not have a .RequestLog property. ko抛出一个错误,因为undefined没有.RequestLog属性。

All of this happens before your ajax call returns. 所有这些都发生在你的ajax调用返回之前。

Reply to comment #1 回复评论#1

Yes, you can call applyBindings after your ajax success event. 是的,您可以在ajax成功事件后调用applyBindings。 However, that's typically not always what you should do. 但是,这通常并不总是你应该做的。 If you want to, here's one example of how it could be done: 如果你愿意,这里有一个如何做到的例子:

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);
    });
});

Yet another way would be to go ahead and eager-bind (applyBindings before the ajax call finishes), but wrap your markup in an if binding like so: 另一种方法是继续eager-bind(在ajax调用完成之前applyBindings),但是将你的标记包装在if绑定中,如下所示:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM