简体   繁体   中英

Call controller method multiple times based on input in multiselect from view on submitting form [ASP.NET Core]

Summary of issue:

I have an existing asp.net core application that takes 3 inputs (single input allowed in each) from dropdown and based on those values first validates them and then adds a new record in a DB in say Table_Abc. Eg: 单输入示例 .

I am trying to modify this such that multiple inputs can be selected from the 2nd dropdown but in the backend the number of records it adds to Table_Abc= number of inputs I have selected such that all the values of the records remain same and the only difference are the different inputs from the 2nd dropdown.

Front end(VIEW):

Existing code in View (eg: demoview.cshtml):

    <div id="newAccess">
        <div>
            <br />
            <form method="post" id="form-submission-id" asp-action="RequestAccessSubmit">
                <div class="form-horizontal">                   
<div class="form-group">
                        <label class="col-md-3 text-left">User Type:</label>
                        <select asp-items="ViewBag.GroupTypes" id="UserTypeID" class="form-control" name="GroupTypeID">
                            <option value="null">--Select User Types--</option>
                        </select>
                    </div>

                    <div class="form-group">
                        <label class="col-md-3 text-left">Application/Business Group:</label>
                        <select class="form-control" asp-items="ViewBag.ContainerSelect" id="BusinessGroup" name="ContainerID" disabled>
                            <option value="null">--Select Business Group--</option>
                        </select>
                    </div>

                    <div class="form-group">
                        <label class="col-md-3 text-left">Requested Permissions:</label>
                        <select asp-items="ViewBag.APSelect" id="RequestedPermission" class="form-control fa-align-left" name="AppPermissionID" disabled>
                            <option value="null">--Select Requested Permission--</option>
                        </select>
                    </div>
                    <div class="form-group">
                        <label class="col-md-3 text-left">Business Justification:</label>
                        <textarea id="Description" name="BusinessJustification" style="width: 40%" rows="4" cols="20" resize="none" class="form-control" placeholder="Provide business justification." required="required" disabled></textarea>
                    </div>

                    </div>


                    <div class="form-group">
                        <div class="col-md-5">
                            <br />
                            <button id="btn-submit-user" type="submit" class="btn btn-success" asp-action="RequestAccessSubmit" disabled>Submit</button>
                            <button id="btn-cancel-request" type="button" class=" btn btn-danger" onclick="redirecttoHome(this); ">Cancel</button>
                        </div>
                    </div>
                </div>
                <br />
            </form>
        </div>
    </div>

In above, successfully changed the view to take multiple input in 2nd dropdown by changing the code from

<select class="form-control" asp-items="ViewBag.ContainerSelect" id="BusinessGroup" name="ContainerID" disabled>

to

<select class="form-control custom-select" multiple asp-items="ViewBag.ContainerSelect" id="BusinessGroup" name="ContainerID" disabled>

Objective (help needed):

Call the method RequestAccessSubmit for each value selected in the app/business group dropdown while the values from other dropdowns remain the same.

Current flow:

Javascript:

<script type="text/javascript">

        $(function () {
            $('#UserTypeID').on('change', null, null, function (e) {
                getGroupsandApplications(e);
            });
            $('#RequestedPermission').on('change', null, null, function (e) {
                ValidatePermissions(e);
            });
            $('#BusinessGroup').on('change', null, null, function (e) {
                ValidatePermissions(e);
            });
        });

Here getGroupsandApplications() is just used to fill the 2nd and 3rd dropdown based on the input in 1st dropdown so that can be safely ignored. As for the ValidatePermissions() javascript function, it calls ValidatePermissions() in controller using the current value of 2nd and 3rd dropdown to validate them(note that this code considers only one value in both of these dropdowns and hence needs to be modified ) -

      function ValidatePermissions(e)
        {
            var businessGroupID = $('#BusinessGroup').val();
            var requestedPermission = $('#RequestedPermission').val();

            if (businessGroupID!='null') {

                $.ajax({
                    url: '@Url.Action("ValidatePermissions")',
                    method: 'GET',
                    cache: false,
                    data: { containerID: businessGroupID, appPermissionID: requestedPermission },
                    success: handleSuccess
                });
            }

        }

