使用敲除和微风时“TypeError:对象不是函数”

Posted

技术标签:

【中文标题】使用敲除和微风时“TypeError:对象不是函数”【英文标题】:"TypeError: object is not a function" when using knockout and breeze 【发布时间】:2015-02-27 01:57:59 【问题描述】:

问题

EntityManager.saveChanges 时出现“TypeError: object is not a function”错误。在微风向服务器发送任何内容之前出现错误。

当我阅读(长)堆栈跟踪时,我看到错误是在名为 InitializeEntityPrototype.prototype.getProperty 的 Breeze 方法中引发的。

您可能会在其他地方遇到此错误;这恰好是我今天发现它的地方。

原因

请注意,我的应用程序使用 Knockout (ko)。这意味着我的实体模型由轻量级“ko”模型适配器管理,该适配器期望 每个数据属性 都是 ko.observable(@ 987654323@ 或observableArray)。这意味着您的属性值被实现为函数,而不是原始数据类型、数组或对象。

Breeze 使用可观察属性初始化我的实体,因此我不必自己动手。但由我决定在我的代码中设置实体模型时保留这些可观察对象

ko 编程中最常见的错误之一是设置属性而不是调用属性设置器。

todo.Description("foo");  // Correct ... call the ko.observable assignment function
todo.Description = "foo"; // WRONG ... wipes out the observable function !!!

当我错误地将“foo”分配给todo.Description 时,可观察函数就消失了……Breeze 监视该属性更改的能力也消失了。

我的数据绑定 html 控件似乎可以继续工作。 Knockout 不必绑定到 observable;它将愉快地绑定到原始数​​据值。但现在这变成了一次性只读绑定。该属性不再是可观察的。对该属性的后续更改将不会传播到屏幕。

换句话说,我的错误分配会导致 UI 行为出现静默错误。

但是当 Breeze 尝试处理该属性时,我的错误并没有消失。 Breeze 假设实体数据属性是一个 ko 函数。 Breeze ko 模型适配器不需要检查......它只是调用它假定的函数。这是(内部)实体getProperty 的实现:

proto.getProperty = function (propertyName) 
  return this[propertyName]();
;

如果this[propertyName] 不是函数,您就会明白为什么会抛出异常。

对我来说可悲的是,错误很可能是在 Breeze 操作的内部深处引发的。错误消息“对象不是函数”(或类似的东西)可能是关于任何东西的。我不太可能建立联系。

这个 S.O.这个问题提醒您寻找这个特定的原因。

怎么办?

这显然是我的错。现在我必须找到我分配实体属性的位置而不是调用函数......并修复它。

我的搜索取决于知道属性名称。当前的错误消息没有告诉我名称。所以我必须对代码进行断点并在抛出错误时捕获它。

一个“简单”的权宜之计是在 breeze.debug.js 中使用 try/catch 版本临时修补此方法。

    在编辑器中打开 breeze.debug.js 找到“return this[propertyName]();

    改成这样:

    proto.getProperty = function(propertyName) 
      try 
        return this[propertyName]();
       catch (err) 
        debugger;
        err.message = propertyName + ' is not a ko function; did you wipe it out by assignment?\n' + 
                      (err.message || '');
        throw err;
      
    
    

    在打开开发者工具的情况下再次运行应用

它应该停在debugger 行,在那里您将了解属性名称和所涉及的实体。

Breeze 是否应该提供更丰富的错误消息?

如果 Breeze 为我撰写此消息,那就太好了。事实上,该团队正在考虑这样做。

主要障碍是性能。 getPropertysetProperty 方法处于热路径上。他们经常被调用,尤其是在查询结果实现期间。 Breeze 团队对这个敏感领域的额外逻辑持谨慎态度。我们不希望每个人都付出高昂的代价来捕捉开发人员错误,至少不是在生产(缩小)Breeze 库中。

让 Breeze 团队有时间解决这个问题。

【问题讨论】:

您先生应该得到更多的支持。你救了我的……你知道吗。 【参考方案1】:

您的问题写得很好,涉及一个常见问题,并且可能对其他搜索此特定问题的人有所帮助。但是,它可能仍在征求意见

无论如何……

我和我的同事将您遇到的问题称为“淘汰税”:您必须处理一些编程不便,才能使用出色的 MVVM 库。当您data-bind="visible: !myObservable" 并忘记将其作为函数执行以获取值时,会发生相同问题的不同实例。

回答这个问题:不,我认为 Breeze 不应该处理这个问题,至少不应该使用更丰富的错误消息。如果有的话,我认为如果您处于问题的情况下,有三种不同的选择:

    为您自己的代码使用 Typescript。如果DescriptionKnockoutObservable<string>,那么如果您为其分配常规字符串,编译器会报错。 在您自己的代码中或作为适当框架的补丁:将可观察对象的属性设置为只读。 Along these lines。但是,this method 依赖于浏览器支持... 切换到另一个没有这个“税”的 MVVM 框架。例如,AFAIK Angular 没有 Knockout 所具有的这种类似“功能”的设置;它使用常规属性。

但这只是我的 2 cts - 阅读我自己的答案,我确实觉得这(“情况 X 是否应该由框架 Y 处理?”)都是见仁见智的问题。

【讨论】:

原则上我认为您是正确的,因为在使用 Knockout 时重新分配变量是一个常见问题,但是我不同意对于大多数开发人员来说简单地切换到不同的框架是可行的。恰当的例子 - 许多开发人员使用敲除只是为了在现有 ASP.NET MVC 或 JSP 页面中进行数据绑定以扩充他们的页面。如您所知,Knockout不是一个完整的 MVC 框架,因此切换到 Angular 进行数据绑定相当于从 C# 切换到 Java,因此您不必使用 razor 引擎。我绝对喜欢选项 2,你在 KO github 上提出过这个吗? 我也喜欢选项 #2,因为它是一种通用解决方案。我们或许可以使用包括浏览器功能检测在内的实体自动为您执行此操作。我可以为此投票吗? 我确实担心这个权宜之计的性能成本。我们已经通过 observable 流血了一些性能。通过定义的属性 fn 运行是否会增加更多?在桌面上,可能不是。在安卓上?好吧,也许 Ko 本身并不是此类设备的最佳选择。在哪里了解更多信息? @PWKad 是的,如果您已经在使用(或想要使用)KO,则不要选择另一个框架(尤其是更大的框架)。但是,如果没有上下文,它一个选项(例如,如果您仍在决定一个框架,那么这个“税”是需要承认的)。 - 关于为 KO 提议 #2,我不确定它是针对我还是针对 OP。就个人而言,我们已经使用 Typescript,所以我们不必经常支付这种“税”。 就性能而言......只有一种方法可以知道。测量:D。

以上是关于使用敲除和微风时“TypeError:对象不是函数”的主要内容,如果未能解决你的问题,请参考以下文章

淘汰赛添加到数组的数组

Knockout JS映射插件没有初始数据/空表单

如何使用 jquery 将文本框设为只读 [重复]

使用 Jquery 加载填充 jquery ui 对话框时的敲除绑定

当微风子实体更新父实体状态不改变时

敲除加载和保存视图模型