将服务器视图模型强制转换为客户端的最佳方法 - Knockout

时间:2014-09-04 10:00:50

标签: asp.net-mvc knockout.js

我正在寻找将我的服务器模型映射到knockout viewmodel的最佳方法 为此,我在这里使用映射插件

Id,Title,People被映射到类似于我在这里的groupViewModel的东西, 我如何确定并强制将此映射始终精确地投射到新的groupViewModel,这里真正的问题是 Id,Title,People 绑定到视图,而不是 addPerson 方法, 请分享关于此的最佳解决方法,或者您知道的任何更好的方法,使其精确,简单,干净,感谢。

这里我们的viewModel包含Groups Array,如何将这个Groups数组转换为GroupViewModel项,也许问题可以用这种方式回答。

var model = @Html.Raw(Json.Encode(Model));

$(function() {

    var root = this;
    var viewModel = ko.mapping.fromJS(model, root);
    // there are Groups as expected but not the addPerson
    // will cause undefined exception

    var groupViewModel = function (item) {
        var self = this;
        self.Id = ko.observable(item.Id);
        self.Title = ko.observable(item.Title);
        self.People = ko.observableArray([]);
        self.addPerson = function (name) {
            console.log('addPerson Clicked');
        }; // .bind(self)
    }
    ko.mapping.fromJS(viewModel);
    ko.applyBindings(viewModel);
});     

编辑 - 根据答案更新:

服务器ViewModel

In Server we have this :

    class ServerVm {
        public int Id { get; set; }
        public IList<GroupMap> Groups { get; set; } // groupViewModel
}

在客户端我们有:

var model = @Html.Raw(Json.Encode(Model));

$(function() {

    var root = this;
    var viewModel = ko.mapping.fromJS(model, root);

    //viewModel.Groups = ko.observableArray([]);
    // No need because it's same as server model

    viewModel.addGroup = function (teamName) {
            console.log('addGroup Clicked');
    }; // .bind(self)       
    // addGroup is fine
    // it's defined here and working fine, no special mapping needed here

    // "model" contains something
    var groupViewModel = function (item) {
        var self = this;
        self.Id = ko.observable(item.Id);
        self.Title = ko.observable(item.Title);
        self.People = ko.observableArray([]);
        // self.addPerson - this is hidden in the "model"
        self.addPerson = function (name) {
            console.log('addPerson Clicked');
        }; // .bind(self)
    }
    ko.mapping.fromJS(viewModel);
    ko.applyBindings(viewModel);
});     

我们的问题是位于嵌套集合中的self.addPerson,因为容器(groupViewModel)不会自动绑定到GroupMap。我每次手动创建groupViewModel都可以,因为我自己投了它,但这不是真正的映射解决方案,你的是什么,谢谢

1 个答案:

答案 0 :(得分:1)

您可以使用来自documentation的3个参数的ko.mapping.fromJS方法的不同重载:

  

指定更新目标
  ... ko.mapping.fromJS的第三个参数表示目标    ko.mapping.fromJS(data,{},someObject);

因此,在您的情况下,您可以按如下方式更新视图模型定义:

function ViewModel() {
    var self = this;    
    this.addPerson = function(data) {
        $.ajax({
            url: ...+ self.Id,
            contentType: 'application/json;charset=utf-8',
            dataType: 'JSON',
            type: "POST",
            success: function (result) // result
            {
                console.log('Success');
                var avm = new childViewModel(result,self); // another defined vm
                self.People.push(avm);
            }
        });
    }
}
ViewModel.prototype.init = function(data) {
    var self = this;
    ko.mapping.fromJS(data, {}, self);
}

并初始化它:

...
var model = @Html.Raw(Json.Encode(Model));
...
var vm = new ViewModel();
vm.init(model);
ko.applyBindings(vm);

见工作demo

另一种缩短的方法是首先映射模型然后再添加方法,如下所示:

var vm = ko.mapping.fromJS(model);
vm.addPerson = function(data) {
...

查看另一个demo

我喜欢第一种方法,因为函数是在视图模型中定义的,后来没有添加。

<强>更新

因此,经过评论和问题更新后的一些澄清,以下是应该做的事情:

  1. 您应该在该子对象中使用提到的ko.mapping.fromJS方法自动映射它的属性。
  2. 您应该使用映射对象告诉mapping插件如何映射您的子对象。
  3. 子对象视图模型:

    function groupViewModel(data) {
        var self = this;
        ko.mapping.fromJS(data, {}, self);
        self.addPerson = function(personData) {
            // here should be your ajax request, I'll use dummy data again        
        };    
    }
    

    映射对象:

    var mapping = {
        "Groups" : {
            'create': function(options) {
                return new groupViewModel(options.data);
            }
        }
    };
    

    初始化:

    var vm = ko.mapping.fromJS(model, mapping);
    

    此处已更新demo