2

为了在客户端使用在服务器端编写的视图模型,我使用了 knockoutjs 映射插件。但在我看来,我并不想写任何 js 代码。出于这个原因,因此我无权访问Model,我现在通过 ajax 调用获取模型,下面代码中的属性中没有值。这段代码写在一个外部的 js 文件中:

var Person = function () {
    var self = this;


    self.SetupKOBindings = function (flagkey) {
        var source = null;
        if (flagkey.val() === "True") {
            this.GetViewModelFromServer = function () {
                $.ajax(
                    {
                        url: "/Person/LoadData",
                        type: "GET",
                        async: false
                    }).
                success(function (data) {
                    if (data !== null && data !== undefined) {
                        source = data;
                        flagkey.val("false");
                    }
                });
            }();
            return ko.mapping.fromJS(source);
        }
        return null;
    };

    self.ViewModel = function (flagkey) {
        this.model = self.SetupKOBindings(flagkey);

        this.model.FullName = ko.computed(function () {
            return this.model.FirstName() + " " + this.model.LastName();
        });

        this.model.ShouldShowFullName = ko.computed(function () {
            return (this.model.FirstName() === null || this.model.LastName() === null) ? false : true;
        });

        this.model.Save = function () {
            if ($(form).valid()) {
                $.ajax(
               {
                   url: "/Person/Index",
                   type: "POST",
                   contentType: "application/json",
                   data: ko.mapping.toJSON(model),
                   async: true
               }).
           success(function (data) {
               ko.mapping.fromJS(model, data);
           });
            }
        }

        return this.model;
    };

    self.ApplyKOBindings = function (vm) {
        ko.applyBindings(vm);
    };

    return this;
};

$(function () {
    var PersonPage = Person();
    var viewModel = PersonPage.ViewModel($('#GetViewModelFromServer'));
    if (viewModel !== null) PersonPage.ApplyKOBindings(viewModel);
});

我遇到这种方法的问题是每次我执行发布操作时,当页面加载时,会触发相同的 ajax 请求以从服务器获取视图模型,并且运行相同的代码,然后将表单与空的 vm 属性绑定。

为了避免这种情况,我使用隐藏控件的值作为是否将服务器端视图模型转换为 js 对象的标志。所以,在第一次调用时,我将 flag 的值设置为 false。

现在为了获得使用数据注释提到的验证消息,我将表单设置为部分视图,并使用 ajax 调用将内容替换为带有 id 作为示例的 div。使用不显眼的验证和服务器端验证的客户端验证效果非常好,淘汰绑定也是如此。

控制器代码:

 [HttpPost]
        public ActionResult Index(PersonViewModel viewModel)
        {
            if (viewModel.Age < 10)
            {
                ModelState.AddModelError("Age", "bal bla bla");
            }
            if (ModelState.IsValid)
            {
                return PartialView("_Form", viewModel);
            }
            else
            {
                viewModel.GetViewModelFromServer = false;
                return PartialView("_Form", viewModel);
            }

        }

索引视图:

<div id="sample">
    @Html.Partial("_Form")
</div>

局部视图:

@model MVCKOPractise.Models.PersonViewModel
<fieldset>
    <legend>PeopleViewModel</legend>

        @using (Ajax.BeginForm("Index", "Person", new AjaxOptions() { InsertionMode = InsertionMode.Replace, HttpMethod = "POST", UpdateTargetId = "sample" }))
        {
            <div>
                First Name:
            @Html.TextBoxFor(model => model.FirstName, new { data_bind = "value: FirstName,valueUpdate:['afterkeydown','propertychange','input']" })<br />
                @Html.ValidationMessageFor(model => model.FirstName)
            </div>
            <div>
                Last Name:
                    @Html.TextBoxFor(model => model.LastName, new { data_bind = "value: LastName,valueUpdate:['afterkeydown','propertychange','input']" })<br />
                @Html.ValidationMessageFor(model => model.LastName)
            </div>
            <div data-bind="visible: ShouldShowFullName">
                Full Name:
                   <div data-bind="text: FullName"></div>
            </div>
            <div>
                Address:
                    @Html.TextBoxFor(model => model.Address, new { data_bind = "value: Address" })<br />
                @Html.ValidationMessageFor(model => model.Address)
            </div>
            <div>
                Age:
                    @Html.TextBoxFor(model => model.Age, new { data_bind = "value: Age" })<br />
                @Html.ValidationMessage("Age")
            </div>
            @Html.HiddenFor(model => model.GetViewModelFromServer)
            <input type="submit" value="Save" />
        }
</fieldset>

虽然上面的小样本对我有用,但我想知道这是继续进行的好方法

4

1 回答 1

0

我想我理解你想要达到的目标,并且过去做过类似的事情。

首先,我不确定你的模型数据来自哪里——显然是服务器端,但这总是一个空白的视图模型吗?就我个人而言,我已经按照这些思路实现了一些东西来检索服务器端数据:

在控制器中:

[HttpGet]
public ActionResult MyAction(int id)
{
    Return View();
}

[HttpGet]
public JsonResult MyActionData(int id)
{
    return Json(Something.GetMyData(id) ?? new MyViewModel(), AllowGet);
}

这使我可以像通常使用标准 MVC 请求一样检索页面的数据,但允许它以 JSON 格式请求。通过这种方式,我要么得到我正在查询的任何数据,要么得到一个空白视图模型。

然后,您可以使用 viewmodel 作为参数添加标准的 post 操作,而不必担心 ajax 方面的任何特殊情况。

其次,为了处理仅服务器端的验证(我在这里假设您的客户端工作正常,但服务器端有更复杂的规则需要回发)我实际上开始在 ModelState 中返回验证数据回到客户端。

不确定在您的场景中最好的传输方式是什么(用于验证的专用 post 方法或带有验证数据的视图模型包装器),但无论哪种方式,验证对象本身都会提供验证失败的字段名称和相关消息显示。

使用该数据,您可以创建一个自定义剔除映射处理程序,该处理程序获取验证数据并将其计算回模型上的验证属性(请记住字段名称直接映射到相关对象结构),或者您可以拥有一个 jQuery 处理程序遍历结果以查找具有相关名称的字段并动态添加验证类。

于 2014-01-22T11:32:05.903 回答