Selezionare Tag Helper in ASP.NET Core MVC


162

Ho bisogno di aiuto con l'helper tag select in ASP.NET Core.

Ho un elenco di dipendenti che sto cercando di associare a un aiutante di selezione tag. I miei dipendenti hanno un List<Employee> EmployeesListvalore selezionato e diventeranno EmployeeIdproprietà. Il mio modello di visualizzazione è simile al seguente:

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

La mia classe di dipendenti si presenta così:

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

La mia domanda è: come posso dire al mio helper di selezione tag di utilizzare Idcome valore durante la visualizzazione FullNamenell'elenco a discesa?

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

Gradirei un aiuto con questo. Grazie.


3
solo qualcosa che ho pensato di aggiungere, sembra non funzionare se chiudi il tag select immediatamente chiudi sempre i tag con </select> l'helper tag non ha funzionato con <select asp-for ..... />
RoughPlace

solo un consiglio. i controllori per ponteggi mostrano che generalmente ti mostrano i modi migliori per tali cose
Neville Nazerane,

Risposte:


379

Utilizzo degli helper Seleziona tag per il rendering di un elemento SELECT

Nella tua azione GET, crea un oggetto del tuo modello di vista, carica la EmployeeListproprietà collection e inviala alla 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);
}

E nella tua vista di creazione, crea un nuovo SelectListoggetto dalla EmployeeListproprietà e passalo come valore per la asp-itemsproprietà.

@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 il tuo metodo di azione HttpPost per accettare i dati del modulo inviato.

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

O

Se il tuo modello di visualizzazione ha List<SelectListItem>come proprietà gli elementi a discesa.

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

E nella tua azione,

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 nella vista, puoi usare direttamente la Employeesproprietà per il 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 classe SelectListItemappartiene allo Microsoft.AspNet.Mvc.Renderingspazio dei nomi.

Assicurati di utilizzare un tag di chiusura esplicito per l'elemento select. Se usi l'approccio tag autochiudente, l'helper tag renderà un elemento SELECT vuoto!

L'approccio seguente non funzionerà

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

Ma questo funzionerà.

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

Ottenere i dati dalla tabella del database utilizzando il framework di entità

Gli esempi sopra riportati utilizzano elementi codificati per le opzioni. Quindi ho pensato di aggiungere un po 'di codice di esempio per ottenere i dati usando Entity Framework come molti lo usano.

Supponiamo che il tuo oggetto DbContext abbia una proprietà chiamata Employees, che è di tipo in DbSet<Employee>cui la Employeeclasse di entità ha una proprietà Ide Namecome questa

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

È possibile utilizzare una query LINQ per ottenere i dipendenti e utilizzare il metodo Select nell'espressione LINQ per creare un elenco di SelectListItemoggetti per ciascun dipendente.

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

Supponendo che contextsia l'oggetto contestuale db. Il codice di visualizzazione è uguale al precedente.

Utilizzando SelectList

Alcune persone preferiscono usare la SelectListclasse per contenere gli elementi necessari per rendere le opzioni.

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

Ora nella tua azione GET, puoi usare il SelectListcostruttore per popolare la Employeesproprietà del modello di vista. Assicurarsi di specificare i parametri 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" }
    };
}

Qui sto chiamando il GetEmployeesmetodo per ottenere un elenco di oggetti dipendenti, ciascuno con un Ide FirstNameproprietà e io uso quelle proprietà come DataValueFielde DataTextFielddel SelectListoggetto che abbiamo creato. È possibile modificare l'elenco hardcoded in un codice che legge i dati da una tabella del database.

Il codice di visualizzazione sarà lo stesso.

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

Rendering di un elemento SELECT da un elenco di stringhe.

A volte potresti voler rendere un elemento select da un elenco di stringhe. In tal caso, puoi usare il SelectListcostruttore che richiede soloIEnumerable<T>

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

Il codice di visualizzazione sarà lo stesso.

Impostazione delle opzioni selezionate

