Knockout Mapping - 如何处理新对象的创建?

时间:2014-07-30 02:19:46

标签: knockout.js

在我的应用程序中,我有一个来自服务器的Books列表。

我使用ko.mapping为从Book AJAX调用点击客户端的每个GetBooks生成可观察属性。

这很有效 - 映射正在处理我必须手动处理的许多“噪音”。

但是说我想创建一个新的Book。我一直养成为所有模型创建具有空属性的对象的习惯 - 所以对于book,我有类似的东西:

var defaultBook = {
   title: null,
   price: null,
   genre: null,
   pages: null
}  

然后,当我创建新Book时,我只需ko.mapping.fromJS(defaultBook)applyBindings到我的add表单。

这是新对象创建的标准方法 - 在客户端的某处保留空模型的集合吗?我觉得这是一个愚蠢的问题但只是想确保我不会错过一个更好,更标准的模式。这让我很痛苦,因为这意味着如果我在服务器上更改属性名称,我需要在我的客户端模型定义上更改它......但也许这是不可避免的。

2 个答案:

答案 0 :(得分:0)

我昨天遇到了这个问题

MyApp.PersonNameEditViewModel = MyApp.define({
    init: function (data) {
        var fields = {
            nameTypeId: null,
            personNameId: null, 
            personId: null,
            firstname: "",
            prefix: "",
            surname: ""
        }

        this.nameTypeId = null;
        this.personNameId = null;
        this.firstname = ko.observable().extend({ required: true });

        ko.utils.extend(fields, data);
        ko.mapping.fromJS(fields, {}, this);

        this.name = ko.computed(this.getName, this);
        this.title = this.name;
        this.new = this.personNameId === null;
    },
    prototype: {
        getName: function () {
            var parts = [this.surname(), this.firstname(), this.prefix()];
            return ko.utils.arrayFilter(parts, function(part) {
                return part != "";
            }).join(", ") || "Untitled";
        }
    }
});

我正在使用发送到构造函数

的数据扩展fields对象
ko.utils.extend(fields, data);

这种方式既可以用于新添加的行,也可以用于后端的现有行。我从映射插件

中使用它
var mapping = {
    personNames: {
        create: function (options) {
            return new MyApp.PersonNameEditViewModel(options.data);
        }
    }
}

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

创建新行时

newAlternativname: function () {
    new MyApp.DetailsEditViewModel(new MyApp.PersonNameEditViewModel({ personId: this.personId(), nameTypeId: MyApp.NameTypes.AlternativeName }), true, this.personNames).modal();
}

正如您所看到的,我将一些内容作为数据发送,但在添加新行时并非全部,ko.utils.extend确保通过ko.mapping.fromJS添加和映射缺少的字段

答案 1 :(得分:0)

虽然@Anders的答案是好的和正确的(你应该在客户端这样做),但它只涉及客户端和TO询问服务器端的模型更改。这是我在类似情况下所做的。我为StringBuilder创建了一个扩展方法,用于构建javascript变量:

public static class ScriptBuilder
{
    public static StringBuilder BuildGlobalVariable(this StringBuilder sb, string name, object value)
    {
        return sb.AppendFormat("var {0} = {1};", name, 
            JsonConvert.SerializeObject(value, new JsonSerializerSettings()
            {
                ContractResolver = new CamelCasePropertyNamesContractResolver()
            }));
    }
}

CamelCasePropertyNamesContractResolver()仅用于以小写形式创建属性名称,以遵循javascript表示法。然后在我的JsModelsController中我有这个方法:

public class JsModelsController : Controller
{
    public JavaScriptResult ServerModels()
    {
        var sb = new StringBuilder();
        return sb.BuildGlobalVariable("Book", new Book()).ToString();
    }
}

书籍在哪里:

public class Book
{
    public string Title { get; set; }
    public string Price { get; set; }
    public string Genre { get; set; }
    public int? Pages { get; set; }
}

所以现在你只需要在_Layout.cshtml页面的某处添加一个脚本标签,如下所示:

<script src="JsModels/ServerModels"></script>

希望它应该在您的页面上加载以下脚本:

var Book = {&#34; title&#34;:null,&#34; price&#34;:null,&#34; genre&#34;:null,&#34; pages&#34;:null };

P.S。我知道创建全局JS对象是一种不好的做法,因此您可以将所有服务器模型累积到一个JS全局对象中,如下所示:

return sb.BuildGlobalVariable("ServerModels", new 
{
    Book = new Book(),
    Person = new Person()
}).ToString();

Person是另一个服务器类。这也不完美,但允许只有一个全局JS变量,其中包含所有服务器模型。