Seleccione Tag Helper en ASP.NET Core MVC

Resuelto Sam asked hace 8 años • 7 respuestas

Necesito ayuda con el asistente de selección de etiquetas en ASP.NET Core.

Tengo una lista de empleados que estoy intentando vincular a un asistente de etiqueta seleccionada. Mis empleados están en una propiedad List<Employee> EmployeesListy el valor seleccionado se destinará a EmployeeIdella. Mi modelo de vista se ve así:

public class MyViewModel
{
   public int EmployeeId { get; set; }
   public string Comments { get; set; }
   public List<Employee> EmployeesList {get; set; }
}

Mi clase de empleado se ve así:

public class Employee
{
   public int Id { get; set; }
   public string FullName { get; set; }
}

Mi pregunta es ¿cómo le digo a mi asistente de etiqueta de selección que use Idcomo valor mientras se muestra FullNameen la lista desplegable?

<select asp-for="EmployeeId" asp-items="???" />

Agradecería algo de ayuda con esto. Gracias.

Sam avatar Jan 06 '16 08:01 Sam
Aceptado

Uso de los ayudantes de Seleccionar etiqueta para representar un elemento SELECT

En su acción GET, cree un objeto de su modelo de vista, cargue la EmployeeListpropiedad de la colección y envíelo a la vista.

public IActionResult Create()
{
    var vm = new MyViewModel();
    vm.EmployeesList = new List<Employee>
    {
        new Employee { Id = 1, FullName = "Shyju" },
        new Employee { Id = 2, FullName = "Bryan" }
    };
    return View(vm);
}

Y en su vista de creación, cree un nuevo SelectListobjeto a partir de la EmployeeListpropiedad y páselo como valor para la asp-itemspropiedad.

@model MyViewModel
<form asp-controller="Home" asp-action="Create">

    <select asp-for="EmployeeId" 
            asp-items="@(new SelectList(Model.EmployeesList, nameof(Employee.Id), nameof(Employee.FullName)))">
        <option>Please select one</option>
    </select>

    <input type="submit"/>

</form>

Y su método de acción HttpPost para aceptar los datos del formulario enviado.

[HttpPost]
public IActionResult Create(MyViewModel model)
{
   //  check model.EmployeeId 
   //  to do : Save and redirect
}

O

Si su modelo de vista tiene una List<SelectListItem>propiedad para sus elementos desplegables.

public class MyViewModel
{
    public int EmployeeId { get; set; }
    public string Comments { get; set; }
    public List<SelectListItem> Employees { set; get; }
}

Y en tu acción de obtención,

public IActionResult Create()
{
    var vm = new MyViewModel();
    vm.Employees = new List<SelectListItem>
    {
        new SelectListItem {Text = "Shyju", Value = "1"},
        new SelectListItem {Text = "Sean", Value = "2"}
    };
    return View(vm);
}

Y en la vista, puede usar directamente la Employeespropiedad para el archivo asp-items.

@model MyViewModel
<form asp-controller="Home" asp-action="Create">

    <label>Comments</label>
    <input type="text" asp-for="Comments"/>

    <label>Lucky Employee</label>
    <select asp-for="EmployeeId" asp-items="@Model.Employees" >
        <option>Please select one</option>
    </select>

    <input type="submit"/>

</form>

La clase SelectListItempertenece al Microsoft.AspNet.Mvc.Renderingespacio de nombres.

Asegúrese de utilizar una etiqueta de cierre explícita para el elemento seleccionado. Si utiliza el enfoque de etiqueta de cierre automático, el asistente de etiqueta generará un elemento SELECT vacío.

El siguiente enfoque no funcionará

<select asp-for="EmployeeId" asp-items="@Model.Employees" />

Pero esto funcionará.

<select asp-for="EmployeeId" asp-items="@Model.Employees"></select>

Obtener datos de la tabla de su base de datos usando el marco de entidad

Los ejemplos anteriores utilizan elementos codificados de forma rígida para las opciones. Así que pensé en agregar un código de muestra para obtener datos usando Entity Framework, ya que mucha gente lo usa.

Supongamos que su objeto DbContext tiene una propiedad llamada Employees, que es de tipo DbSet<Employee>donde la Employeeclase de entidad tiene una Idpropiedad Namecomo esta

public class Employee
{
   public int Id { set; get; }
   public string Name { set; get; }
}

Puede usar una consulta LINQ para obtener los empleados y usar el método Select en su expresión LINQ para crear una lista de SelectListItemobjetos para cada empleado.

public IActionResult Create()
{
    var vm = new MyViewModel();
    vm.Employees = context.Employees
                          .Select(a => new SelectListItem() {  
                              Value = a.Id.ToString(),
                              Text = a.Name
                          })
                          .ToList();
    return View(vm);
}

Suponiendo que contextsea su objeto de contexto de base de datos. El código de vista es el mismo que el anterior.

Usando la lista de selección

Algunas personas prefieren usar SelectListla clase para contener los elementos necesarios para representar las opciones.