Alcune volte, è possibile impostare un'opzione come opzione predefinita nell'elemento SELECT (ad esempio, in una schermata di modifica, si desidera caricare il valore dell'opzione salvata in precedenza). Per fare ciò, è possibile semplicemente impostare il EmployeeIdvalore della proprietà sul valore dell'opzione che si desidera selezionare.

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

Questo selezionerà l'opzione Tom nell'elemento select quando la pagina viene renderizzata.

Menu a discesa multi-selezione

Se si desidera eseguire il rendering di un menu a discesa a selezione multipla, è possibile modificare semplicemente la proprietà del modello di vista utilizzata per l' asp-forattributo nella vista in un tipo di array.

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

Ciò renderà il markup HTML per l'elemento select con l' multipleattributo che consentirà all'utente di selezionare più opzioni.

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

Impostazione delle opzioni selezionate in selezione multipla

Simile alla selezione singola, imposta il EmployeeIdsvalore della proprietà su una matrice di valori che desideri.

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

Questo selezionerà l'opzione Tom e Jerry nell'elemento multi-selezione quando viene eseguito il rendering della pagina.

Utilizzo di ViewBag per trasferire l'elenco di elementi

Se non si preferisce mantenere una proprietà di tipo di raccolta per passare alla lista di opzioni per la vista, è possibile utilizzare il ViewBag dinamica di farlo. ( Questo non è il mio approccio personalmente raccomandato come viewbag è dinamico e il codice è incline a uncatched errori di battitura )

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 nella vista

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

Utilizzo di ViewBag per trasferire l'elenco di elementi e l'impostazione dell'opzione selezionata

È come sopra. Tutto quello che devi fare è impostare il valore della proprietà (per cui stai vincolando il menu a discesa) sul valore dell'opzione che desideri selezionare.

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 nella vista

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

Raggruppare gli articoli

Il metodo helper di selezione tag supporta le opzioni di raggruppamento in un menu a discesa. Tutto quello che devi fare è specificare il Groupvalore della proprietà di ciascuno SelectListItemnel tuo metodo di azione.

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

Non ci sono cambiamenti nel codice di visualizzazione. l'helper tag select ora visualizzerà le opzioni all'interno di 2 elementi optgroup .


1
Grazie! Un po 'come la vecchia sintassi del rasoio in cui dobbiamo eseguire una conversione in SelectList. Grazie ancora.
Sam

7
Stavo cercando come aggiungere un'opzione vuota, grazie per aver usato<option>Please select one</option>
Serj Sagan il

5
Nota che DEVI avere un tag di chiusura </select> - non funzionerà se provi a chiudere automaticamente il tag di selezione come <seleziona ... />. Inoltre, se si utilizza un'opzione vuota come "Seleziona uno", è necessario assegnargli un valore di stringa vuota "" affinché la convalida del campo richiesta funzioni.
Andy,

3
"Assicurati di utilizzare un tag di chiusura esplicito per l'elemento select. Se usi l'approccio tag autochiudente, l'helper tag renderà un elemento SELECT vuoto!" Questo era il problema nel mio caso. Saluti!
Mariusz,

2
Grazie per l'impegno profuso in questa risposta!
Vidar,

11

Ho creato un'interfaccia e un <options>tag helper per questo. Quindi non ho dovuto convertire gli IEnumerable<T>elementi in IEnumerable<SelectListItem>ogni volta che devo popolare il <select>controllo.

E penso che funzioni magnificamente ...

L'utilizzo è simile a:

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

E per farlo funzionare con l'helper tag devi implementare quell'interfaccia nella tua 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 ; }
}

Questi sono i codici necessari:

L'interfaccia:

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

Il <options>tag helper:

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

Potrebbero esserci degli errori di battitura, ma credo che l'obiettivo sia chiaro. Ho dovuto modificare un po '.


4

Puoi anche usare 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;

4

La mia risposta di seguito non risolve la domanda ma si riferisce a.

Se qualcuno utilizza enuminvece di un modello di classe, come in questo esempio:

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

E una proprietà per ottenere il valore al momento dell'invio:

public int No { get; set; }

Nella pagina del rasoio, è possibile utilizzare Html.GetEnumSelectList<Counter>()per ottenere le proprietà enum.

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

Genera il seguente 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>

3

È possibile utilizzare il codice seguente per la selezione multipla :

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

Puoi anche usare:

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

0

In Ottieni:

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

In posta:

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

In vista :

<label>Tags</label>
<select  asp-for="Tags"  id="Tags" name="Tags" class="form-control" asp-items="ViewBag.Tags" multiple></select>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.