Controller:

ValidatePermissions() method -

public JsonResult ValidatePermissions(int containerID, int appPermissionID)
{
    //code to validate containerID and appPermissionID
    var sampleRecord = new sampleModel();
    sampleRecord = _context.Table_Abc
.FirstOrDefault(x => x.UserId.Equals(user.UserID) && x.ContainerId.Equals(containerID) && x.AppPermissionId.Equals(appPermissionID));

return Json(new
            {
                success = true
            });

}

Note that the sampleModel controls the values needed to insert a record in the Table_Abc in the database.

RequestAccessSubmit() method -

[HttpPost]
[Authorize]
public async Task<IActionResult> RequestAccessSubmit(sampleModel xyz)
{
   //some code for variable user

   var sampleRecord = new sampleModel();
   sampleRecord = _context.Table_Abc.FirstOrDefault(x => x.UserId.Equals(user.UserID)
                                                                    && x.ContainerId.Equals(xyz.ContainerId)
                                                                    && x.AppPermissionId.Equals(xyz.AppPermissionId)


 if (sampleRecord == null)
            {
                xyz.BusinessJustification = xyz.BusinessJustification;               
                //fills other needed information in the record
                _context.Table_Abc.Add(xyz);
                await _context.SaveChangesAsync();
            }

return RedirectToAction("Index");


}

Model:

public class UserAccess
    {
        public string BusinessJustification { get; set; }
        public int RequestAccessId { get; set; }
        public int AppPermissionId { get; set; }
        public int ContainerId { get; set; }
        public GroupType GroupType { get; set; }
        //rest of the columns in Table_Abc
    }

Approach:

To modify above code such that both the above methods in the controller are called such that multiple records can be inserted for the different values of containerId

I finally worked out a successful solution. Optimizations to this are most welcome.

Controller: Unchanged

Model: Unchanged

View: Changes in form -

  1. Changed from:
    <form method="post" asp-action="RequestAccessSubmit" id="form-submission-id">

to:

    <form method="post" id="form-submission-id">
  1. Changed from:
    <select asp-items="ViewBag.ContainerSelect" id="BusinessGroup" class="form-control custom-select" multiple name="ContainerID" disabled>

to:

    <select asp-items="ViewBag.ContainerSelect" id="BusinessGroup" class="form-control custom-select" multiple disabled>
  1. Changed from:
<button id="btn-submit-user" type="submit" class="btn btn-success" asp-action="RequestAccessSubmit" disabled>Submit</button>                         

to

<button id="btn-submit-user" type="submit" class="btn btn-success" disabled>Submit</button>

In short, I removed the direct connection of the submit button to RequestAccessSubmit method of controller and removed the value of the 2nd dropdown to the column ContainerID.

Javascript changes:

Added a new function to handle the execution of submit button:

$("#btn-submit-user").click(function (event) {
            event.preventDefault(); 
            var businessGroupID = $('#BusinessGroup').val();

            for (var i = 0; i < businessGroupID.length; i++) {

                if (businessGroupID[i] != 'null') {

                    var requestObj = {
                        BusinessJustification : $('#Description').val(),
                        GroupTypeId : $('#UserTypeID').val(),
                        AppPermissionId : $('#RequestedPermission').val(),
                        ContainerId : businessGroupID[i]
                    };

                    $.ajax({
                        url: '@Url.Action("RequestAccessSubmit")',
                        type: 'POST',
                        data: { 'requestAccess': requestObj },
                        success: handleSuccess
                    });
                }

            }
        });

Modified ValidatePermissions() script method to:

 function ValidatePermissions(e)
        {
            var businessGroupID = $('#BusinessGroup').val();
            var requestedPermission = $('#RequestedPermission').val();

            //validating each selected container id with the selected permission id

            for (var i = 0; i < businessGroupID.length; i++)
            {
                if (businessGroupID[i] != 'null') {
                    $.ajax({
                        url: '@Url.Action("ValidatePermissions")',
                        method: 'GET',
                        cache: false,
                        data: { containerID: businessGroupID[i], appPermissionID: requestedPermission },
                        success: handleSuccess
                    });
                }
            }

        }

My approach towards this requirement is a strongly typed Model binding View . You can use inheritance in your Model classes if you want to keep your database context model the way it is. In this example, I have hard-coded values for my dropdown and listbox. You can tailor that requirement according to your implementation. So here I go:

The Model looks like:

using Microsoft.AspNetCore.Mvc.Rendering;
using System.Collections.Generic;

namespace SampleMultiSelectListExample.Models
{
    //No touchy as per requirement
    public class UserAccess
    {
        //Assumption: This is your textarea
        public string BusinessJustification { get; set; }
        //This is user type id
        public int RequestAccessId { get; set; }
        //This is requested permission id
        public int AppPermissionId { get; set; }
        //This is application/business group which is a multi select
        public int ContainerId { get; set; }
        //public GroupType GroupType { get; set; }
        //rest of the columns in Table_Abc
    }

    //Make our class for our purpose. More of a viewmodel
    public partial class UserAccessData : UserAccess
    {
        //These two variables are to hold only one value as per  requirement
        public string requestAccessId { get; set; }
        public string appPermissionId { get; set; }

        public MultiSelectList RequestAccessIdList { get; set; }
        public MultiSelectList AppPermissionIdList { get; set; }
        public MultiSelectList ContainerIdList { get; set; }

        //Define multiselect list to capture multiple application/business role
        public List<string> containerIds { get; set; }

        //Define our constructor to get these values
        public UserAccessData()
        {
            this.RequestAccessIdList = GetRequestAccessIdList(null);
            this.AppPermissionIdList = GetAppPermissionIdList(null);
            this.ContainerIdList = GetContainerIdList(null);
        }

        public MultiSelectList GetRequestAccessIdList(string[] selectedValues)
        {
            List<Common> getRequestAccessList = new List<Common>()
            {
                new Common() { ID = 1, Name= "User 1" },
                new Common() { ID = 2, Name= "User 2" },
                new Common() { ID = 3, Name= "User 3" },
                new Common() { ID = 4, Name= "User 4" },
                new Common() { ID = 5, Name= "User 5" },
                new Common() { ID = 6, Name= "User 6" },
                new Common() { ID = 7, Name= "User 7" }
            };

            return new MultiSelectList(getRequestAccessList, "ID", "Name", selectedValues);
        }

        public MultiSelectList GetAppPermissionIdList(string[] selectedValues)
        {
            List<Common> getAppPermissionList = new List<Common>()
            {
                new Common() { ID = 1, Name= "User 1" },
                new Common() { ID = 2, Name= "User 2" },
                new Common() { ID = 3, Name= "User 3" },
                new Common() { ID = 4, Name= "User 4" },
                new Common() { ID = 5, Name= "User 5" }
            };

            return new MultiSelectList(getAppPermissionList, "ID", "Name", selectedValues);
        }

        //This will multi selectable as per our view
        public MultiSelectList GetContainerIdList(string[] selectedValues)
        {
            List<Common> getContainerList = new List<Common>()
            {
                new Common() { ID = 1, Name= "User 1" },
                new Common() { ID = 2, Name= "User 2" },
                new Common() { ID = 3, Name= "User 3" }
            };

            return new MultiSelectList(getContainerList, "ID", "Name", selectedValues);
        }
    }


    //Generic class to match what a select list or dropdown list or combobox or radio box requires
    public class Common
    {
        public int ID { get; set; }
        public string Name { get; set; }
    }
}

The Controller looks like:

using System;
using Microsoft.AspNetCore.Mvc;
using SampleMultiSelectListExample.Models;

namespace SampleMultiSelectListExample.Controllers
{
    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            //Here we get our items as required
            //UserAccessData useraccessdata = new UserAccessData();
            //If you are getting your data from a db, you can do something like this
            //useraccessdata.ContainerIdList = GetAllContainerId(null);
            //useraccessdata.RequestAccessIdList = GetAllRequestAccessId(null);
            //useraccessdata.AppPermissionIdList= GetAllAppPermissionIdList(null);

            //Now since I am hardcoding these values, I can directly send my model to the view to render

            return View(new UserAccessData());
        }

        //Just an example implementation of how this would look via a webservice:
        //private MultiSelectList GetAllContainerId(string[] selectedValues)
        //{
        //    //Just an example using HttpClient to call a webservice to get this data.
        //    var client = clientFactory.CreateClient(baseAPIUrl);
        //    var containerIdDTO = httpClient.GetAsync<UserDtoList>(new Uri(new Uri(baseAPIUrl), $"Master/getUserDto").AbsoluteUri).Result;
        //    //return containerIdDTO.data;
        //    List<Common> allContainerIds = new List<Common>();
        //    foreach (var item in containerIdDTO)
        //    {
        //        Common common = new Common();
        //        common.ID = item.id;
        //        common.Name = item.fullName;
        //        allContainerIds.Add(common);
        //    }

        //    return new MultiSelectList(allContainerIds, "ID", "Name", selectedValues);
        //}

        //Now process your selected data as required
        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult ProcessInformation(UserAccessData useraccessdata)
        {
            //Our main model which is untouched
            UserAccess user = new UserAccess();
            user.AppPermissionId = Convert.ToInt32(useraccessdata.appPermissionId);
            user.RequestAccessId = Convert.ToInt32(useraccessdata.requestAccessId);
            user.BusinessJustification = useraccessdata.BusinessJustification;

            //Now for every container list, do your processing
            foreach(var containerid in useraccessdata.containerIds)
            {
                //Insert in your DB here 
                //var insert = CallDBInsert(user);
            }
            return View();
        }
    }
}

