C# 动态更新视图后更新ViewModel

C# 动态更新视图后更新ViewModel,c#,asp.net-mvc-5,viewmodel,C#,Asp.net Mvc 5,Viewmodel,我试图定义忠实地表示视图的视图模型(以严格使用该概念) ViewModel的某些元素会动态更新。我遇到的问题是,当我发布文章时,ViewModel返回时没有动态更新的元素 在执行事件时,通过jQuery完成更新。通过Url.action调用操作,并更新Div 我举了一个例子来说明这个情况。仅存储位置(州和城市)的应用程序。为此,我有三个视图模型:一个表示SelectList中的州,一个表示SelectList中的城市,最后一个表示位置(由我首先提到的两个视图模型组成) 型号: public cl

我试图定义忠实地表示视图的视图模型(以严格使用该概念)

ViewModel的某些元素会动态更新。我遇到的问题是,当我发布文章时,ViewModel返回时没有动态更新的元素

在执行事件时,通过jQuery完成更新。通过Url.action调用操作,并更新Div

我举了一个例子来说明这个情况。仅存储位置(州和城市)的应用程序。为此,我有三个视图模型:一个表示SelectList中的州,一个表示SelectList中的城市,最后一个表示位置(由我首先提到的两个视图模型组成)

型号:

public class State
{
    public int Id { get; set; }
    [Required]
    public string Name { get; set; }
}

public class City
{
    public int Id { get; set; }
    [Required]
    public string Name { get; set; }
    [Required]
    public int StateId { get; set; }
    public virtual State State { get; set; }
}
视图模型:

public class CitySelectListViewModel
{
    public CitySelectListViewModel() { }
    public CitySelectListViewModel(IEnumerable<Models.City> cities)
    {
        this.Cities = cities;
    }
    [Display(Name = "Cities")]
    [Required]
    public int? SelectedCityId { get; set; }
    public IEnumerable<City> Cities { get; }
}

public class StateSelectListViewModel
{
    public StateSelectListViewModel() { }
    public StateSelectListViewModel(IEnumerable<State> states)
    {
        this.States = states;
    }
    [Display(Name = "States")]
    [Required]
    public int? SelectedStateId { get; set; }
    public IEnumerable<State> States { get; }
}

public class LocationCreateViewModel
{
    public LocationCreateViewModel() { }
    public LocationCreateViewModel(ICollection<State> states)
    {
        this.StateSelectListViewModels = new StateSelectListViewModel(states);
        this.CitySelectListViewModel = new CitySelectListViewModel();
    }
    public StateSelectListViewModel StateSelectListViewModels { set; get; }
    public CitySelectListViewModel CitySelectListViewModel { set; get; }
}
创建[视图]:

@model ViewModelExample.ViewModels.LocationCreateViewModel
....
@using (Html.BeginForm()) 
{
    @Html.AntiForgeryToken()
    <div class="form-horizontal">
        <h4>State</h4>
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        <div class="form-group">
            @Html.LabelFor(model => model.StateSelectListViewModels.SelectedStateId, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.DropDownListFor(model => model.StateSelectListViewModels.SelectedStateId, new SelectList(Model.StateSelectListViewModels.States, "Id", "Name"), "Select a State", htmlAttributes: new { @class = "form-control", @id = "StateSelectList" })
                @Html.ValidationMessageFor(model => model.StateSelectListViewModels.SelectedStateId, "", new { @class = "text-danger" })
            </div>
        </div>

        <div id="CityContainer">
            @Html.Action("CitySelectList")
        </div>

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </div>
    </div>
}

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
    <script type="text/javascript">
        $(function () {
            // Fill City DropDownList
            $('#StateSelectList').change(function () {
                var selectedStateId = this.value;
                $('#CityContainer').load('@Url.Action("CitySelectList")?stateId=' + selectedStateId);
            });
        });
    </script>
}
@model-viewmodel-example.ViewModels.LocationCreateViewModel
....
@使用(Html.BeginForm())
{
@Html.AntiForgeryToken()
陈述
@Html.ValidationSummary(true,“,new{@class=“text danger”})
@LabelFor(model=>model.StateSelectListViewModels.SelectedStateId,htmlAttributes:new{@class=“control label col-md-2”})
@Html.DropDownListFor(model=>model.StateSelectListViewModels.SelectedStateId,新建SelectList(model.StateSelectListViewModels.States,“Id”,“Name”),“选择一个状态”,htmlAttributes:new{@class=“form control”,@Id=“StateSelectList”})
@Html.ValidationMessageFor(model=>model.StateSelectListViewModels.SelectedStateId,“,新{@class=“text danger”})
@Html.Action(“城市选择列表”)
}
@节脚本{
@Scripts.Render(“~/bundles/jqueryval”)
$(函数(){
//填写城市下拉列表
$('#StateSelectList')。更改(函数(){
var selectedStateId=this.value;
$('#CityContainer').load('@Url.Action(“CitySelectList”)?stateId='+selectedStateId);
});
});
}
城市选择列表[视图]:

