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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21291251/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 20:34:49  来源:igfitidea点击:

KnockoutJS Binding Issue - Cannot read property

javascriptknockout.jsasp.net-mvc-2

提问by Rob Horton

I have what is likely a simple Knockout question but I'm a complete beginner with it. 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

未捕获的类型错误:无法处理绑定“文本:函数(){return selected.RequestLog.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 doesseem 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);
        });

回答by danludwig

Your selectedobservable does not have a .RequestLogproperty at the time ko is evaluating the binding expression. That error is coming from javascript, not ko (though ko wraps the exception in the error message you see). When running, selected.RequestLog === undefinedis true, and you can't invoke anything on undefined. It's like a null reference exception.

您的selectedobservable.RequestLog在 ko 评估绑定表达式时没有属性。该错误来自 javascript,而不是 ko(尽管 ko 在您看到的错误消息中包含了异常)。运行时,selected.RequestLog === undefined为真,并且您不能在 undefined 上调用任何内容。这就像一个空引用异常。

It makes sense if you are calling applyBindingsbefore the ajax call finishes.

如果您applyBindings在 ajax 调用完成之前调用,这是有道理的。

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. You can leave all of your html as-is, and just to this:

解决它的另一种方法是保持绑定不变,但给选定的 observable 一个初始值。您可以保留所有 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. Try it out:

任何时候通过执行类似的操作来初始化 observable 时self.prop = ko.observable(),该 observable 的实际值都是undefined. 试试看:

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.
  2. You call ko.applyBindings against the viewmodel.
  3. ko parses the data-bind attributes, and tries to bind.
  4. ko gets to the text: selected.RequestLog.Timestampbinding.
  5. ko invokes selected(), which returns undefined.
  6. ko tries to invoke .RequestLog on undefined.
  7. ko throws an error, because undefined does not have a .RequestLog property.
  1. 你在你的视图模型中用一个等于 undefined 的值来初始化你选择的 observable。
  2. 您针对视图模型调用 ko.applyBindings。
  3. ko 解析数据绑定属性,并尝试绑定。
  4. ko 进入text: selected.RequestLog.Timestamp绑定。
  5. ko 调用 selected(),它返回 undefined。
  6. ko 尝试在未定义时调用 .RequestLog。
  7. 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. However, that's typically not always what you shoulddo. If you want to, here's one example of how it could be done:

是的,您可以在 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);
    });
});

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:

另一种方法是继续进行热切绑定(在 ajax 调用完成之前应用绑定),但将您的标记包装在一个 if 绑定中,如下所示:

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