Asp.net mvc 4 将下拉列表中的选定项传递到viewmodel

Asp.net mvc 4 将下拉列表中的选定项传递到viewmodel,asp.net-mvc-4,knockout.js,viewmodel,html-select,Asp.net Mvc 4,Knockout.js,Viewmodel,Html Select,我在页面上有四个控件,一个简单的表单,上面有名字和姓氏、出生日期和这个下拉列表,其中包含一些国家的名称。当我对这些控件进行更改时,我能够在我的viewModel中看到这些更改,这些更改在下面的SavePersonDetails文章中作为参数传入,但我从未在该视图模型中看到LocationId更新,我不知道为什么 这是我的标记Index.cshtml中的内容: @model Mvc4withKnockoutJsWalkThrough.ViewModel.PersonViewModel @using

我在页面上有四个控件,一个简单的表单,上面有名字和姓氏、出生日期和这个下拉列表,其中包含一些国家的名称。当我对这些控件进行更改时,我能够在我的viewModel中看到这些更改,这些更改在下面的SavePersonDetails文章中作为参数传入,但我从未在该视图模型中看到LocationId更新,我不知道为什么

这是我的标记Index.cshtml中的内容:

@model Mvc4withKnockoutJsWalkThrough.ViewModel.PersonViewModel
@using System.Globalization
@using Mvc4withKnockoutJsWalkThrough.Helper

@section styles{
@Styles.Render("~/Content/themes/base/css")
<link href="~/Content/Person.css" rel="stylesheet" />
}

@section scripts{
@Scripts.Render("~/bundles/jqueryui")
<script src="~/Scripts/knockout-2.1.0.js"></script>
<script src="~/Scripts/knockout.mapping-latest.js"></script>
<script src="~/Scripts/Application/Person.js"></script>
<script type="text/javascript">
    Person.SaveUrl = '@Url.Action("SavePersonDetails", "Person")';
    Person.ViewModel = ko.mapping.fromJS(@Html.Raw(Json.Encode(Model)));
    var userObject =  '@Html.Raw(Json.Encode(Model))';
    var locationsArray =  '@Html.Raw(Json.Encode(Model.Locations))';
    var vm = {
        user : ko.observable(userObject),
        availableLocations: ko.observableArray(locationsArray)
    };
    ko.applyBindings(vm);
</script>
}
<form>
<p data-bind="with: user">
        Your country:
        <select data-bind="options: $root.availableLocations, optionsText: 'Text', optionsValue: 'Value', value: LocationID, optionsCaption: 'Choose...'">
        </select>
    </p>
</form>
最后,这是我的Person.js文件,我正在使用knockout

var Person = {

PrepareKo: function () {
    ko.bindingHandlers.date = {
        init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
            element.onchange = function () {
                var observable = valueAccessor();
                observable(new Date(element.value));
            }
        },
        update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
            var observable = valueAccessor();
            var valueUnwrapped = ko.utils.unwrapObservable(observable);
            if ((typeof valueUnwrapped == 'string' || valueUnwrapped instanceof String) && valueUnwrapped.indexOf('/Date') === 0) {
                var parsedDate = Person.ParseJsonDate(valueUnwrapped);
                element.value = parsedDate.getMonth() + 1 + "/" + parsedDate.getDate() + "/" + parsedDate.getFullYear();
                observable(parsedDate);
            }
        }
    };
},

ParseJsonDate: function (jsonDate) {
    return new Date(parseInt(jsonDate.substr(6)));
},

BindUIwithViewModel: function (viewModel) {
    ko.applyBindings(viewModel);
},

EvaluateJqueryUI: function () {
    $('.dateInput').datepicker();
},

RegisterUIEventHandlers: function () {

    $('#Save').click(function (e) {

        // Check whether the form is valid. Note: Remove this check, if you are not using HTML5
        if (document.forms[0].checkValidity()) {

            e.preventDefault();

            $.ajax({
                type: "POST",
                url: Person.SaveUrl,
                data: ko.toJSON(Person.ViewModel),
                contentType: 'application/json',
                async: true,
                beforeSend: function () {
                    // Display loading image
                },
                success: function (result) {
                    // Handle the response here.
                    if (result > 0) {
                        alert("Saved");
                    } else {
                        alert("There was an issue");
                    }

                },
                complete: function () {
                    // Hide loading image.
                },
                error: function (jqXHR, textStatus, errorThrown) {
                    // Handle error.
                }
            });
          }
      });
  },
};

$(document).ready(function () {
  Person.PrepareKo();
  Person.BindUIwithViewModel(Person.ViewModel);
  Person.EvaluateJqueryUI();
  Person.RegisterUIEventHandlers();
});
如您所见,我在页面中有数据,但没有一个显示为选中


您的解决方案过于复杂,会导致数据出现某些奇怪的情况。不要试图修补泰坦尼克号,最好的办法是重新开始并简化:

页面的模型包含所需的所有信息。不需要尝试创建两个完全独立的视图模型来处理用户数据和位置。使用mapping插件,您可以为主视图模型中的各种对象指定不同的视图模型,并且有一个更简单的模式可以用来设置所有这些。下面是我要做的:

// The following goes in external JS file

