Select with optgroup via ajax and mvc controller

I had a need to bind a select control to a set of json data delievered via a  $ ajax call…

 

C# method

 public List<SelectListItem> changeHistorySelect { get; private set; }

        [AcceptVerbs(HttpVerbs.Get)]

        public ActionResult GetChangeHistorySelect(Int64 Id)
        {
            var selectListGroup_Info = new SelectListGroup() { Name = "Info"};
            var selectListGroup_Allotments = new SelectListGroup() { Name = "Allotments" };
            var selectListGroup_Financial = new SelectListGroup() { Name = "Financial" };
            var selectListGroup_Daily_Status = new SelectListGroup() { Name = "Daily Status" };
            var selectListGroup_Enrollments = new SelectListGroup() { Name = "Enrollments" };

            #region  Misc Code...

            // Build list
            changeHistorySelect = new List<SelectListItem>
            {
              new  SelectListItem
              {
                  Value = "",
                  Text = "- Select -",
                  Selected = true
                 
              },
                new SelectListItem
                {
                    Value = "student",
                    Text = "Student",
                    Group = selectListGroup_Info
                },
                new SelectListItem
                {
                    Value = "altAddresses",
                    Text = "Alternate Addresses",
                    Group = selectListGroup_Info
                },
                new SelectListItem
                {
                    Value = "enrollment",
                    Text = "Enrollment",
                    Group = selectListGroup_Info
                },
                new SelectListItem
                {
                    Value = "enrollmentType",
                    Text = "Enrollment Type",
                    Group = selectListGroup_Info
                },


                new SelectListItem
                {
                    Value = "allotteeInfo",
                    Text = "Allottee Information",
                    Group = selectListGroup_Allotments
                },

                new SelectListItem
                {
                    Value = "earnings",
                    Text = "Earnings",
                    Group = selectListGroup_Financial
                },
                new SelectListItem
                {
                    Value = "deductions",
                    Text = "Deductions",
                    Group = selectListGroup_Financial
                },
                new SelectListItem
                {
                    Value = "reimbursements",
                    Text = "Reimbursements",
                    Group = selectListGroup_Financial
                },
                new SelectListItem
                {
                    Value = "accruals",
                    Text = "Accruals",
                    Group = selectListGroup_Financial
                },
              
            };

            // Now add enrollments to list

            var enrollments = new StudentChangeHistoryService().GetStudentEnrollment(Id);
            foreach (var er in enrollments)
            {
                if (er.sep_dt.ToString() == "1/1/0001 12:00:00 AM")
                {
                    changeHistorySelect.Add(new SelectListItem
                    {
                        Value = er.enr_dt != null ? er.enr_dt.Value.ToString("MM/dd/yyyy") : String.Empty,
                        Text = "Current Enrollment: " + (er.enr_dt != null ? er.enr_dt.Value.ToString("MM/dd/yyyy") : String.Empty) + " - Current",
                        Group = selectListGroup_Daily_Status
                    });
                }
                else
                {
                    changeHistorySelect.Add(new SelectListItem
                    {
                        Value = er.enr_dt != null ? er.enr_dt.Value.ToString("MM/dd/yyyy") : String.Empty,
                        Text = String.Format("Prior Enrollment: {0} - {1} ", er.enr_dt != null ? er.enr_dt.Value.ToString("MM/dd/yyyy") : String.Empty,(er.sep_dt != null ? er.sep_dt.Value.ToString("MM/dd/yyyy"): String.Empty)), 
                        Group = selectListGroup_Daily_Status
                    });
                }


            }

            var jsonResult = Json(changeHistorySelect);
            #endregion

            return Json(changeHistorySelect,JsonRequestBehavior.AllowGet);
        }

