在保持可维护代码的同时,这一直困扰着我最好的方式。以下代码为付款网关表单设置了月份和年份列表,然后将其分配给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循环),正好重复两次。有功能的基本控制器?或者像上面那样重新设置是否更好?
任何建议表示赞赏! 麦克
答案 0 :(得分:1)
向您的控制器添加私有方法(以下代码假定您的ExpiryMonth
和ExpiryYear
属性为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团队。