1

I have two lists (list-left and list-right) prepared and populate. Then I have this JS code to move elements from one list to another. And it works fine.

$(function () {
    $('body').on('click', '.list-group .list-group-item', function () {
        $(this).toggleClass('active');
    });
    $('.list-arrows button').click(function () {
        var $button = $(this), actives = '';
        if ($button.hasClass('move-left')) {
            actives = $('.list-right ul li.active');
            actives.clone().appendTo('.list-left ul');
            actives.remove();
        } else if ($button.hasClass('move-right')) {
            actives = $('.list-left ul li.active');
            actives.clone().appendTo('.list-right ul');
            actives.remove();
        }        
    });
});

But honestly I don't know how to pass the result back to codebehind when user clicks the submit button on the form?

This is my cshtml code:

@using AuthDatabase.Entities
@using Identity.Models
@model RoleEdit

@{
    ViewData["Title"] = "Edit role";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h1>@ViewData["Title"]</h1>
<br />
<h4>Roles</h4>
<hr />
<div asp-validation-summary="All" class="text-danger"></div>

<form method="post">
    <input type="hidden" name="roleName" value="@Model.Role.Name" />
    <input type="hidden" name="roleId" value="@Model.Role.Id" />
    <div class="row">
        <div class="dual-list list-left col-md-5">
            <h5>No roles</h5>
            <div class="well text-right">
                <div class="row">
                    <div class="col-md-10">
                        <div class="input-group">
                            <span class="input-group-addon glyphicon glyphicon-search"></span>
                            <input type="text" name="SearchDualList" class="form-control" placeholder="search" />
                        </div>
                    </div>
                </div>
                <ul class="list-group">
                    @foreach (AppUser user in Model.NonMembers)
                    {
                        <li class="list-group-item">@user.UserName</li>
                    }
                </ul>
            </div>
        </div>
        <div class="list-arrows col-md-1 text-center">
            <button class="btn btn-default btn-sm move-left" type="button">
                <span class="btn btn-primary"><</span>
            </button>

            <button class="btn btn-default btn-sm move-right" type="button">
                <span class="btn btn-primary">></span>
            </button>
        </div>
        <div class="dual-list list-right col-md-5">
            <h5>With roles</h5>
            <div class="well">
                <div class="row">
                    <div class="col-md-10">
                        <div class="input-group">
                            <input type="text" name="SearchDualList" class="form-control" placeholder="search" />
                            <span class="input-group-addon glyphicon glyphicon-search"></span>
                        </div>
                    </div>
                </div>
                <ul class="list-group">
                    @foreach (AppUser user in Model.Members)
                    {
                        <li class="list-group-item">@user.UserName</li>
                    }
                </ul>
            </div>
        </div>
    </div>
    <br />
    <a asp-action="Index" class="btn btn-secondary">Back to List</a>
    <button type="submit" class="btn btn-primary">Save</button>
</form>

@section Scripts
{
    <script src="~/js/lists.js" asp-append-version="true"></script>
}

And my controller:

       public async Task<IActionResult> Edit(string id)
        {
            IdentityRole role = await _roleManager.FindByIdAsync(id);
            List<AppUser> members = new List<AppUser>();
            List<AppUser> nonMembers = new List<AppUser>();
            foreach (AppUser user in _userManager.Users)
            {
                var list = await _userManager.IsInRoleAsync(user, role.Name) ? members : nonMembers;
                list.Add(user);
            }
            return View(new RoleEdit
            {
                Role = role,
                Members = members,
                NonMembers = nonMembers
            });
        }



        [HttpPost]
        public async Task<IActionResult> Edit(RoleModification model)
        {
            IdentityResult result;
            if (ModelState.IsValid)
            {
                foreach (string userId in model.AddIds ?? new string[] { })
                {
                    AppUser user = await _userManager.FindByIdAsync(userId);
                    if (user != null)
                    {
                        result = await _userManager.AddToRoleAsync(user, model.RoleName);
                        if (!result.Succeeded)
                            Errors(result);
                    }
                }
                foreach (string userId in model.DeleteIds ?? new string[] { })
                {
                    AppUser user = await _userManager.FindByIdAsync(userId);
                    if (user != null)
                    {
                        result = await _userManager.RemoveFromRoleAsync(user, model.RoleName);
                        if (!result.Succeeded)
                            Errors(result);
                    }
                }
            }

            if (ModelState.IsValid)
                return RedirectToAction(nameof(Index));
            else
                return await Edit(model.RoleId);
        }

The [HttpPost] Edit action requires RoleModification object with two arrays of strings (changes on the lists -> it is now based on previous solution without jquery). It would be also suitable to have only one list-right passed.

 public class RoleModification
    {
        [Required]
        public string RoleName { get; set; }
        public string RoleId { get; set; }
        public string[] AddIds { get; set; }
        public string[] DeleteIds { get; set; }
    }
derloopkat
  • 6,232
  • 16
  • 38
  • 45

3 Answers3

0

change this to

@using(Html.BeginForm("Action","Controller")){your html here}

you can also do this

$.ajax({
url:'/controller/action',
data:form,
success:function(){alert('submitted');},
error:function(x,y,z){console.log(x,y,z);}
});

basically mvc parser search for elements with name tag same as model btw

 public string[] AddIds { get; set; }
 public string[] DeleteIds { get; set; }

these two fields are tricky

var addIds = ['a','b'];

and same for DeleteIds and then create a parent object that holds all the fields required for backend worth to say you can use razor in js with @ and js in razor with @: so you can do something like this

@foreach(var id in AddIds){
 @:addIds.add(@id);
}

and last but not least you can hook an event and listen to it and fire submit

$('window').ready(function(){
$('#yourSubmitButtonId').on('click',function(){
$('form').submit();
});
});
Alireza Madad
  • 151
  • 2
  • 13
  • please mark as answer if you think you have your answer otherwise I am happy to add information – Alireza Madad Aug 02 '20 at 10:54
  • Still stuck. I managed to do something like this: `$('window').ready(function () { $('#submitBtn').on('click', function () { let addIds = []; $('.list-right ul li').each(function (index) { addIds.push($(this).text()); }); $('form').submit(); }); });` It's fired when user saves the form and prepares an array named addIds with a proper list of Ids. But still addIds is not binded with model required by httpPost action. – Krzysztof Patra Aug 04 '20 at 21:58
  • basically when you create multi instance with same name mvc parser make it as an array thats awesome so foreach loop on forexample addIds and create forexample hidden elements with exact name addIds in each element and mvc parser cast it to string[] addIds – Alireza Madad Aug 11 '20 at 19:53
0
  $('#submitBtn').on('click', function () {           
  let addIds = [];            
 $('.list-right ul li').each(function (index) {                
 addIds.push($(this).text());             
});  
ajaxSubmit(addIds);          
       });     
});
function ajaxSubmit(input){
$.ajax({
url:'/controller/action',
method:'POST',
data:input --- or {input}
success:function(){
console.log('success');},
error:function(x,y,z){
console.log(x,y,z)};}
}); 
}