Now I need to have the UI call and build selector in javascript


 function getChangeHistorySelect(studentId) {

            $.getJSON('@Url.Action("GetChangeHistorySelect")', { Id: studentId }).done(function (data) {

                console.log(data);
                var result = data;

                var changeHistorySelect = $('#changeHistorySelect');
                changeHistorySelect.empty();
                var groupName = '';
                var groupPreviousName = '';
                $.each(result,
                    function () {
                        // Need an outloop for the option group
                       // debugger;
                        if (this.Group != null) {
                            // Has the group already been added?
                            var group = this.Group;
                            if (groupName === '') {

                                groupName = group.Name;
                                groupPreviousName = groupName;
                                // First Add...
                                changeHistorySelect.append(
                                    $('<optgroup>',
                                        {
                                            label: groupName,
                                            id: groupName
                                        }));


                            } else {
                                groupName = group.Name;
                                // A group has already been added... does it match?
                                if (groupPreviousName === groupName) {
                                    // They Match skip (added select under this group
                                } else {
                                    // They don' match, add new optGroup and contiue on to add selects under it
                                    groupName = group.Name;
                                    groupPreviousName = groupName;

                                    changeHistorySelect.append(
                                        $('<optgroup>',
                                            {
                                                label: groupName,
                                                id: groupName.replace(/\s/g, "")
                                            }));
                                }
                            }
                            // Add Option Group
                            var custom = $('#' + groupName.replace(/\s/g, ""));
                            var option = $("<option></option>");
                            option.val(this.Value);
                            option.text(this.Text);
                            option.appendTo(custom);


                        } else {
                            changeHistorySelect.append(
                                                           $('<option>',
                                                               {
                                                                   value: this.Value
                                                               }).text(this.Text)
                                                       );
                        }

                    });

            });
        }


.

 

 

 

 

 

 

I promise, just wait will ya while I do…

A traditional synchronous callback structure versus asynchronous promise equivalent

synchronous

try {
  var value = myFunction();
  console.log(value);
} catch(err) {
  console.log(err);
}

asynchronous

myFunction().then(function(value) {
  console.log(value);
}).catch(function(err) {
  console.log(err);
});

 

In the sync example you would have to wait until myfunction completed before any more code could be executed.  I am sure you have seen sites where the browser UI seems locked up or non-responsive… this kind of code can be the culprit.

The second example returns a promise for the value, then the rest of the code can carry on.  Like rendering a GRID that will hold the data the promise is fetching…  etc…

 

 

 

WEB API 2

In the past you had to configure http responses like this:

 throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));

or

 return new HttpResponseMessage(HttpStatusCode.OK);

 

Now you can do the following:

 public IHttpActionResult GetProduct(int id)
 {
 var product = products.FirstOrDefault((p) => p.Id == id);
 if (product == null)
 {
 return NotFound();
 }
 return Ok(product);
 }

Here are a few articles about it and a working example download

Download will work in VS 2015 or 2017 – just make sure you unlock after unzipping and use package manager to restore packages in vs.

ApiController helper methods like OK)_, NotFound() and more…

Name Description
System_CAPS_protmethod BadRequest()

Creates a BadRequestResult.

System_CAPS_protmethod BadRequest(ModelStateDictionary)

Creates an InvalidModelStateResult with the specified model state.

System_CAPS_protmethod BadRequest(String)

Creates an T:System.Web.Http.Results.ErrorMessageResult (400 Bad Request) with the specified error message.

System_CAPS_protmethod Conflict()

Creates a ConflictResult (409 Conflict).

System_CAPS_protmethod Content<T>(HttpStatusCode, T)

Creates a <see cref=”T:System.Web.Http.NegotiatedContentResult`1″ /> with the specified values.

System_CAPS_protmethod Content<T>(HttpStatusCode, T, MediaTypeFormatter)

Creates a <see cref=”T:System.Web.Http.FormattedContentResult`1″ /> with the specified values.

System_CAPS_protmethod Content<T>(HttpStatusCode, T, MediaTypeFormatter, MediaTypeHeaderValue)

Creates a <see cref=”T:System.Web.Http.FormattedContentResult`1″ /> with the specified values.

System_CAPS_protmethod Content<T>(HttpStatusCode, T, MediaTypeFormatter, String)

Creates a <see cref=”T:System.Web.Http.FormattedContentResult`1″ /> with the specified values.

System_CAPS_protmethod Created<T>(String, T)

Creates a CreatedNegotiatedContentResult<T> (201 Created) with the specified values.

