javascriptasp.net-corerazor-pagescascadingdropdown

Cascading DropDown Lists - What am I missing?


I've been following a tutorial on how to have one dropdown cascade from another, but am unable to get it to work. Any assistance would be greatly appreciated. I do not know javascript well, so fudging my way thru. I am not entirely sure I have the URL portion correct?

I am attempting to populate the ArrestDept dropdown and then filter and populate the ArrestOfficer dropdown based on the selected ArrestDept.

Here is what I have so far:

Relevant Part of View:

                <td style="width: 20%">
                        <div class="mb-3">
                            <label asp-for="Incident.ArrestDept"></label>
                            <select id="agency" class="form-select"></select>
                        </div>
                </td>
                <td style="width: 20%">
                    <div class="mb-3">
                        <label asp-for="Incident.ArrestOfficer"></label>
                        <select id="officer" class="form-select"></select>
                    </div>
                </td>

@section Scripts at bottom of View:

@section Scripts
{
    <partial name="_ValidationScriptsPartial" />

    <script type="text/javascript">
        $(document).ready(function () {
        $('#agency').attr('disabled', true);
        $('#officer').attr('disabled', true);
        LoadAgencies();
    });

    function LoadAgencies() {
        $('#agency').empty();

        $.ajax({
            url: '/CreateModel/GetAgencies',
            success: function (response) {
                if (response != null && response != undefined && response.length > 0) {
                    $('#agency').attr('disabled', false);
                    $('#agency').append('
                        <option>---Select Arresting Agency---</option>');
                    $('#officer').append('
                        <option>---Select Arresting Officer---</option>');
                    $.each(response, function (i, data) {
                        $('#agency').append('
                        <option value=' + data.id + '>' + data.AgencyName + '</option>');
                    });
                }
                else {
                    $('#agency').attr('disabled', true);
                    $('#officer').attr('disabled', true);
                    $('#agency').append('
                        <option>---Arresting Agencies Not Available---</option>');
                    $('#officer').append('
                        <option>---Arresting Officers Not Available---</option>');
                }
            },
            error: function (error) {
                alert(error);
            }
        });
    }
    }
    function LoadOfficers(agencyId) {
        $('#officer').empty();

        $.ajax({
            url: '/CreateModel/GetOfficers?Id=' + agencyId,
            success: function (response) {
                if (response != null && response != undefined && response.length > 0) {
                    $('#officer').attr('disabled', false);
                    $('#officer').append('
                        <option>---Select Arresting Officer---</option>');
                    $.each(response, function (i, data) {
                        $('#officer').append('
                        <option value=' + data.id + '>' + data.OfficerDisplayName + '</option>');
                    });
                }
                else {
                    $('#officer').attr('disabled', true);
                    $('#officer').append('
                        <option>---Arresting Officers Not Available---</option>');
            }
        },
        error: function (error) {
            alert(error);
        }
    });
    </script>

    }

.cs for the View:

using DWITracker.Data;
using DWITracker.Model;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
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> DisplayCityData { get; set; }
    public IEnumerable<County> DisplayPIAddressCountyData { get; set; }
    public IEnumerable<Ethnicity> DisplayPIEthnicityData { get; set; }
    public IEnumerable<ArrestMethod> DisplayArrestMethodData { get; set; }
    public IEnumerable<Test> DisplayTestGivenData { get; set; }
    public IEnumerable<Charge> DisplayChargeData { get; set; }
    public IEnumerable<DrinkLocation> DisplayLastDrinkData { get; set; }
    public IEnumerable<DrugRecognitionExpert> DisplayDrugExpertData { get; set; }

    public async Task OnGet()
    {
        await _db.City.Select(a => a.CityName).ToListAsync();
        DisplayCityData = 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();
        await _db.ArrestMethod.Select(a => a.ArrestMethodDesc).ToListAsync();
        DisplayArrestMethodData = await _db.ArrestMethod.ToListAsync();
        await _db.Test.Select(a => a.TestDesc).ToListAsync();
        DisplayTestGivenData = await _db.Test.ToListAsync();
        await _db.Charge.Select(a => a.ChargeCode).ToListAsync();
        DisplayChargeData = await _db.Charge.ToListAsync();
        await _db.DrinkLocation.Select(a => a.LastDrinkLocation).ToListAsync();
        DisplayLastDrinkData = await _db.DrinkLocation.ToListAsync();
        await _db.DrugRecognitionExpert.Select(a => a.DrugRecExpert).ToListAsync();
        DisplayDrugExpertData = await _db.DrugRecognitionExpert.ToListAsync();
    }

    public JsonResult GetAgencies()
    {
        var agencies = _db.Agency.OrderBy(x => x.AgencyName).ToList();
        return new JsonResult(agencies);
    }

    public JsonResult GetOfficers(int id)
    {
        var officers = _db.Officer.Where(x => x.Agency.Id == id).OrderBy(x => x.OfficerDisplayName).ToList();
        return new JsonResult(officers);
    }


    public async Task<IActionResult> OnPost()
    {
        if (ModelState.IsValid)
        {
            _db.Incident.Add(Incident);
            await _db.Incident.AddAsync(Incident);
            await _db.SaveChangesAsync();
            TempData["success"] = "Incident added successfully.";
            return RedirectToPage("Index");
        }
        return Page();
    }

}

Relevant part of Incident Model:

public class Incident
    {
        [Key]
        public int Id { get; set; }
        [Display(Name = "Arresting Dept")]
        public string? ArrestDept { get; set; }
        [Display(Name = "Arresting Officer")]
        public string? ArrestOfficer { get; set; }
    }

Relevant part of Agency Model:

    public class Agency
    {
        [Key]
        public int Id { get; set; }
        [Required]
        [Display(Name = "Agency")]
        public string AgencyName { get; set; }
    }

Relevant Part of Officer Model:

    public class Officer
    {
        [Key]
        public int Id { get; set; }
        [Display(Name ="Officer Name (Last, First, MI)")]
        public string? OfficerDisplayName { get; set; }
        [Display(Name = "First Name")]
        public string? OfficerFirstName { get; set; }
        [Display(Name = "MI")]
        public string? OfficerMiddleInitial { get; set; }
        [Display(Name = "Last Name")]
        public string? OfficerLastName { get; set; }
        public Agency Agency { get; set; }
    }

***An additional question is if this would be easier to accomplish using ONE table as I can easily combine the Officer and Agency models into ONE table. Actually, this would be my preference, but have not been able to find a tutorial that addresses how to do this.

I'm thinking I could easily eliminate the Agency model and simply combine them on the Officer model, so have edited to add the Agency to the Officer model. There could be many officers to a single 'OfficerAgency'.

using System.ComponentModel.DataAnnotations;

namespace DWITracker.Model
{
    public class Officer
    {
        [Key]
        public int Id { get; set; }
        [Display(Name ="Officer Name (Last, First, MI)")]
        public string? OfficerDisplayName { get; set; }
        [Display(Name = "First Name")]
        public string? OfficerFirstName { get; set; }
        [Display(Name = "MI")]
        public string? OfficerMiddleInitial { get; set; }
        [Display(Name = "Last Name")]
        public string? OfficerLastName { get; set; }
        [Display(Name = "Agency")]
        public string? OfficerAgency { get; set; }
        [Display(Name = "Added/Updated By")]
        public string UpdatedBy { get; set; }
        [Display(Name = "Date Added/Updated")]
        [DataType(DataType.Date)]
        public DateTime UpdateDate { get; set; }
        public Agency Agency { get; set; }
    }
}

How would this change the code? Hoping it would make it simpler?

Just not sure how I would change the .cs to work on the single table, specifically the OnGetGetOfficers(int id):

    public JsonResult OnGetGetAgencies()
    {
        var agencies = _db.Officer.OrderBy(x => x.OfficerAgency).ToList();
        return new JsonResult(agencies);
    }

    public JsonResult OnGetGetOfficers(int id)
    {
        var officers = _db.Officer.Where(x => x.OfficerAgency == id).OrderBy(x => x.OfficerDisplayName).ToList();
        return new JsonResult(officers);
    }

Solution

  • Your js contains multiple problems here:

    1.You add an extra } in LoadAgencies function.

    2.You miss a } in LoadOfficers function.

    3.You can see each time you use append, the string in this method you always write in a new line, you need concatenate strings in JavaScript using the + operator like below(just an example, your js contains many this type problems):

    $('#agency').append(''+
        '<option>---Select Arresting Agency---</option>');
    

    Or just move to the same line:

    $('#agency').append('<option>---Select Arresting Agency---</option>');
    

    4.The response data is camel case format, for example, you need change data.AgencyName to data.agencyName:

    $('#agency').append('<option value=' + data.id + '>' + data.agencyName + '</option>');
    

    5.Razor Pages routing is not like MVC, Razor pages uses OnGet and OnPost to deal with the Http Get and Post request. And the url is related to the PageModelName and folder name, e.g: IndexModel in Pages/Student folder, the url is:/Student/Index. If it is just in Pages folder, the url is:/Index. If you need another Get or Post method in current PageModel, you need define the method name like: OnGetHandlerName or OnPostHandlerName. The url is: /FolderName/PageModelName?handler=HandlerName.


    Whole working code:

    Page

    @page
    @model CreateModel
    <table>
        <tr>
            <td style="width: 20%">
                <div class="mb-3">
                    <label asp-for="Incident.ArrestDept"></label>
                                                                     @*add onchange function*@
                    <select id="agency" class="form-select" onchange="LoadOfficers(this.value)"></select>  
                </div>
            </td>
            <td style="width: 20%">
                <div class="mb-3">
                    <label asp-for="Incident.ArrestOfficer"></label>
                    <select id="officer" class="form-select"></select>
                </div>
            </td>
        </tr>
    </table>
    

    JS in page:

    @section Scripts
        {
        <partial name="_ValidationScriptsPartial" />
    
        <script type="text/javascript">
                $(document).ready(function () {
                $('#agency').attr('disabled', true);
                $('#officer').attr('disabled', true);
                LoadAgencies();
            });
    
            function LoadAgencies() {
                $('#agency').empty();
    
                $.ajax({
                    url: '/Create?handler=GetAgencies',   //change here....
                    success: function (response) {
                        if (response != null && response != undefined && response.length > 0) {
                            $('#agency').attr('disabled', false);
                            $('#agency').append('<option>---Select Arresting Agency---</option>');//change to one line...
                            $('#officer').append('<option>---Select Arresting Officer---</option>');//change to one line...
                            $.each(response, function (i, data) {
                                                                                    //change to camel case here...
                                $('#agency').append('<option value=' + data.id + '>' + data.agencyName + '</option>');
                            });
                        }
                        else {
                            $('#agency').attr('disabled', true);
                            $('#officer').attr('disabled', true);
                            $('#agency').append('<option>---Arresting Agencies Not Available---</option>');//change to one line...
                            $('#officer').append('<option>---Arresting Officers Not Available---</option>');//change to one line...
                        }
                    },
                    error: function (error) {
                        alert(error);
                    }
                });
            }
            //}
            function LoadOfficers(agencyId) {
                $('#officer').empty();
    
                $.ajax({
                    url: '/Create?handler=GetOfficers&Id=' + agencyId,  //change here....
                    success: function (response) {
                        if (response != null && response != undefined && response.length > 0) {
                            $('#officer').attr('disabled', false);
                            $('#officer').append('<option>---Select Arresting Officer---</option>'); //change to one line...
                            $.each(response, function (i, data) {
                                                                                         //change to camel case here...
                                $('#officer').append('<option value=' + data.id + '>' + data.officerDisplayName + '</option>');
                            });
                        }
                        else {
                            $('#officer').attr('disabled', true);
                            $('#officer').append('<option>---Arresting Officers Not Available---</option>'); //change to one line...
                    }
                },
                error: function (error) {
                    alert(error);
                }
            });
        }  //add this '}'
        </script>
    
    }
    

    PageModel

    public JsonResult OnGetGetAgencies()
    {
        //...
        return new JsonResult(agencies);
    }
    
    public JsonResult OnGetGetOfficers(int id)
    {
        //...
        return new JsonResult(officers);
    }