and your controller muset be like this

public ActionResult Edit(List<string> input)
Alireza Madad
  • 151
  • 2
  • 13
  • at full lenght you can read this https://stackoverflow.com/questions/39129555/add-item-into-list-from-view-and-pass-to-controller-in-mvc5/39193087 @Krzysztof Patra – Alireza Madad Aug 05 '20 at 04:30
  • Thanks for help. I was looking for something simpler (without using Ajax). Thought it's possible to send the modified data back to a binded model in MVC. Althought, solution with Ajax works well. – Krzysztof Patra Aug 09 '20 at 20:25
0

cshtml

@model HelloWorldMvcApp.SampleViewModel
    @{
        Layout = null;
    }

<!DOCTYPE html>
<!-- template from http://getbootstrap.com/getting-started -->

<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width,initial-scale=1">
        <title>Bootstrap 101 Template</title>
    </head>
    
    <body>
        @using(Html.BeginForm("GetAnswer","Home"))
        {
            
            <ul>
                @foreach(var item in Model.questions)
                {
                <input type="hidden" value="@item" name="questions"/>
                <li>@item</li>
                }
                @foreach(var item2 in Model.questions2)
                {
                <input type="hidden" value="@item2"  name="questions"/>
                <li>@item2</li>
                }
            </ul>
        @Model.text
        <input type="hidden" value="asd3" name="text"/>
        <input type="submit" value="submit"/>
        }
      
        <!-- JS includes -->
        <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
        <script src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
    
        <script src="//ajax.aspnetcdn.com/ajax/jquery.validate/1.11.1/jquery.validate.min.js"></script>
        <script src="//ajax.aspnetcdn.com/ajax/mvc/4.0/jquery.validate.unobtrusive.min.js"></script>
        
    
    </body>
</html>

ViewModel

using System;
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
namespace HelloWorldMvcApp
{
    public class SampleViewModel
    {
        public SampleViewModel()
        {
            this.questions = new string[2];
            this.questions2 = new string[2];
        }
        public string[] questions { get; set; }
        public string[] questions2 { get; set; }
        public string text {get;set;}
    }

    
}

controller

using System;
using System.Web.Mvc;
using System.Collections.Generic;
using System.Linq;
namespace HelloWorldMvcApp
{
    public class HomeController : Controller
    {
        [HttpGet]
        public ActionResult Index()
        {
            SampleViewModel model = new SampleViewModel();
            model.questions.SetValue("Test Data",0);
            model.questions.SetValue("Test Data2",1);
            model.questions2.SetValue("Test Data",0);
            model.questions2.SetValue("Test Data2",1);
            return View(model);
        }


        [HttpPost]
        public ActionResult GetAnswer(SampleViewModel model)
        {               
            
                return View(model);
            
        }
        
    
    }
}

as you can see it passes array of string and string as object to mvc controller

Alireza Madad
  • 151
  • 2
  • 13