@model ViewModelExample.ViewModels.CitySelectListViewModel
....
<div class="form-group">
    @Html.LabelFor(model => model.SelectedCityId, htmlAttributes: new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        @Html.DropDownListFor(model => model.SelectedCityId, new SelectList(Model.Cities, "Id", "Name"), "Select a City", htmlAttributes: new { @class = "form-control" })
        @Html.ValidationMessageFor(model => model.SelectedCityId, "", new { @class = "text-danger" })
    </div>
</div>
@model-viewmodel-example.ViewModels.CitySelectListViewModel
....
@LabelFor(model=>model.SelectedCityId,htmlAttributes:new{@class=“controllabel col-md-2”})
@DropDownListFor(model=>model.SelectedCityId,new SelectList(model.Cities,“Id”,“Name”),“选择一个城市”,htmlAttributes:new{@class=“form control”})
@Html.ValidationMessageFor(model=>model.SelectedCityId,“,new{@class=“text danger”})
我将展示我的示例的执行情况,我将通过检查我在发布后收到的ViewModel来展示问题:

  • 我选择一个州和一个城市,然后按Create。

  • 我检查了发布后收到的ViewModel。我们可以看到CitySelectListViewModel是如何为空的,我想要的是通过jQuery更新的最后一个ViewModel

  • 我承认我提供了一个很长的例子,但这是我找到的唯一解释我需要什么的方法。提前谢谢


    这是因为在替换
    的内部HTML时,您在
    创建
    操作中阻止modelBinder准确绑定到
    位置CreateViewModel
    (这是您使用
    $('.\CityContainer')。加载(…
    )。您指示model binder绑定到 @模型ViewModelExample.ViewModels.CitySelectListViewModel,因此您可以获得城市选择列表的以下HTML:

    解决此问题的一种方法是将
    CitySelectList.cshtml
    修改为:

    @model ViewModelExample.ViewModels.LocationCreateViewModel
    
    @{
        Layout = null;
    }
    
    <div class="form-group">
        @Html.LabelFor(model => model.CitySelectListViewModel.SelectedCityId, 
    htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.DropDownListFor(model => 
    model.CitySelectListViewModel.SelectedCityId, new 
    SelectList(Model.CitySelectListViewModel.Cities, "Id", "Name"), "Select a City", htmlAttributes: new { @class = "form-control" })
            @Html.ValidationMessageFor(model => model.CitySelectListViewModel.SelectedCityId, "", new { @class = "text-danger" })
        </div>
    </div>
    

    但我也推荐。

    你能将其存储在隐藏字段中吗?@KevinCook在这种情况下不能。我有大约2400条记录。我需要应用程序尽可能轻。(感谢你的快速响应!)您是否考虑过将记录存储在缓存中?@kevinbook出于我前面提到的相同原因,我希望发送尽可能少的数据。是否无法动态更新ViewModel?因为您的第二个下拉列表具有
    name=“SelectedCityId”
    ,但它必须是
    name=“CitySelectListViewModel.SelectedCityId”
    以绑定到您的模型。但是您采取了错误的方法并使其过于复杂。还有其他问题导致代码无法正常工作,包括在返回视图时丢失了第二个dropdownlist中的数据。ModelState
    无效。请参阅如何正确实现级联dropdownlist汉克斯,这就解决了我的问题。我发现自己有另一个问题,我不想重复我自己。我想制作可重用的组件,你提到的策略迫使我重复视图和控制器。现在我正在研究你向我建议的自定义视图模型。如果你有更多的想法来实现我想要的r、 我都听到了。再次感谢。好吧,自定义模型绑定可以防止您重复自己的操作,但我不认为我会建议为项目中的每个模型创建自定义绑定。也许您应该检查viewmodels模型,并以有助于绑定的方式设计它们。另一种方法可能是@kevincook建议的,您可以在一个隐藏字段中重新选择
    selectedCityId
    的选定值,该字段的
    name
    属性将使其可访问您尝试绑定的模型的属性。解决此问题的方法有很多,可能是其中之一。但这一切都取决于您项目的整体情况。
    @model ViewModelExample.ViewModels.LocationCreateViewModel
    
    @{
        Layout = null;
    }
    
    <div class="form-group">
        @Html.LabelFor(model => model.CitySelectListViewModel.SelectedCityId, 
    htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.DropDownListFor(model => 
    model.CitySelectListViewModel.SelectedCityId, new 
    SelectList(Model.CitySelectListViewModel.Cities, "Id", "Name"), "Select a City", htmlAttributes: new { @class = "form-control" })
            @Html.ValidationMessageFor(model => model.CitySelectListViewModel.SelectedCityId, "", new { @class = "text-danger" })
        </div>
    </div>
    
     public ActionResult CitySelectList(int? stateId)
        {
            LocationCreateViewModel locationCreateViewModel = new LocationCreateViewModel();
            locationCreateViewModel.CitySelectListViewModel = new CitySelectListViewModel(db.Cities.Where(c => c.StateId == stateId).ToList());
    
            return View(locationCreateViewModel);
        }