var PersonEditor = function () {
    var _init = function (person) {
        var viewModel = PersonEditor.PersonViewModel(person);
        ko.applyBindings(viewModel);
        _wireEvents(viewModel);
    }

    var _wireEvents = function (viewModel) {
        // event handlers here
    }

    return {
        Init: _init
    }
}();

PersonEditor.PersonViewModel = function (person) {
    var mapping = {
        'Locations': {
            create: function (options) {
                return new PersonEditor.LocationViewModel(options.data)
            }
        }
    }
    var model = ko.mapping.fromJS(person, mapping);

    // additional person logic and observables

    return model;
}

PersonEditor.LocationViewModel = function (location) {
    var model = ko.mapping.fromJS(location);

    // additional location logic and observables

    return model;
}

// the following is all you put on the page

<script src="/path/to/person-editor.js"></script>
<script>
    $(document).ready(function () {
        var person = @Html.Raw(@Json.Encode(Model))
        PersonEditor.Init(person);
    });
</script>
然后,将选择列表绑定到位置数组所需的全部内容是:

<p>
    Your country:
    <select data-bind="options: Locations, optionsText: 'Text', optionsValue: 'Value', value: LocationId, optionsCaption: 'Choose...'">
    </select>
</p>

根据您更新的问题,执行此操作

$('#Save').click(function (e) {
    var updatedUser = Person.ViewModel.user();
    updatedUser.Locations.length = 0; // not mandatory, but we don't need to send extra payload back to server.
    // Check whether the form is valid. Note: Remove this check, if you are not using HTML5
    if (document.forms[0].checkValidity()) {
        e.preventDefault();
        $.ajax({
            type: "POST",
            url: Person.SaveUrl,
            data: ko.toJSON(updatedUser), // Data Transfer Object
            contentType: 'application/json',
            beforeSend: function () {
                // Display loading image
            }
        }).done(function(result) {
            // Handle the response here.
            if (result > 0) {
                alert("Saved");
            } else {
                alert("There was an issue");
            }
        }).fail(function(jqXHR, textStatus, errorThrown) {
            // Handle error.
        }).always(function() {
            // Hide loading image.
        });
    }
});
1.实际上,我们不需要地点。它已经在用户中了。我很傻 2.在Index.cshtml上,按如下方式更改JavaScript页面

var userObject = @Html.Raw(Json.Encode(Model)); // no need for the quotes here
jQuery(document).ready(function ($) {
    Person.PrepareKo();
    Person.EvaluateJqueryUI();
    Person.RegisterUIEventHandlers();
    Person.SaveUrl = "@Url.Action("SavePersonDetails", "Knockout")";
    Person.ViewModel = {
        user : ko.observable(userObject)
    };
    Person.BindUIwithViewModel(Person.ViewModel);
});
3.在Person.js页面上的RegisterUIEventHandlers保存按钮内单击事件,执行此操作

$('#Save').click(function (e) {
    var updatedUser = Person.ViewModel.user();
    updatedUser.Locations.length = 0; // not mandatory, but we don't need to send extra payload back to server.
    // Check whether the form is valid. Note: Remove this check, if you are not using HTML5
    if (document.forms[0].checkValidity()) {
        e.preventDefault();
        $.ajax({
            type: "POST",
            url: Person.SaveUrl,
            data: ko.toJSON(updatedUser), // Data Transfer Object
            contentType: 'application/json',
            beforeSend: function () {
                // Display loading image
            }
        }).done(function(result) {
            // Handle the response here.
            if (result > 0) {
                alert("Saved");
            } else {
                alert("There was an issue");
            }
        }).fail(function(jqXHR, textStatus, errorThrown) {
            // Handle error.
        }).always(function() {
            // Hide loading image.
        });
    }
});
5.最后,我们的加价

<p data-bind="with: user">
    Your country:
    <select data-bind="options: Locations, 
                optionsText: 'Text', 
                optionsValue: 'Value', 
                value: LocationId, 
                optionsCaption: 'Choose...'">
    </select>
</p>
另一方面

jqXHR.success、jqXHR.error和jqXHR.complete回调是 从jQuery 1.8起已弃用。为它们的最终应用准备代码 删除时,请改用jqXHR.done、jqXHR.fail和jqXHR.always


非常感谢你的建议。我相信它工作得很好,但这需要一些重新工作,还有一些地方我不清楚。我对MVC和jQuery的理解是有限的,所以我只希望从下拉列表中获取值并更新ViewModel。你能提供一个建议吗?我尝试了以下方法:BindUIwithViewModel:function viewModel{ko.applyBindingsviewModel.LocationId,document.getElementByIdlocation_list.selectedIndex.text;ko.applyBindingsviewModel;}您的保存操作完全相同。序列化视图模型时,通过将select的值绑定到LocationId,它将被适当地设置并传递回post操作。我根本不想工作。我注意到BindUIwithViewModel在文档准备就绪时运行,因此我没有机会为LocationId赋值。我还尝试在我的ajax帖子的beforeSend中分配一个值,但实际上从未分配该值。我真的很迷茫,我相信这很简单,但我只是被卡住了。
<p data-bind="with: user">
    Your country:
    <select data-bind="options: Locations, 
                optionsText: 'Text', 
                optionsValue: 'Value', 
                value: LocationId, 
                optionsCaption: 'Choose...'">
    </select>
</p>