Pregunta

Relativamente nuevo en MVC e intentando que una lista desplegable en cascada funcione para los tiempos de tren.

Después de mirar muchas publicaciones, la gente dice que debe mantenerse alejado de Viewbag/ViewData y, en cambio, concentrarse en ViewModels, pero parece que no puedo entenderlo, y me está llevando por la pared. Cualquier tutorial parece ser complejo o demasiado fácil y toda la idea de ViewModel simplemente no ha hecho clic conmigo todavía.

Así que aquí está mi escenario: tengo un sistema de administración donde el personal puede agregar viajes de trenes individuales. Para cada tiempo de tren, tengo un formulario de entrada en el que el usuario puede elegir una empresa, y desde allí, me gustaría que la lista desplegable debajo de poblar con una lista de números de viaje, que indican rutas. Una vez que han elegido un número, pueden continuar con el resto del formulario, que es bastante grande, incluido el tiempo de viaje, las instalaciones en el tren, etc.

He creado un ViewModel como SO:

public class JourneyNumbersViewModel
    {
        private List<SelectListItem> _operators = new List<SelectListItem>();
        private List<SelectListItem> _journeys= new List<SelectListItem>();

        [Required(ErrorMessage = "Please select an operator")]
        public string SelectedOperator { get; set; }
        [Required(ErrorMessage = "Please select a journey")]
        public string SelectedJourney { get; set; }

        public List<SelectListItem> Journeys
        {
            get { return _journeys; }
        }
        public List<SelectListItem> Operators
        {
            get
            {
                foreach(Operator a in Planner.Repository.OperatorRepository.GetOperatorList())
                {
                    _operators.Add(new SelectListItem() { Text = a.OperatorName, Value = a.OperatorID.ToString() });
                }
                return _operators;
            }
        }
    }

En mi controlador, tengo esto para la vista Crear:

    public ActionResult Create()
    {
        return View(new JourneyNumbersViewModel());
    }

Y aquí es donde realmente no funciona para mí, si cambio mi modelo en la parte superior de la vista de creación a: @model Planner.ViewModels.JourneyNumbersViewModel, entonces el resto de mi forma arroja errores ya que el modelo ya no es correcto para el resto del formulario. ¿Es esta la forma en que se supone que debe funcionar? ¿Qué pasa si necesita hacer referencia a modelos de vista múltiple con una sola vista?

Sé que esto es algo simple y tan pronto como haga clic, me preguntaré cómo diablos podría haber luchado con eso en primer lugar, pero si alguien puede señalar dónde me estoy equivocando, estaría muy agradecido .

¿Fue útil?

Solución

He hecho algo similar. Aquí hay algunos de los códigos (disculpas por adelantado por que esto sea bastante largo, pero quería asegurarme de que pudieras recrear esto de tu lado):

La vista se ve así:

using Cascading.Models
@model CascadingModel


@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Cascading Forms</h2>
<table>

@using(Html.BeginForm("Index", "Home"))
{
<tr>
    <td>@Html.LabelFor(m=>m.CategoryId)</td>
    <td>@Html.DropDownListFor(m => m.CategoryId, new SelectList(Model.Categories, "Id", "Name"), string.Empty)</td>
</tr>
<tr>
    <td>@Html.LabelFor(m=>m.ProductId)</td>
    <td>@Html.CascadingDropDownListFor(m => m.ProductId, new SelectList(Model.Products, "Id", "Name"), string.Empty, null, "CategoryId", "Home/CategorySelected")</td>
</tr>
<tr>
    <td>&nbsp;</td>
    <td><input type="submit" value="Go"/></td>
</tr>
}
</table>

El modelo se ve de la siguiente manera:

public class CascadingModel
{
    public int CategoryId { get; set; }
    public List<Category> Categories { get; set; }
    public int ProductId { get; set; }
    public List<Product> Products { get; set; }
}

La parte real "inteligente" del sistema es el HTML.CascadingdropDownlistfor que se ve lo siguiente:

public static class MvcHtmlExtensions
{
    public static MvcHtmlString CascadingDropDownListFor<TModel, TProperty>(
        this HtmlHelper<TModel> htmlHelper,
        Expression<Func<TModel, TProperty>> expression,
        IEnumerable<SelectListItem> selectList,
        string optionLabel,
        IDictionary<string, Object> htmlAttributes,
        string parentControlName,
        string childListUrl
        )
    {
        var memberName = GetMemberInfo(expression).Member.Name;

        MvcHtmlString returnHtml = Html.SelectExtensions.DropDownListFor(htmlHelper, expression, selectList, optionLabel, htmlAttributes);

        var returnString = MvcHtmlString.Create(returnHtml.ToString() + 
                    @"<script type=""text/javascript"">
                        $(document).ready(function () {
                            $(""#<<parentControlName>>"").change(function () { 
                                var postData = { <<parentControlName>>: $(""#<<parentControlName>>"").val() };
                                $.post('<<childListUrl>>', postData, function (data) {
                                    var options = """";
                                    $.each(data, function (index) {
                                        options += ""<option value='"" + data[index].Id + ""'>"" + data[index].Name + ""</option>"";
                                    });
                                    $(""#<<memberName>>"").html(options);
                                })
                                .error(function (jqXHR, textStatus, errorThrown) { alert(jqXHR.responseText); });
                            });
                        });
                     </script>"
                    .Replace("<<parentControlName>>", parentControlName)
                    .Replace("<<childListUrl>>", childListUrl)
                    .Replace("<<memberName>>", memberName));

        return returnString;

    }

    private static MemberExpression GetMemberInfo(Expression method)
    {
        LambdaExpression lambda = method as LambdaExpression;
        if (lambda == null)
            throw new ArgumentNullException("method");

        MemberExpression memberExpr = null;

        if (lambda.Body.NodeType == ExpressionType.Convert)
        {
            memberExpr = ((UnaryExpression)lambda.Body).Operand as MemberExpression;
        }
        else if (lambda.Body.NodeType == ExpressionType.MemberAccess)
        {
            memberExpr = lambda.Body as MemberExpression;
        }

        if (memberExpr == null)
            throw new ArgumentException("method");

        return memberExpr;
    }
}

Lógica del controlador para aquellos que lo buscan:

public ActionResult CategoriesAndProducts()
{
    var viewModel = new CategoriesAndProductsViewModel();
    viewModel.Categories = FetchCategoriesFromDataBase();
    viewModel.Products = FetchProductsFromDataBase();
    viewModel.CategoryId = viewModel.Categories[0].CategoryId;
    viewModel.ProductId = viewModel.Products.Where(p => p.CategoryId).FirstOrDefault().ProductId;
    return View(viewModel);
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top