MVC在ModelState.IsValid = false上设置Html.DropdownList

时间:2016-01-21 14:16:20

标签: c# asp.net-mvc html.dropdownlistfor modelstate dropdownlistfor

在保持可维护代码的同时,这一直困扰着我最好的方式。以下代码为付款网关表单设置了月份和年份列表,然后将其分配给List<SelectListItem>类型的变量。

初步行动

PayNowViewModel paymentGateway = new PayNowViewModel();
List<SelectListItem> paymentGatewayMonthsList = new List<SelectListItem>();
List<SelectListItem> paymentGatewayYearsList = new List<SelectListItem>();

for (int i = 1; i <= 12; i++)
{
    SelectListItem selectListItem = new SelectListItem();
    selectListItem.Value = i.ToString();
    selectListItem.Text = i.ToString("00");

    paymentGatewayMonthsList.Add(selectListItem);
}

int year = DateTime.Now.Year;
for (int i = year; i <= year + 10; i++)
{
    SelectListItem selectListItem = new SelectListItem();
    selectListItem.Value = i.ToString();
    selectListItem.Text = i.ToString("00");

    paymentGatewayYearsList.Add(selectListItem);
}

paymentGateway.ExpiryMonth = paymentGatewayMonthsList;
paymentGateway.ExpiryYear = paymentGatewayYearsList;

return View(paymentGateway);

它是一个相当多的代码,我发现自己重复这段代码,以类似的格式重新设置下拉列表选项,如果ModelState.IsValid为false并且我想返回视图用户纠正错误。

HttpPost操作 - 代码

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ConfirmPayment(PayNowViewModel paymentGatewayForm, FormCollection form)
{
    if (ModelState.IsValid)
    {
        // Post processing actions...
        return View();
    }
    else
    {
        for (int i = 1; i <= 12; i++)
        {
            SelectListItem selectListItem = new SelectListItem();
            selectListItem.Value = i.ToString();
            selectListItem.Text = i.ToString("00");

            paymentGatewayMonthsList.Add(selectListItem);
        }

        int year = DateTime.Now.Year;
        for (int i = year; i <= year + 10; i++)
        {
            SelectListItem selectListItem = new SelectListItem();
            selectListItem.Value = i.ToString();
            selectListItem.Text = i.ToString("00");

            paymentGatewayYearsList.Add(selectListItem);
        }

        form.ExpiryMonth = paymentGatewayMonthsList;
        form.ExpiryYear = paymentGatewayYearsList;

        return View("MakePayment", form);
    }
}

什么是集中这个下拉设置代码的最佳方式,因此它只在一个地方?目前你会看到很大比例(for循环),正好重复两次。有功能的基本控制器?或者像上面那样重新设置是否更好?

任何建议表示赞赏! 麦克

2 个答案:

答案 0 :(得分:1)

向您的控制器添加私有方法(以下代码假定您的ExpiryMonthExpiryYear属性为IEnumerable<SelectListItem>,这是DropDownListFor()方法所需的全部内容

private void ConfigureViewModel(PayNowViewModel model)
{
  model.ExpiryMonth = Enumerable.Range(1, 12).Select(m => new SelectListItem
  {
    Value = m.ToString(),
    Text = m.ToString("00")
  });
  model.ExpiryYear = Enumerable.Range(DateTime.Today.Year, 10).Select(y => new SelectListItem
  {
    Value = y.ToString(),
    Text = y.ToString("00")
  });
}

然后在GET方法中

public ActionResult ConfirmPayment()
{
  PayNowViewModel model = new PayNowViewModel();
  ConfigureViewModel(model);
  return View(model);
}

和POST方法

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ConfirmPayment(PayNowViewModel model)
{
  if (!ModelState.IsValid)
  {
    ConfigureViewModel(model);
    return View(model);
  }
  .... // save and redirect (should not be returning the view here)
}

答案 1 :(得分:0)

如果您的下拉选项集已修复(或在潜在选项更改后重新编译正常),您可以使用枚举来存储您的选项。

public enum Month {
    // if the dropdown is not required, add default value 0
    Optional = 0, 
    [Display(Name = @"Month_January")]
    January = 1,
    [Display(Name = @"Month_February")]
    February = 2,
    // etc ..
}

要将其呈现为下拉列表,请使用EditorTemplate Enum.cshtml

  @model Enum
  @{
      var enumType = ViewData.ModelMetadata.ModelType;
      var allValues = Enum.GetValues(enumType).Cast<object>().ToSelectList(Model);
      // read any attributes like [Required] from ViewData and ModelMetadata ...                
      var attributes = new Dictionary<string, object>();    
   }

    @Html.DropDownListFor(m => m, allValues, attributes)

ToSelectList扩展方法遍历所有枚举值并将它们转换为SelectListItems:

    public static IList<SelectListItem> ToSelectList<T>(this IEnumerable<T> list) {
        return ToSelectList<T>(list, list.FirstOrDefault());
    }

    public static IList<SelectListItem> ToSelectList<T>(this IEnumerable<T> list, T selectedItem) {
        var items = new List<SelectListItem>();
        var displayAttributeType = typeof(DisplayAttribute);

        foreach (var item in list) {
            string displayName;

            // multi-language: 
            // assume item is an enum value
            var field = item.GetType().GetField(item.ToString());
            try {
                // read [Display(Name = @"someKey")] attribute
                var attrs = (DisplayAttribute)field.GetCustomAttributes(displayAttributeType, false).First();
                // lookup translation for someKey in the Resource file
                displayName =  Resources.ResourceManager.GetString(attrs.Name);
            } catch {
                // no attribute -> display enum value name
                displayName = item.ToString();
            }

            // keep selected value after postback:
            // assume selectedItem is the Model passed from MVC
            var isSelected = false;
            if (selectedItem != null) {
                isSelected = (selectedItem.ToString() == item.ToString());
            }

            items.Add(new SelectListItem {
                Selected = isSelected,
                Text = displayName,
                Value = item.ToString()
            });
        }

        return items;
    }     

要支持多种语言,请为显示名称键添加翻译,例如"Month_January",到资源文件。

现在设置代码已经使用一些反射魔法抽象掉了,创建一个新的视图模型是一件轻而易举的事:&gt;

public class PayNowViewModel {
    // SelectListItems are only generated if this gets rendered
    public Month ExpiryMonth { get; set; }
}

// Intial Action
var paymentGateway = new PayNowViewModel();
return View(paymentGateway);

// Razor View: call the EditorTemplate 
@Html.EditorFor(m => m.ExpiryMonth)

请注意,在EditorTemplate中,Model作为选定项目传递给ToSelectList。回发后,Model将保留当前选定的值。因此,即使您在控制器中发生错误后返回模型,它也会保持选中状态:

// HttpPost Action
if (!ModelState.IsValid) {
    return View("MakePayment", paymentGatewayForm);
}

我们花了一些时间来提出这个解决方案,将信用转到Saratiba团队。