使用对象继承时的淘汰赛映射问题 [英] Problems with Knockout mapping when using object inheritance

查看:55
本文介绍了使用对象继承时的淘汰赛映射问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在使用带有子类对象的KO映射插件时得到一些奇怪的行为.

Getting some weird behaviour I don't quite get when using the KO mapping plugin with subclassed objects.

用例:

  • 服务器具有许多用于AJAX的简单模型类 API.

  • The server has a number of simple model classes used for the AJAX API.

我想生成一个生成KO可观察值的存根类 这些类的属性,因此,如果我更改服务器代码, 存根类自动更新为带有可观察到的新的 属性-我需要此属性,因此我可以创建全新的对象而无需从 现有数据.

I want to generate a stub class that generates KO observables for properties on these classes, so that if I change the server code the stub classes are automatically updated with an observable for the new property - I need this so I can create brand new objects without mapping from existing data.

然后,我希望能够创建具有以下内容的存根的子类: 针对用户界面,操作方法,客户端状态等进行计算.这些附加功能 属性不需要映射回服务器-实际上,它们不能 是,因为服务器模型将具有它们(否则它们将位于存根中)

I then want to be able to create a subclass of the stub that has e.g. computeds for the UI, action methods, client state etc. These additional properties do not need to be mapped back to the server - in fact they cannot be, as the server models will have them (or they'd be in the stub)

我无法正常工作.

这是一些缩减的代码:

function ItemModelBase(data, mapping)
{
    if(data)
        ko.mapping.fromJS(data, mapping, this);

    this.Name = this.Name || ko.observable();
}

function ItemModel(data, mapping)
{
    var _this = this;

    ItemModelBase.call(this, data, mapping);

    this.Salutation = ko.computed(function() { return 'Hello ' + _this.Name(); });
}

// Comment this out and it works
ItemModel.prototype = new ItemModelBase;

function Model()
{
    var _this = this;

    var _mapping = {
        'Items': {
            create: function(o) { return new ItemModel(o.data); }
        }
    };

    this.Items = ko.observableArray();

    this.load = function()
    {
        ko.mapping.fromJS({ 
            Items: [
                { Name: 'Aardvark' },
                { Name: 'Bat' },
                { Name: 'Cheetah' },
                { Name: 'Duiker' },
                { Name: 'Ocelot' }
            ]
        }, _mapping, _this);                          
    };
}

var model = new Model();
ko.applyBindings(model);
model.load(); 

使用视图:

<ul data-bind="foreach: Items">
    <li>
        <span data-bind="text: Name"></span>: 
        <span data-bind="text: Salutation"></span>
    </li>
</ul>

显示的内容是正确数量的项目的列表,但仅使用最后一项的数据-结果:

What this displays is a list of the correct number of items, but only using data from the last item - result:

  • 豹猫:你好豹猫
  • 豹猫:你好豹猫
  • 豹猫:你好豹猫
  • 豹猫:你好豹猫
  • 豹猫:你好豹猫

如果我不继承基础对象,即我注释掉ItemModel.prototype = new ItemModelBase;行,它将按预期工作.

If I don't subclass the base object, i.e. I comment out the ItemModel.prototype = new ItemModelBase; line, it works as expected.

我的主要语言是C#,我发现很难继承Java脚本的继承性;这里发生了什么?据我所知,没有原型就可以工作,因为基本构造函数调用只是A.N.其他向当前实例添加属性负载的JS调用.

My main language is C# and I find Javascript's inheritance very difficult to get my head round; what is going on here? As far as I can tell, without the prototype it works because the base constructor call is just A.N. Other JS call that is adding a load of properties to the current instance.

这适用于这种情况,但是大概意味着如果我在基类原型中添加一些常用功能,我将无法使用它们?

This works for this case, but presumably means if I add some common functions to the base class prototype I won't be able to use them?

我想我只需要回答一个主要问题,那就是:为什么只显示最后一个项目,我该怎么办?

I guess I need a main single question, so it is: Why is only the last item being displayed, and what do I do about it?

JFfiddle: https://jsfiddle.net/whelkaholism/d427mssa/

JFfiddle: https://jsfiddle.net/whelkaholism/d427mssa/

如果我包括继承,然后创建一个新模型,则该新模型也具有最后一项的属性.真的不明白,但是看起来它是在向原型而不是实例分配值?

If I include the inheritance and then create a new model, that new model has the properties of the last item too. REALLY don't understand that, but it looks like it's assigning values to the prototype not the instance?

console.log(ko.toJSON(new ItemModel()));

结果:

{"Name":"Ocelot","Salutation":"Hello Ocelot"}

Tomalak 发布了一个似乎已删除的答案,这很可惜,尽管我仍然不这样做.不知道发生了什么,它确实提供了解决方法.

Tomalak posted an answer which seems to have been deleted, which is a shame as although I still don't know what is going on, it did actually give a fix.

问题是这样的:this.Name = this.Name || ko.observable();

如果将可观察的定义移到映射上方,它将起作用:

If I move the observable definition above the mapping it works:

function ItemModelBase(data, mapping)
{
    this.Name = ko.observable();
    // Exhibits the same bug as original code
    // this.Name = this.Name || ko.observable();

    if(data)
        ko.mapping.fromJS(data, mapping, this);       
}

奇怪的是,即使我对JS的理解是,此时仍未定义this.Name,导致该语句的值为'ko.observable()'并且与没有代码的代码?

Oddly leaving the condition it results in the same bug, even though my understanding of JS was that this.Name would be undefined at that point, resulting that statement having the value of `ko.observable()' and being identical to the code without the cnodition?

推荐答案

请勿尝试在原型链的任何位置创建计算或可观察值.这是你可以做的和不能做的:

Don't attempt to create computeds or observables anywhere up the prototype chain. Here's what you can and can't do:

  • 您可以修改原型以在视图模型之间共享共享的,不可观察的(!)属性和方法.

  • You can modify the prototype to share share common, non-observable (!) properties and methods between viewmodels.

var common = {
    baz: function () {
        return ko.unwrap(this.bar) || ko.unwrap(this.foo);
    }
};

function VM1() {
    this.foo = ko.observable();
}
VM1.prototype = common;

function VM2() {
    this.foo = ko.observable();
    this.bar = ko.observable();
}
VM2.prototype = common;

var vm2 = new VM2();
vm2.foo("FOO");
vm2.baz();  // -> returns "FOO";

  • 您不能(阅读:不应)在原型链中拥有其他视图模型.由于所有原型本身都是活动对象实例,因此它们的所有可观察对象将在您的 actual 视图模型实例之间共享.这样做有意义的唯一情况是,如果您想在实例之间共享可订阅的(!)数据.

  • You can't (read: shouldn't) have other viewmodels in the prototype chain. Since all prototypes are living object instances themselves, all their observables would be shared between your actual viewmodel instances. The only situation where doing this would make sense is if you wanted to share subscribable (!) data between instances.

    function VM0() {
        this.count = ko.observable(0);
    }
    VM0.prototype.increment = function () {
        this.count(this.count() + 1);
    };
    
    function VM1() {
        this.foo = ko.observable();
        this.count.subscribe(function (newCount) {
            // count has increased across all VM1 instances
        });
    }
    VM1.prototype = new VM0();
    
    var vm1 = new VM1();
    vm1.inc();  // -> triggers subscription across all VM1 instances
    

  • 您可以使用函数将常见的独立功能(包括可观察的属性)附加到视图模型实例.基本上,这就是应用像ItemModelBase.call(this, ...);这样的其他构造函数所要做的.

  • You can use functions to attach common, self-contained functionality (including observables) to a viewmodel instance. That's basically what applying a different constructor like ItemModelBase.call(this, ...); would do.

    function VM1() {
        this.foo = ko.observable();
    }
    
    function VM2() {
        VM1.call(this);   // creates foo observable
        this.bar = ko.observable();
    }
    

  • 您不能在同一viewmodel实例上多次调用ko.mapping(即先映射基本属性,然后再映射特定属性).映射插件将其内部状态保留在实例变量本身中-第二轮运行将覆盖第一轮的状态. (当然,您仍然可以再次映射相同的属性列表,即更新它们的值.)

  • You cannot call ko.mapping more than once on the same viewmodel instance (i.e. first map basic properties, then specific ones). The mapping plugin keeps its internal state in an instance variable itself - a second run would overwrite the state of the first. (Of course you can still map the same list of properties a second time, i.e. update their values.)

    所有这些都为C#类型继承层次结构留了很少的空间.除非您具有共享的通用功能,否则不要在JS中构建视图模型层次结构.

    All of this leaves you with pretty little room for a C# type inheritance hierarchy. Unless you have common functionality to share, don't bother building a viewmodel hierarchy in JS.

    这篇关于使用对象继承时的淘汰赛映射问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