Home > database >  Submit button sending blank(No data) Data MVC ASP.Net
Submit button sending blank(No data) Data MVC ASP.Net

Time:09-23

In a project of Core MVC, ASP.Net 5, I have a simple task of allowing Super Admin to Update the user role. But only thing I have managed till now is make the submit button reach till controller.

But the data rendered is blank. I am trying to send the userid back to controller to update the record and then return the updated user list as view.

I am displaying the users in a table and every row has role update option.

My code piece from View:

    @using (Html.BeginForm("SuperAdminEdit", "Referral", FormMethod.Post))
{
    <table class="table">
        <thead>
            <tr>
                <th>
                    Email
                </th>
                <th>
                    Role
                </th>

            </tr>
        </thead>
        <tbody>

        @if (Model != null)
        {

            foreach (var item in Model)
            {
                <tr>
                    <td>
                        @item.Key
                    </td>
                    <td>
                        <input asp-for="@item.Key" type="text" id="chosen @item.Key" value="" style="display: none;" />
                       
                        <select id="@item.Key" class="form-control">

                            @{
                                string[] roles = { "User", "Admin", "Super Admin" };

                                foreach (var role in roles)
                                {
                                    if (role.Equals(item.Value))
                                    {
                                        <option selected> @role </option>
                                    }
                                    else
                                    {
                                        <option> @role </option>
                                    }
                                }

                            }

                        </select>

                        <script type="text/javascript">

                            document.getElementById('@item.Key').addEventListener('change', function () {

                                document.getElementById('chosen @item.Key').value = document.getElementById('@item.Key').selectedOptions[0].value;
                                console.log(document.getElementById('chosen @item.Key').value);
                        });
                        </script>

                    </td>
                    <td>

                        <input id="submit @item.Key" type="submit" value="Update" class="btn btn-primary" />

                    </td>
                </tr>
            }
        }


    </tbody>
</table>

}

The Controller handling the Edit :

 [HttpPost]
    public ActionResult SuperAdminEdit(string data)
    {
        Console.WriteLine("New role is " data);
        SqlConnection conn = new SqlConnection...............................
        conn.Open();

//Rest of the Code
}

When I am printing the message from the controller method it is printing but blank for value.

enter image description here

Please help in understanding the issue.

CodePudding user response:

Forms submit are designed to submit the model, which in your case looks to be a collection of users. You're then creating an action with the intention of accepting a new role, but a new role for what item?? As far as that method is concerned you've told it to expect a role, but beyond that it knows nothing. Forms would generally be used in cases where you had a list of Users but visited a page with a form for a single User which would be updated and submitted, rather than a page listing many rows that can be updated individually.

If you are rendering a list of users and want to be able to change each of their roles and submit an update on a per-line basis, you won't want a type "submit" button, but rather a type "button" button that will make an Ajax call back to a POST action on the controller, providing the ID (well, in your case it looks like you are just passing the User Name [Key]) and the updated Role. That way the controller can load the applicable User by name, and update the role for that user before persisting the change.

Normally as a starting point I would recommend loading a view model containing all of the data that the view will need. In your case something like:

[Serializable]
public class UserEditViewModel
{
    public ICollection<UserViewModel> Users { get; set; } = new List<UserViewModel>();
    public ICollection<RoleViewModel> Roles { get; set; } = new List<RoleViewModel>();
}

[Serializable]
public class UserViewModel
{
    public int UserId { get; set; }
    public string Name { get; set; }
    public int? RoleId { get; set; }
}

[Serializable]
public class RoleViewModel
{
    public int RoleId { get; set; }
    public string Name { get; set; }
}

Then when you load your data, use Select or Automapper's ProjectTo to build your collection of Users and the lookup list of Roles, and populate a UserEditViewModel to serve as your page's Model. From there you can build your <select> lists from the model's "Roles" collection (rather than a hard-coded list of role names) and select the appropriate one for each User row.

Updating rows in a dynamic table can be a little daunting, but there are a few things that can help you such as generating unique IDs for controls (which you are doing) and leveraging data- attributes. There are lots of ways to skin a cat and there will be plenty of examples out there to consider, as well as best practices such as where to place your script blocks. (definitely not inside table rows!)

As a simple demonstration of an option:

foreach (var user in Model.Users)
{
   <tr>
       <td>
           @user.Name
       </td>
       <td>
           <select id="[email protected]" class="form-control">
               foreach (var role in Model.Roles)
               {
                   if (role.RoleId == user.RoleId)
                   {
                       <option selected data-role-id=@role.RoleId> @role.Name </option>
                   }
                   else
                   {
                       <option data-role-id=@role.RoleId> @role.Name </option>
                   }
               }
           </select>
       </td>
       <td>
           <input id="update @user.UserId" type="button" value="Update" class="btn btn-primary update-button" data-user-id=@user.UserId />
       </td>
    </tr>
}

The key differences here will be that the page's Model will now be our UserEditViewModel so we have access to a Users collection for our row source and a Roles collection for our lookups. The update button is now not a Submit type button, we give it a class of "update-button" as a marker for jQuery, and a data-user-id attribute for the UserId it is associated to.

In a nominated script block you can use the

$(document).ready(function() {
    $('.update-button').on('click', function(e) {
        var userId = $(this).data("user-id");
        var newRoleId = $('#role-'   userId).find(':selected').data('role-id`);
        updateUserRole(userId, newRoleId);
    });
};

function updateUserRole(userId, newRoleId) {
    $.ajax({
        url: "@Url.Action("UpdateUserRole", "User")",
        type: "POST",
        dataType: "json",
        data: { userId: userId, newRoleId: newRoleId },
        success: function (result) {
            // TODO: Handle result.
        },
        error: function (xhr, status, error) {
            // TODO: Handle error.
        }
    });
}

Here the "data-" attributes are useful for conveying useful details from the model into the control such as what UserId each button should apply to, then using that to find the appropriate select list for the role, and then get the Role ID from the selected option. This does need some error handling and checks for whether a role is actually selected or not.

Then in your controller:

[HttpPost]
public JsonResult UpdateUserRole(int userId, int newRoleId)
{
    try
    {
    // TODO, logic to load the user, update it's role and save changes.
        using(var context = new AppDbContext())
        {
           var user = context.Users.Include(x => x.Role)
               .Single(x => x.UserId == userId);
           var newRole = context.Roles.Single(x => x.RoleId == newRoleId);
           user.Role = newRole;
           context.SaveChanges();
        }
        return Json(new { success = true });
    }
    catch(Exception ex)
    {
        return Json(new { success = false, message = "An unexpected error occurred." });
    }
}

IF you're using EF then you wouldn't be opening SQL Connections but rather instantiating a application DbContext instance, fetching a user, updating it's properties and calling SaveChanges().

This example is not complete code, and written from memory. It should give you a starting point to consider and there are bound to be plenty of examples out there as well as advice on best practices for using Ajax, forms, etc.

  • Related