Selecione Auxiliar de Marca no ASP.NET Core MVC

162

Preciso de ajuda com o auxiliar de seleção de tags no ASP.NET Core.

Eu tenho uma lista de funcionários que estou tentando vincular a um auxiliar de seleção de tags. Meus funcionários estão em um List<Employee> EmployeesListvalor selecionado e entrarão em EmployeeIdpropriedade. Meu modelo de exibição fica assim:

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

Minha classe de funcionários fica assim:

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

Minha pergunta é como digo ao meu ajudante de tag select para usar Idcomo valor enquanto é exibido FullNamena lista suspensa?

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

Eu apreciaria alguma ajuda com isso. Obrigado.

Sam
fonte
3
apenas algo que eu pensei que deveria adicionar, parece não funcionar se você fechar a tag select imediatamente sempre fechar as tags com </select> o auxiliar de tag não funcionou com <select asp-for ..... />
RoughPlace
apenas uma dica. andaimes controladores geralmente mostram mostrar-lhe as melhores maneiras de tais coisas
Neville Nazerane

Respostas:

379

Usando os auxiliares Select Tag para renderizar um elemento SELECT

Na sua ação GET, crie um objeto do seu modelo de visualização, carregue a EmployeeListpropriedade de coleção e envie-a para a visualização.

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);
}

E, na sua exibição de criação, crie um novo SelectListobjeto a partir da EmployeeListpropriedade e passe-o como valor para a asp-itemspropriedade.

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

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

    <input type="submit"/>

</form>

E seu método de ação HttpPost para aceitar os dados do formulário enviado.

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

Ou

Se o seu modelo de visualização tiver uma List<SelectListItem>propriedade como para seus itens suspensos.

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

E na sua ação,

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);
}

E, na visualização, você pode usar diretamente a Employeespropriedade para oasp-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>

A classe SelectListItem pertence ao Microsoft.AspNet.Mvc.Renderingespaço para nome.

Verifique se você está usando uma tag de fechamento explícita para o elemento select. Se você usar a abordagem de fechamento automático, o auxiliar de renderização renderizará um elemento SELECT vazio!

A abordagem abaixo não funcionará

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

Mas isso vai funcionar.

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

Obtendo dados da tabela do banco de dados usando a estrutura da entidade

Os exemplos acima estão usando itens codificados para as opções. Então, pensei em adicionar um código de exemplo para obter dados usando o Entity Framework, pois muitas pessoas o usam.

Vamos supor que seu objeto DbContext tenha uma propriedade chamada Employees, que é do tipo em DbSet<Employee>que a Employeeclasse de entidade possui uma propriedade Ide Namecomo esta

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

Você pode usar uma consulta LINQ para obter os funcionários e usar o método Select na sua expressão LINQ para criar uma lista de SelectListItemobjetos para cada funcionário.

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);
}

Assumindo que contexté seu objeto de contexto db. O código de exibição é o mesmo que acima.

Usando SelectList

Algumas pessoas preferem usar a SelectListclasse para armazenar os itens necessários para renderizar as opções.

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

Agora, na sua ação GET, você pode usar o SelectListconstrutor para preencher a Employeespropriedade do modelo de exibição. Verifique se você está especificando os parâmetros dataValueFielde dataTextField.

public IActionResult Create()
{
    var vm = new MyViewModel();
    vm.Employees = new SelectList(GetEmployees(),"Id","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" }
    };
}

Aqui eu estou chamando o GetEmployeesmétodo para obter uma lista de objetos Employee, cada um com uma Ide FirstNamepropriedade e eu usar essas propriedades como DataValueFielde DataTextFielddo SelectListobjeto que criamos. Você pode alterar a lista codificada para um código que lê dados de uma tabela de banco de dados.

O código da visualização será o mesmo.

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

Renderize um elemento SELECT a partir de uma lista de cadeias.

Às vezes, você pode querer renderizar um elemento de seleção de uma lista de cadeias. Nesse caso, você pode usar o SelectListconstrutor que leva apenasIEnumerable<T>

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

O código da visualização será o mesmo.

Definir opções selecionadas

Algumas vezes, convém definir uma opção como a opção padrão no elemento SELECT (por exemplo, em uma tela de edição, você deseja carregar o valor da opção salva anteriormente). Para fazer isso, você pode simplesmente definir o EmployeeIdvalor da propriedade para o valor da opção que deseja ser selecionada.

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);
}

Isso selecionará a opção Tom no elemento de seleção quando a página for renderizada.

Menu suspenso de seleção múltipla

Se você deseja renderizar uma lista suspensa de seleção múltipla, pode simplesmente alterar a propriedade do modelo de visualização que você usa para asp-foratribuir na visualização a um tipo de matriz.

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

Isso renderizará a marcação HTML para o elemento select com o multipleatributo que permitirá ao usuário selecionar várias opções.

@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>

Definir opções selecionadas na seleção múltipla

Semelhante à seleção única, defina o EmployeeIdsvalor da propriedade para uma matriz de valores que você deseja.

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);
}

