Publicar una lista desplegable desde la vista de tipo fuerte
Esto está relacionado con una pregunta anterior sobre la vinculación de una lista desplegable de una base de datos. La lista desplegable se está vinculando y completando, pero arroja un error "Referencia de objeto no configurada para una instancia de un objeto" en el navegador al enviar el formulario.
Al depurar, agregué una observación rápida en el parámetro m => m.SelectedDepartment del método auxiliar Html.DropDownListFor (...) y obtuve: "No se puede convertir la expresión lambda al tipo 'objeto' porque no es un tipo delegado" .
Ver:
@model BudgetDemo.Models.BudgetsActualsViewModel
@using (Html.BeginForm("GetBudgetsActuals", "BudgetsActuals", FormMethod.Post))
{
@Html.DropDownListFor(m => m.SelectedDepartment, Model.Departments,
"Select Department", new { @class = "form-control" })
}
ViewModel:
public class BudgetsActualsViewModel
{
[Display(Name = "Cost Center/Department")]
[Required(ErrorMessage = "Cost Center/Department is required.")]
[StringLength(62)]
public string SelectedDepartment { get; set; }
public List<SelectListItem> Departments { get; set; }
}
Controlador:
// GET: Render view with dropdowns
public ActionResult GetBudgetsActuals()
{
repo = new BudgetDemoRepository();
ModelState.Clear();
try
{
return View(repo.GetBudgetsActuals());
}
catch
{
return View("Error");
}
}
Repositorio:
public BudgetsActualsViewModel GetBudgetsActuals()
{
...
BudgetsActualsViewModel budgetsActuals = new BudgetsActualsViewModel()
{ Departments = new List<SelectListItem>() };
// Query returning correct data from DB here
for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
{
budgetsActuals.Departments.Add(
new SelectListItem
{
Text = ds.Tables[0].Rows[i]["Department"].ToString(),
Value = ds.Tables[0].Rows[i]["ListValue"].ToString()
}
);
}
return budgetsActuals;
}
ACTUALIZAR
Está funcionando ahora. El siguiente código se actualizó / agregó para volver a renderizar el formulario después de una devolución de datos con un mensaje de éxito que contiene el valor del departamento seleccionado.
Ver:
@model BudgetDemo.Models.BudgetsActualsViewModel
@Html.AntiForgeryToken()
@using (Html.BeginForm("GetBudgetsActuals", "BudgetsActuals",
FormMethod.Post))
{
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@if (TempData["SuccessMessage"] != null)
{
<p class="alert alert-success"
id="successMessage">@TempData["SuccessMessage"]</p>
}
@Html.DropDownListFor(m => m.SelectedDepartment, Model.Departments,
"Select Department", new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.SelectedDepartment, "",
new { @class = "text-danger" })
}
Controladores:
// GET
public ActionResult GetBudgetsActuals()
{
Populate Department dropdown from DB and Year dropdown (static) here
repo = new BudgetDemoRepository();
//ModelState.Clear();
try
{
return View(repo.GetBudgetsActuals());
}
catch
{
return View("Error");
}
}
// POST
[HttpPost]
public ActionResult GetBudgetsActuals(BudgetsActualsViewModel model)
{
try
{
repo = new BudgetDemoRepository();
if (ModelState.IsValid)
{
TempData["SuccessMessage"] = "Value of SelectedDepartment is: "
+ model.SelectedDepartment;
return View(repo.GetBudgetsActuals(model));
}
else
{
model.Departments = repo.GetBudgetsActuals().Departments;
return View(model);
}
}
catch
{
return View("Error");
}
}
Repositorio : nuevo método sobrecargado para manejar POST
public BudgetsActualsViewModel GetBudgetsActuals(BudgetsActualsViewModel model)
{
....
// Get the data again
....
budgetsActuals.SelectedDepartment = model.SelectedDepartment;
for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
{
budgetsActuals.Departments.Add(
new SelectListItem
{
Text = ds.Tables[0].Rows[i]["Department"].ToString(),
Value = ds.Tables[0].Rows[i]["ListValue"].ToString()
}
);
}
return budgetsActuals;
}
Respuestas
Modifique controllerpara tener una nueva API que admita una POSTllamada. Haz lo siguiente.
Agregue a este método un método de obtención que se llama cuando se carga la vista, así:
[HttpGet]
public ActionResult GetBudgetsActuals()
{
repo = new BudgetDemoRepository();
ModelState.Clear();
try
{
return View(repo.GetBudgetsActuals());
}
catch
{
return View("Error");
}
}
Agregue un método de publicación al que se llama cuando se envía el formulario:
[HttpPost] // Decorate with this attribute
public ActionResult PostBudgetsActuals(BudgetDemo.Models.BudgetsActualsViewModel model)
{
var selectedDepartment = model.SelectedDepartment;
// dostuff with the model
return somethingFromHere;
}
Y cambia la vista así:
@model BudgetDemo.Models.BudgetsActualsViewModel
@using (Html.BeginForm("PostBudgetsActuals", "BudgetsActuals", FormMethod.Post))
{
@Html.DropDownListFor(m => m.SelectedDepartment, Model.Departments,
"Select Department", new { @class = "form-control" })
}
Si tiene un problema con la sobrecarga del DropDownListFormétodo, también puede probar un DropDownListmétodo como el siguiente:
@using (Html.BeginForm("PostBudgetsActuals", "BudgetsActuals", FormMethod.Post))
{
@Html.DropDownList("SelectedDepartment", Model.Departments,
"Select Department", new { @class = "form-control" })
}
ACTUALIZAR
// POST
[HttpPost]
public ActionResult GetBudgetsActuals(BudgetsActualsViewModel model)
{
try
{
repo = new BudgetDemoRepository();
if (model.SelectedDepartment != null)
{
TempData["SuccessMessage"] = "Value of SelectedDepartment is:
" + model.SelectedDepartment;
}
model.Departments = repo.GetBudgetsActuals().Departments;
return View(model);
}
catch
{
return View("Error");
}
}