And the View looks like:

@using SampleMultiSelectListExample.Models
@model UserAccessData
@{
    ViewData["Title"] = "Home Page";
}
<div class="panel-heading">
    <h2>Sample MultiSelectList Example</h2>
</div>

<div class="row">

    <br />
    <div id="newAccess">
        <div>
            <br />
            @using (Html.BeginForm("ProcessInformation", "Home", FormMethod.Post, new { @id = "form-submission-id", @class = "" }))
            {
                @Html.AntiForgeryToken()
                <div class="form-horizontal">
                    <div class="form-group">
                        <label class="col-md-3 text-left">User Type:</label>
                        @Html.DropDownListFor(m => m.requestAccessId, Model.RequestAccessIdList, new { @id= "UserTypeID", @class = "form-control", placeholder = "Select User Type", @required = "required", @autocomplete = "off" })
                    </div>

                    <div class="form-group">
                        <label class="col-md-3 text-left">Application/Business Group:</label>
                        @Html.ListBoxFor(m => m.containerIds, Model.ContainerIdList, new { @id = "ContainerID", @name = "choices-multiple-remove-button", @class = "form-control", placeholder = "Select Application/Business Group", @required = "required", @autocomplete = "off", @multiple = "multiple" })
                    </div>

                    <div class="form-group">
                        <label class="col-md-3 text-left">Requested Permissions:</label>
                        @Html.DropDownListFor(m => m.appPermissionId, Model.AppPermissionIdList, new { @id = "AppPermissionID", @class = "form-control", placeholder = "Select Requested Permission", @required = "required", @autocomplete = "off" })

                    </div>
                    <div class="form-group">
                        <label class="col-md-3 text-left">Business Justification:</label>
                        @Html.TextAreaFor(m => m.BusinessJustification, new { @id = "Description", @class = "form-control", placeholder = "Provide business justification", @cols="20",@rows="4",@resize="none", @required = "required", @autocomplete = "off" })

                    </div>

                </div>

                <div class="form-group">
                    <div class="col-md-5">
                        <br />
                        <button id="btn-submit-user" type="submit" class="btn btn-success">Submit</button>
                        <button id="btn-cancel-request" type="button" class=" btn btn-danger" onclick="location.href='@Url.Action("Index", "Home")'">Cancel</button>
                    </div>
                </div>
            }
        </div>
        <br />

    </div>
</div>

You can find this project on my GitHub repository .

I hope this helps someone who is having a similar requirement.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM