动态更新视图后更新ViewModel

时间:2017-12-13 15:44:09

标签: c# asp.net-mvc-5 viewmodel

我正在尝试定义忠实代表视图的ViewModel(严格使用该概念)。

ViewModel的一些元素是动态更新的。我遇到的问题是,当我执行Post时,ViewModel将返回没有动态更新的元素。

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

我举了一个例子来澄清这个场景。仅存储位置(州和城市)的应用程序。为此,我有三个ViewModel:一个用于表示SelectList中的状态,一个用于表示SelectList中的Cities,最后一个用于表示Location(由我首先提到的两个ViewModel形成)。

型号:

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; }
}

的ViewModels:

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; }
}

位置[控制器]:

public class LocationController : Controller
{
    private DALDbContext db = new DALDbContext();

    // GET: Location/Create
    public ActionResult Create()
    {
        LocationCreateViewModel locationCreateViewModel = new LocationCreateViewModel(db.States.ToList());
        return View(locationCreateViewModel);
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create(LocationCreateViewModel pLocationCreateViewModel)
    {
        if (ModelState.IsValid)
        {
            //db.States.Add(state);
            //db.SaveChanges();
            return RedirectToAction("Index", "Home");
        }
        LocationCreateViewModel locationCreateViewModel = new LocationCreateViewModel(db.States.ToList());
        return View(locationCreateViewModel);
    }

    public ActionResult CitySelectList(int? stateId)
    {
        CitySelectListViewModel citySelectListViewModel = new CitySelectListViewModel(db.Cities.Where(c => c.StateId == stateId).ToList());
        return View(citySelectListViewModel);
    }
}

创建[查看]:

@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>
}

CitySelectList [查看]:

@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>

我将展示我的示例的执行情况,我将通过检查Post后收到的ViewModel来显示问题:

  1. 我选择了一个州和一个城市,然后按下创建。 Example image Number 1

  2. 我检查Post后收到的ViewModel。我们可以看到CitySelectListViewModel是如何为null,我想要的是带来通过jQuery更新的最后一个ViewModel

  3. Example image Number 2

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

    VS-Project of the example

1 个答案:

答案 0 :(得分:1)

这是因为在替换LocationCreateViewModel的内部HTML时,你阻止了modelBinder准确地绑定到Create操作中的<div id="CityContainer">(这就是你对{{1}所做的事情})。您指示模型绑定器绑定到 @model ViewModelExample.ViewModels.CitySelectListViewModel,因此您可以获得城市选择列表的HTML:

enter image description here

解决此问题的一种方法是将$('#CityContainer').load(...修改为:

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> 操作:

CitySelectList

但我也会推荐custom model binding