System_CAPS_protmethod Created<T>(Uri, T)

Creates a CreatedNegotiatedContentResult<T> (201 Created) with the specified values.

System_CAPS_protmethod CreatedAtRoute<T>(String, IDictionary<String, Object>, T)

Creates a CreatedAtRouteNegotiatedContentResult<T> (201 Created) with the specified values.

System_CAPS_protmethod CreatedAtRoute<T>(String, Object, T)

Creates a CreatedAtRouteNegotiatedContentResult<T> (201 Created) with the specified values.

System_CAPS_pubmethod Dispose()

Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.

System_CAPS_protmethod Dispose(Boolean)

Releases the unmanaged resources that are used by the object and, optionally, releases the managed resources.

System_CAPS_pubmethod Equals(Object)

(Inherited from Object.)

System_CAPS_pubmethod ExecuteAsync(HttpControllerContext, CancellationToken)

Executes asynchronously a single HTTP operation.

System_CAPS_protmethod Finalize()

(Inherited from Object.)

System_CAPS_pubmethod GetHashCode()

(Inherited from Object.)

System_CAPS_pubmethod GetType()

(Inherited from Object.)

System_CAPS_protmethod Initialize(HttpControllerContext)

Initializes the ApiController instance with the specified controllerContext.

System_CAPS_protmethod InternalServerError()

Creates an InternalServerErrorResult (500 Internal Server Error).

System_CAPS_protmethod InternalServerError(Exception)

Creates an ExceptionResult (500 Internal Server Error) with the specified exception.

System_CAPS_protmethod Json<T>(T)

Creates a JsonResult<T> (200 OK) with the specified value.

System_CAPS_protmethod Json<T>(T, JsonSerializerSettings)

Creates a JsonResult<T> (200 OK) with the specified values.

System_CAPS_protmethod Json<T>(T, JsonSerializerSettings, Encoding)

Creates a JsonResult<T> (200 OK) with the specified values.

System_CAPS_protmethod MemberwiseClone()

(Inherited from Object.)

System_CAPS_protmethod NotFound()

Creates a NotFoundResult.

System_CAPS_protmethod Ok()

Creates an OkResult (200 OK).

System_CAPS_protmethod Ok<T>(T)

Creates an OkNegotiatedContentResult<T> with the specified values.

System_CAPS_protmethod Redirect(String)

Creates a redirect result (302 Found) with the specified value.

System_CAPS_protmethod Redirect(Uri)

Creates a redirect result (302 Found) with the specified value.

System_CAPS_protmethod RedirectToRoute(String, IDictionary<String, Object>)

Creates a redirect to route result (302 Found) with the specified values.

System_CAPS_protmethod RedirectToRoute(String, Object)

Creates a redirect to route result (302 Found) with the specified values.

System_CAPS_protmethod ResponseMessage(HttpResponseMessage)

Creates a ResponseMessageResult with the specified response.

System_CAPS_protmethod StatusCode(HttpStatusCode)

Creates a T:System.Web.Http.StatusCodeResult with the specified status code.

System_CAPS_pubmethod ToString()

(Inherited from Object.)

System_CAPS_protmethod Unauthorized(AuthenticationHeaderValue[])

Creates an UnauthorizedResult (401 Unauthorized) with the specified values.

System_CAPS_protmethod Unauthorized(IEnumerable<AuthenticationHeaderValue>)

Creates an UnauthorizedResult (401 Unauthorized) with the specified values.

System_CAPS_pubmethod Validate<TEntity>(TEntity)

Validates the given entity and adds the validation errors to the model state under the empty prefix, if any.

System_CAPS_pubmethod Validate<TEntity>(TEntity, String)

Validates the given entity and adds the validation errors to the model state, if any.

Repost: Dependency Injection in ASP.NET Core

By Steve Smith and Scott Addie

ASP.NET Core is designed from the ground up to support and leverage dependency injection. ASP.NET Core applications can leverage built-in framework services by having them injected into methods in the Startup class, and application services can be configured for injection as well. The default services container provided by ASP.NET Core provides a minimal feature set and is not intended to replace other containers.2

View or download sample code (how to download)

…  Click to route to orignal article