skip to Main Content

I have a multi-select dropdown that I need to grab the values from and place in another data table – the Incident table.

I am pulling the values for the dropdown from this model:

using System.ComponentModel.DataAnnotations;

namespace DWITracker.Model
{
    public class Charge
    {
        [Key]
        public int Id { get; set; }
        [Required]
        [Display(Name = "Charge")]
        public string ChargeCode { get; set; }
        [Required]
        [Display(Name = "Charge Description")]
        public string ChargeDesc { get; set; }
    }
}

And this is my Create.cs:

using DWITracker.Data;
using DWITracker.Model;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;

namespace DWITracker.Pages.Incidents;

[BindProperties]

public class CreateModel : PageModel
{
    private readonly ApplicationDbContext _db;
    public Incident Incident { get; set; }
    public CreateModel(ApplicationDbContext db)
    {
        _db = db;
    }

    public IEnumerable<City> DisplayPIAddressCityData { get; set; }
    public IEnumerable<County> DisplayPIAddressCountyData { get; set; }
    public IEnumerable<Ethnicity> DisplayPIEthnicityData { get; set; }
    public IEnumerable<int> Charge { get; set; }
    public IEnumerable<SelectListItem> ChargeList { get; set; }

    public async Task<PageResult> OnGet()
    {
        await _db.City.Select(a => a.CityName).ToListAsync();
        DisplayPIAddressCityData = await _db.City.ToListAsync();
        await _db.County.Select(a => a.CountyName).ToListAsync();
        DisplayPIAddressCountyData = await _db.County.ToListAsync();
        await _db.Ethnicity.Select(a => a.EthnicityName).ToListAsync();
        DisplayPIEthnicityData = await _db.Ethnicity.ToListAsync();
        var charges = from c in _db.Charge
                         select c;
        ChargeList = charges.Select(c => new SelectListItem { Value = c.Id.ToString(), Text = c.ChargeCode });
        return Page();
    }
    
    public async Task<IActionResult> OnPost()
    {
        await _db.Incident.AddAsync(Incident);
        await _db.SaveChangesAsync();
        TempData["success"] = "Incident Information added successfully.";
        return RedirectToPage("Index");
    }

}

And here is the relevant part of my view:

                    <td style="width: 40%">
                    <div class="mb-3">
                        <label asp-for="Incident.ArrestCharges" class="control-label"></label>
                        <select asp-for="Incident.ArrestCharges" class="form-select" multiple="multiple" asp-items="Model.ChargeList">
                            <option value="">Select Applicable Charge(s)</option>
                        </select>
                    </div>
                </td>

Right now, if I select more than one item from the multi-select dropdown, it places the Id value of only the FIRST selection made into the Incident.ArrestCharges column.

I would like it to place a comma separated list of multiple charge codes (not IDs) that would look like this as an example: VTL 1192-1, VTL 1192-2, VTL 1192-2

Would appreciate some guidance in what I need to remove, add or change. This is my first time creating a multi-select dropdown and binding it. Thank you!

2