Isso selecionará a opção Tom e Jerry no elemento de seleção múltipla quando a página for renderizada.

Usando o ViewBag para transferir a lista de itens

Se você não prefere manter uma propriedade tipo de coleção para passar a lista de opções para a vista, você pode usar o ViewBag dinâmica para fazê-lo. ( Esta não é a minha abordagem recomendada, pessoalmente, como viewbag é dinâmico e seu código é propenso a uncatched erros de digitação )

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

e na vista

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

Usando o ViewBag para transferir a lista de itens e definir a opção selecionada

É o mesmo que acima. Tudo o que você precisa fazer é definir o valor da propriedade (para a qual você está vinculando a lista suspensa) com o valor da opção que você deseja selecionar.

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());
}

e na vista

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

Agrupando itens

O método auxiliar de seleção de tags suporta opções de agrupamento em uma lista suspensa. Tudo o que você precisa fazer é especificar o Groupvalor da propriedade de cada um SelectListItemno seu método de ação.

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);
}

Não há alterações no código da visualização. o auxiliar de seleção de tags agora renderizará as opções em dois itens do grupo de opções.

Shyju
fonte
1
Obrigado! É como a sintaxe antiga do razor, na qual precisamos realizar uma conversão para o SelectList. Obrigado novamente.
Sam
7
Eu estava procurando como adicionar uma opção vazia, obrigado por usar<option>Please select one</option>
Serj Sagan
5
Observe que você DEVE ter uma tag de fechamento </select> - não funcionará se você tentar fechar automaticamente a tag de seleção como <select ... />. Além disso, se você usar uma opção vazia como "Por favor, selecione uma", precisará atribuir a ela um valor de sequência vazia "" para que a validação de campo necessária funcione.
21917 Andy
3
"Verifique se você está usando uma tag de fechamento explícita para o elemento select. Se você usar a abordagem de tag de fechamento automático, o auxiliar de tag renderizará um elemento SELECT vazio!" Este foi o problema no meu caso. Felicidades!
Mariusz
2
Obrigado por colocar tanto esforço nesta resposta!
Vidar
11

Eu criei uma interface e um <options>auxiliar de tag para isso. Portanto, não tive que converter os IEnumerable<T>itens em IEnumerable<SelectListItem>todas as vezes que preciso preencher o <select>controle.

E acho que funciona lindamente ...

O uso é algo como:

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

E para fazê-lo funcionar com o auxiliar de tag, você precisa implementar essa interface em sua classe:

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

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

Estes são os códigos necessários:

A interface:

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

O <options>auxiliar de tag:

[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>");
            }
        }
    }
}

Pode haver algum erro de digitação, mas o objetivo é claro, eu acho. Eu tive que editar um pouco.

Yves
fonte
4

Você também pode usar IHtmlHelper.GetEnumSelectList.

    // Summary:
    //     Returns a select list for the given TEnum.
    //
    // Type parameters:
    //   TEnum:
    //     Type to generate a select list for.
    //
    // Returns:
    //     An System.Collections.Generic.IEnumerable`1 containing the select list for the
    //     given TEnum.
    //
    // Exceptions:
    //   T:System.ArgumentException:
    //     Thrown if TEnum is not an System.Enum or if it has a System.FlagsAttribute.
    IEnumerable<SelectListItem> GetEnumSelectList<TEnum>() where TEnum : struct;
Amir Rezaei
fonte
4

Minha resposta abaixo não resolve a questão, mas está relacionada a.

Se alguém estiver usando, em enumvez de um modelo de classe, como este exemplo:

public enum Counter
{
    [Display(Name = "Number 1")]
    No1 = 1,
    [Display(Name = "Number 2")]
    No2 = 2,
    [Display(Name = "Number 3")]
    No3 = 3
}

E uma propriedade para obter o valor ao enviar:

public int No { get; set; }

Na página de navalha, você pode usar Html.GetEnumSelectList<Counter>()para obter as propriedades da enumeração.

<select asp-for="No" asp-items="@Html.GetEnumSelectList<Counter>()"></select>

Ele gera o seguinte HTML:

<select id="No" name="No">
    <option value="1">Number 1</option>
    <option value="2">Number 2</option>
    <option value="3">Number 3</option>
</select>
Bronzeado
fonte
3

Você pode usar o código abaixo para seleção múltipla :

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

Você também pode usar:

<select id="EmployeeId" name="EmployeeId"  multiple="multiple" asp-items="@ViewBag.Employees">
    <option>Please select</option>
</select>
Zubayer Bin Ayub
fonte
0

Em Get:

public IActionResult Create()
{
    ViewData["Tags"] = new SelectList(_context.Tags, "Id", "Name");
    return View();
}

Na postagem:

var selectedIds= Request.Form["Tags"];

Em vista :

<label>Tags</label>
<select  asp-for="Tags"  id="Tags" name="Tags" class="form-control" asp-items="ViewBag.Tags" multiple></select>
Anas Al-Qudah
fonte