public class MyViewModel
{
    public int EmployeeId { get; set; }
    public SelectList Employees { set; get; }
}

Ahora, en su acción GET, puede usar el SelectListconstructor para completar la Employeespropiedad del modelo de vista. Asegúrese de especificar los parámetros dataValueFieldy dataTextField. Puede utilizar una expresión nameof para vincular los nombres de los campos estáticamente.

public IActionResult Create()
{
    var vm = new MyViewModel();
    vm.Employees = new SelectList(GetEmployees(), nameof(Employee.Id), nameof(Employee.FirstName));
    return View(vm);
}
public IEnumerable<Employee> GetEmployees()
{
    // hard coded list for demo. 
    // You may replace with real data from database to create Employee objects
    return new List<Employee>
    {
        new Employee { Id = 1, FirstName = "Shyju" },
        new Employee { Id = 2, FirstName = "Bryan" }
    };
}

Aquí estoy llamando al GetEmployeesmétodo para obtener una lista de objetos Empleado, cada uno con una Idpropiedad FirstNamey uso esas propiedades como DataValueFieldy DataTextFielddel SelectListobjeto que creamos. Puede cambiar la lista codificada a un código que lea datos de una tabla de base de datos.

El código de vista será el mismo.

<select asp-for="EmployeeId" asp-items="@Model.Employees" >
    <option>Please select one</option>
</select>

Representa un elemento SELECT de una lista de cadenas.

A veces es posible que desee representar un elemento seleccionado de una lista de cadenas. En ese caso, puedes usar el SelectListconstructor que solo tomaIEnumerable<T>

var vm = new MyViewModel();
var items = new List<string> {"Monday", "Tuesday", "Wednesday"};
vm.Employees = new SelectList(items);
return View(vm);

El código de vista será el mismo.

Configuración de opciones seleccionadas

Algunas veces, es posible que desee establecer una opción como opción predeterminada en el elemento SELECT (por ejemplo, en una pantalla de edición, desea cargar el valor de la opción previamente guardada). Para hacer eso, simplemente puede establecer el EmployeeIdvalor de la propiedad en el valor de la opción que desea seleccionar.

public IActionResult Create()
{
    var vm = new MyViewModel();
    vm.Employees = new List<SelectListItem>
    {
        new SelectListItem {Text = "Shyju", Value = "11"},
        new SelectListItem {Text = "Tom", Value = "12"},
        new SelectListItem {Text = "Jerry", Value = "13"}
    };
    vm.EmployeeId = 12;  // Here you set the value
    return View(vm);
}

Esto seleccionará la opción Tom en el elemento de selección cuando se represente la página.

Menú desplegable de selección múltiple

Si desea representar un menú desplegable de selección múltiple, simplemente puede cambiar la propiedad del modelo de vista que usa como asp-foratributo en su vista a un tipo de matriz.

public class MyViewModel
{
    public int[] EmployeeIds { get; set; }
    public List<SelectListItem> Employees { set; get; }
}

Esto mostrará el marcado HTML para el elemento seleccionado con el multipleatributo que permitirá al usuario seleccionar múltiples opciones.

@model MyViewModel
<select id="EmployeeIds" multiple="multiple" name="EmployeeIds">
    <option>Please select one</option>
    <option value="1">Shyju</option>
    <option value="2">Sean</option>
</select>

Configuración de opciones seleccionadas en selección múltiple

De manera similar a la selección única, establezca el EmployeeIdsvalor de la propiedad en la matriz de valores que desee.

public IActionResult Create()
{
    var vm = new MyViewModel();
    vm.Employees = new List<SelectListItem>
    {
        new SelectListItem {Text = "Shyju", Value = "11"},
        new SelectListItem {Text = "Tom", Value = "12"},
        new SelectListItem {Text = "Jerry", Value = "13"}
    };
    vm.EmployeeIds= new int[] { 12,13} ;  
    return View(vm);
}

Esto seleccionará la opción Tom y Jerry en el elemento de selección múltiple cuando se represente la página.

Usando ViewBag para transferir la lista de elementos

Si no prefiere mantener una propiedad de tipo de colección para pasar la lista de opciones a la vista, puede usar ViewBag dinámico para hacerlo. ( Este no es mi enfoque recomendado personalmente ya que viewbag es dinámico y su código es propenso a no ser detectado errores tipográficos )

public IActionResult Create()
{       
    ViewBag.Employees = new List<SelectListItem>
    {
        new SelectListItem {Text = "Shyju", Value = "1"},
        new SelectListItem {Text = "Sean", Value = "2"}
    };
    return View(new MyViewModel());
}

y en la vista

<select asp-for="EmployeeId" asp-items="@ViewBag.Employees">
    <option>Please select one</option>
</select>

Usando ViewBag para transferir la lista de elementos y configurar la opción seleccionada

Es lo mismo que el anterior. Todo lo que tiene que hacer es establecer el valor de la propiedad (para la cual está vinculando el menú desplegable) al valor de la opción que desea seleccionar.