Answers


  1. Ok, so if user selects 2, or 1 or 5 options?

    Then you will need to add 2, or 1 or 5 recrods here.

    You can NOT do this:

    Eg: tblPeople

    FirstName: John
    LastName: Smith
    HotelsBooked: 123, 832, 992
    

    So, by storing 3 values (of hotels I am booked to), then now what?

    How you going to report, or query or use sql to query what Hotels I am booked into?

    Answer: you can’t!!!

    So, do NOT just out of the blue start shoving multiple ID’s into ONE column, since then you just broken all possibilities of using SQL to query that data.

    In other words, you going to break your relational model, and you do that, then your whole system can’t report on say what hotels I am booked into (in above example).

    And since the column data type is a "number" and "id"? Then of course you can’t just shove in multiple ID values. You COULD as my above example shows shove in multiple values if using a string for that column, but as noted, the INSTANT you do that? that is the very same instant you broken the relational model, but it not really about some relational "mumbo-jumbo", but now that you have no practical means to use sql to query that data. (and even translate the "id’s" back into their correct values for display in reports or even in a web page.

    So in above, if I want to allow the person to be booked into multiple hotels, I can’t just out of the blue shove in multiple values, but will need to create a new child table. In my exmaple, it would be

    tblPeople – my above information about the person.

    tblPeopleBookedInhotels – this is the new child table.

    So, in place of above, (where I attempted to shove in multiple id’s into that column, I will now have this:

    So, old:

    enter image description here

    New:

    enter image description here

    So, you can see the "instant" we want multiple hotels to be booked, then we can’t use the one column (hotel_id) that used to save ONE value and ONE id.

    The issue is even worse, since your column data type is no doubt a number type, and thus you can’t even shove in a string of "id’s" anyway.

    And worse yet, you don’t want to do that, since then any and all ability to query that data also goes out the window.

    So, don’t shove multiple values into that one column (that does not allow multiple values).

    you will have to get the user selections, AND THEN LOOP over the selections, adding a whole new row to this new table that now can contain a "list" of many hotel bookings.

    I mean, you can try can kluge this, and change that one column type from a "number" to a string, and then try to shove in multiple values as a string, but you then break all existing software that assumed and expected ONLY one "id" value in that column.

    Edit: Example to write out many rows

    Ok, so assume we have "people", and then that listbox of hotels they want to visit or whatever.

    So, we have this:

    So, we will have People, a table of hotels, and then of course our table that allows each person to have mutliple hotel bookings.

    So, tblHotelsBooked.

    we in effect have this:

    enter image description here

    So, say we have some markup to display the Person.

    Nothing special, just some markup, AND ALSO that listbox of hotels.

    so, we have this markup (not all that important).

    <div id="EditRecord" runat="server" style="float:left;display: normal;border:solid 2px;padding:15px;border-radius:12px">
    
        <h3>Edit Bookings</h3>
    
        <div style="float:left" class="iForm">
                <label>First Name</label>
                <asp:TextBox ID="tFN" runat="server" f="FirstName" Width="140" /> <br />
                <label>Last Name</label>
                <asp:TextBox ID="tLN" runat="server" f="LastName" Width="140" /> <br />
                <label>City</label>
                <asp:TextBox ID="tCity" runat="server" f="City" Width="140" /><br />
                <label>Prov</label>
                <asp:TextBox ID="tProvince" runat="server" f="Province" Width="75" ></asp:TextBox>
        </div>
        <div style="float:left;margin-left:20px;margin-top:-30px" class="iForm">
            <label>Notes</label> <br />
            <asp:TextBox ID="txtNotes" runat="server" Width="400" TextMode="MultiLine" 
                Height="150px" f="Notes" ></asp:TextBox> <br />
        </div>
        <div style="float:left;margin-left:20px;margin-top:-30px" class="iForm">
            <label>Select Hotels</label> <br />
            <asp:ListBox ID="lstHotels" runat="server"
                DataValueField="id"
                DataTextField="HotelName"
                SelectionMode="Multiple" Height="180px" Width="183px">
            </asp:ListBox>
        </div>
        <div style="clear:both;height:20px"></div>
    
        <button id="cmdSave" runat="server" class="btn myshadow" onserverclick="cmdSave_ServerClick" >
            <span aria-hidden="true" class="glyphicon glyphicon-floppy-saved"> Save</span> 
        </button>
    
        <button id="cmdCancel" runat="server" class="btn myshadow" style="margin-left:15px" >
            <span aria-hidden="true" class="glyphicon glyphicon-arrow-left"> Back/Cancel</span>
        </button>
    
        <button id="cmdDelete" runat="server" class="btn myshadow" style="margin-left:15px">
            <span aria-hidden="true" class="glyphicon glyphicon-trash"> Delete</span>
        </button>
    
    </div>
    

    Ok, so our code has to load hte one person, and load up the listbox of hotels we want to choose.

    So, code to load is this:

        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                ViewState["PKID"] = 16;  // assume passed value from preivous page
                LoadData();
            }
        }
    
        void LoadData()
        {
            string strSQL =
                @"SELECT id, HotelName FROM tblHotelsA
                 ORDER BY HotelName";
            lstHotels.DataSource = General.MyRst(strSQL); // load list box
            lstHotels.DataBind();
    
            strSQL = $@"SELECT * from People WHERE ID = {ViewState["PKID"]}";
            DataTable dtPerson = General.MyRst(strSQL);
            General.FLoader(EditRecord, dtPerson.Rows[0]);
        }
    

    And now we see/have this:

    enter image description here

    So, the save code has to:

    Save any edits to the one person, and THEN write out a new row for each hotel selected in the listbox.

    So, the save code is this:

        protected void cmdSave_ServerClick(object sender, EventArgs e)
        {
            int PKID = (int)ViewState["PKID"];
            General.FWriter(EditRecord, PKID, "People"); // send form to database
    
            // now save/process list box of multiple selected hotels
            // create one new whole row for each hotel selected.
            string strSQL = "SELECT * FROM tblHotelsBooked WHERE ID = 0";
            using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST4))
            {
                using (SqlCommand cmdSQL = new SqlCommand(strSQL, conn))
                {
                    conn.Open();
                    DataTable dtBookedHotels = new DataTable();
                    dtBookedHotels.Load(cmdSQL.ExecuteReader());
                    foreach (ListItem OneHotel in lstHotels.Items)
                    {
                        if (OneHotel.Selected) 
                        {
                            DataRow MyNewBooking = dtBookedHotels.NewRow();
                            MyNewBooking["People_ID"] = PKID;
                            MyNewBooking["Hotel_id"] = OneHotel.Value;
                            dtBookedHotels.Rows.Add(MyNewBooking);
                        }  
                    }
                    SqlDataAdapter da = new SqlDataAdapter(cmdSQL);
                    SqlCommandBuilder daU = new SqlCommandBuilder(da);
                    // now save/send all rows in data table back to database
                    da.Update(dtBookedHotels);
                }
            }
        }
    

    So, now we written out a row of data for each selected hotel.

    However, in this example?

    Our problem now is WHEN we go back to the page to edit again.

    We will now have to not only load the one reocrd to edit, but ALSO then load up the list box with possible choices, and ALSO select/highlight each existing hotel so the user can "see" and "know" which options are selected.

    And if the user un-selects a hotel, then we have to remove/delete that child row from our tblHotelsBooked.

    Now, if this was just a single value (like favorote food), or some such, then that UI can be rather nice. However, in my case, given we are selecting (and adding) hotels to the give user?).

    Then we REALLY need to adopt a UI in which the user can see what hotels, and add more hotels, and remove hotels.

    So, really, the listbox should become a table like view (say a grid view).

    So, THEN when we return to that reocrd, we can more clear see, choose, add, remove, edit, change that information.

    So, really, we should dump the listbox, and use a grid of some type.

    The result then would be this:

    enter image description here

    Now, in above example, we kind of have "more" then the selected list.

    So, if we were to JUST have a listbox with selected hotels? Sure, but we kind of need "more" in this example, since I want to add more hotels, or remove them.

    So, if we "stick" to the list box?

    Then we need to add code to select-highlight existing selections.

    then we need to add code to "delete" a child row when we un-select.

    Login or Signup to reply.
  2. I would like it to place a comma separated list of multiple charge codes (not IDs)

    That is because your dropdown value has been set with the Id, change the value to ChargeCode like below:

    ChargeList = charges.Select(c => new SelectListItem { Value = c.ChargeCode, Text = c.ChargeCode });
    

    And the multiple dropdownlist should match List<string> type property from your requirement, so you need create a separate List<string> property to receive the multiple selected value and then convert this list to string with comma. At last you can set this string value to Incident.ArrestCharges .

    Here is a whole working demo you could follow:

    Page

    @page
    @model CreateModel 
    <form method="post">
        <div class="mb-3">
            <label asp-for="Incident.ArrestCharges" class="control-label"></label>
               //change the tag helper value here...........
             <select asp-for="MultiCharge" class="form-select" multiple="multiple" asp-items="Model.ChargeList">
                <option value="">Select Applicable Charge(s)</option>
            </select>
        </div>
        <input type="submit" value="Post" />
    </form>
    

    PageModel

    [BindProperties]
    public class CreateModel : PageModel
    {
        //more properties......
        public Incident Incident { get; set; }
        public IEnumerable<SelectListItem> ChargeList { get; set; }
        public List<string> MultiCharge { get; set; }  //receive the multiple selected values
        public async Task<PageResult> OnGet()
        {   
            //other not important code......
            //var charges = from c in _db.Charge
            //             select c;
            //hard-coding here is just for easy testing
            var charges = new List<Charge>()
            {
                new Charge(){ChargeCode="aa",Id=1},
                new Charge(){ChargeCode="bb",Id=2},
                new Charge(){ChargeCode="xx",Id=3}
            };
            // change the Value here....
            ChargeList = charges.Select(c => new SelectListItem { Value = c.ChargeCode, Text = c.ChargeCode });
            return Page();
        }
        public async Task<IActionResult> OnPost()
        {
            //convert list into string with comma.....
            Incident.ArrestCharges = String.Join(",", MultiCharge); 
            //..... 
            return RedirectToPage("Index");
        }
    
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search