public IActionResult Create()
{       
    ViewBag.Employees = new List<SelectListItem>
    {
        new SelectListItem {Text = "Shyju", Value = "1"},
        new SelectListItem {Text = "Bryan", Value = "2"},
        new SelectListItem {Text = "Sean", Value = "3"}
    };

    vm.EmployeeId = 2;  // This will set Bryan as selected

    return View(new MyViewModel());
}

y en la vista

<select asp-for="EmployeeId" asp-items="@ViewBag.Employees">
    <option>Please select one</option>
</select>

Agrupar elementos

El método auxiliar de selección de etiquetas admite opciones de agrupación en un menú desplegable. Todo lo que tiene que hacer es especificar el Groupvalor de propiedad de cada uno SelectListItemen su método de acción.

public IActionResult Create()
{
    var vm = new MyViewModel();
   
    var group1 = new SelectListGroup { Name = "Dev Team" };
    var group2 = new SelectListGroup { Name = "QA Team" };

    var employeeList = new List<SelectListItem>()
    {
        new SelectListItem() { Value = "1", Text = "Shyju", Group = group1 },
        new SelectListItem() { Value = "2", Text = "Bryan", Group = group1 },
        new SelectListItem() { Value = "3", Text = "Kevin", Group = group2 },
        new SelectListItem() { Value = "4", Text = "Alex", Group = group2 }
    };
    vm.Employees = employeeList;
    return View(vm);
}

No hay cambios en el código de vista. el asistente de etiqueta de selección ahora mostrará las opciones dentro de 2 elementos del grupo de opciones .

Shyju avatar Jan 06 '2016 01:01 Shyju

Creé una interfaz y una <options>etiqueta auxiliar para esto. Así que no tuve que convertir los IEnumerable<T>elementos IEnumerable<SelectListItem>cada vez que tenía que completar el <select>control.

Y creo que funciona maravillosamente...

El uso es algo como:

<select asp-for="EmployeeId">
    <option value="">Please select...</option>
    <options asp-items="@Model.EmployeesList" />
</select>

Y para que funcione con el asistente de etiquetas, debes implementar esa interfaz en tu clase:

public class Employee : IIntegerListItem
{
   public int Id { get; set; }
   public string FullName { get; set; }

   public int Value { return Id; }
   public string Text{ return FullName ; }
}

Estos son los códigos necesarios:

La interfaz:

public interface IIntegerListItem
{
    int Value { get; }
    string Text { get; }
}

El <options>ayudante de etiquetas:

[HtmlTargetElement("options", Attributes = "asp-items")]
public class OptionsTagHelper : TagHelper
{
    public OptionsTagHelper(IHtmlGenerator generator)
    {
        Generator = generator;
    }

    [HtmlAttributeNotBound]
    public IHtmlGenerator Generator { get; set; }

    [HtmlAttributeName("asp-items")]
    public object Items { get; set; }

    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
        output.SuppressOutput();
        // Is this <options /> element a child of a <select/> element the SelectTagHelper targeted?
        object formDataEntry;
        context.Items.TryGetValue(typeof(SelectTagHelper), out formDataEntry);

        var selectedValues = formDataEntry as ICollection<string>;
        var encodedValues = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
        if (selectedValues != null && selectedValues.Count != 0)
        {
            foreach (var selectedValue in selectedValues)
            {
                encodedValues.Add(Generator.Encode(selectedValue));
            }
        }

        IEnumerable<SelectListItem> items = null;
        if (Items != null)
        {
            if (Items is IEnumerable)
            {
                var enumerable = Items as IEnumerable;
                if (Items is IEnumerable<SelectListItem>)
                    items = Items as IEnumerable<SelectListItem>;
                else if (Items is IEnumerable<IIntegerListItem>)
                    items = ((IEnumerable<IIntegerListItem>)Items).Select(x => new SelectListItem() { Selected = false, Value = ((IIntegerListItem)x).Value.ToString(), Text = ((IIntegerListItem)x).Text });
                else
                    throw new InvalidOperationException(string.Format("The {2} was unable to provide metadata about '{1}' expression value '{3}' for <options>.",
                        "<options>",
                        "ForAttributeName",
                        nameof(IModelMetadataProvider),
                        "For.Name"));
            }
            else
            {
                throw new InvalidOperationException("Invalid items for <options>");
            }

            foreach (var item in items)
            {
                bool selected = (selectedValues != null && selectedValues.Contains(item.Value)) || encodedValues.Contains(item.Value);
                var selectedAttr = selected ? "selected='selected'" : "";

                if (item.Value != null)
                    output.Content.AppendHtml($"<option value='{item.Value}' {selectedAttr}>{item.Text}</option>");
                else
                    output.Content.AppendHtml($"<option>{item.Text}</option>");
            }
        }
    }
}

Puede que haya algún error tipográfico, pero creo que el objetivo es claro. Tuve que editar un poco.

Yves avatar Jan 06 '2016 01:01 Yves