1

Ok so I basically have an ajax call retrieving values from html select multiple tag ... Thing is I'm not able to pass these values into my controller (I'm getting a null reference in my controller)

Check types: $('select#opt2').val() in the ajax call. avion: $('select#opt1').val() isn't a multiple values so it works fine. When I alert($('select#opt2').val()) I get values like: GC,VSG,AA7... (They are separated by ",")

Here's my code:

AJAX

$('select#opt2').change(function () {
            $.ajax({
                url: '@Url.Action("RetournerPostes", "Home", new { area = "Avion" })',
                data: { avion: $('select#opt1').val(), types: $('select#opt2').val() },
                type: 'POST',
                dataType: 'JSON',
            //Rest of code

CONTROLLER This is where I get the null reference for variable "types"

[HttpPost]
    public async Task<ActionResult> RetournerPostes(string avion,List<string> types)
    {
       //Rest of action

Tell me if you need any more information. Thanks!

EDIT

Added fiddle: https://jsfiddle.net/f73jxo5v/

2
  • val() returns an array for multiple. However output of $('select#opt2').val() seems to be string try to convert it to array. try types: $('select#opt2').val().split(',') once Commented Aug 17, 2017 at 12:52
  • It tells me it doesn't generate the .split() attribute Commented Aug 17, 2017 at 12:54

5 Answers 5

2

If you bind comma separated value (CSV) a lot, the easiest and maintainable way is to create a custom model binder called CommaSeparatedModelBinder.

It is not common to capture select's change event, and make an Ajax call whenever user selects an option. But it is up to you.

enter image description here

enter image description here

[HttpGet]
public ActionResult RetournerPostes()
{
    return View();
}

[HttpPost]
public ActionResult RetournerPostes(string avion, 
    [ModelBinder(typeof(CommaSeparatedModelBinder))] int[] types)
{
    return View();
}

View

@using (Html.BeginForm())
{
    <select id="opt1">
        <option value="1">1</option>
        <option value="2">2</option>
        <option value="3">3</option>
        <option value="4">4</option>
    </select>

    <select id="opt2" multiple>
        <option value="1">1</option>
        <option value="2">2</option>
        <option value="3">3</option>
        <option value="4">4</option>
    </select>
    <input type="submit" value="Submit"/>
}

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script type="text/javascript">
    $('select#opt2').change(function () {
        var data = JSON.stringify({ avion: $('select#opt1').val(), types: $('select#opt2').val() });
        console.log(data);
        $.ajax({
            url: '@Url.Action("RetournerPostes", "Home")',
            data: data,
            type: 'POST',
            contentType: "application/json",
            dataType: 'JSON',
            success: function(msg) {
                console.log(msg);
            }
        });
    });
</script>

CommaSeparatedModelBinder

public class CommaSeparatedModelBinder : DefaultModelBinder
{
    private static readonly MethodInfo ToArrayMethod = typeof(Enumerable).GetMethod("ToArray");

    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        return BindCsv(bindingContext.ModelType, bindingContext.ModelName, bindingContext)
                ?? base.BindModel(controllerContext, bindingContext);
    }

    protected override object GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder)
    {
        return BindCsv(propertyDescriptor.PropertyType, propertyDescriptor.Name, bindingContext)
                ?? base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder);
    }

    private object BindCsv(Type type, string name, ModelBindingContext bindingContext)
    {
        if (type.GetInterface(typeof(IEnumerable).Name) != null)
        {
            var actualValue = bindingContext.ValueProvider.GetValue(name);

            if (actualValue != null)
            {
                var valueType = type.GetElementType() ?? type.GetGenericArguments().FirstOrDefault();

                if (valueType != null && valueType.GetInterface(typeof(IConvertible).Name) != null)
                {
                    var list = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(valueType));

                    foreach (var splitValue in actualValue.AttemptedValue.Split(new[] { ',' }))
                    {
                        if (!String.IsNullOrWhiteSpace(splitValue))
                            list.Add(Convert.ChangeType(splitValue, valueType));
                    }

                    if (type.IsArray)
                        return ToArrayMethod.MakeGenericMethod(valueType).Invoke(this, new[] { list });

                    return list;
                }
            }
        }

        return null;
    }
}

Original Source: CommaSeparatedModelBinder.cs

Sign up to request clarification or add additional context in comments.

2 Comments

Wow thanks for the example and the time you put into this! I'm trying it before closing the case but thanks again man!
Whenever possible please include contentType: "application/json" and JSON.stringify to ensure data is in Json format.
1

That's because what you are passing as types, are a string object and not as string list.

You will have to cast the types variable into an array of string:

var array = string.split(','); 

and pass it as an array to the method, not as a list:

[HttpPost]
    public async Task<ActionResult> RetournerPostes(string avion, string[] types)
    {

6 Comments

var array = $('select#opt2').val().split(',') doesn't work, it tells me it doesn't generate the property
types: $('select#opt2').val().split(',')
it does the same :/
then types: $('select#opt2').val().split(' '); this way or the other, you will have to use split.
I'm only trying to say that when I try to use split I get a javascript error
|
1

Hope this will sort out your problem:

       $('select#opt2').change(function () {
        var stringArray = new Array();
        stringArray =$("#opt2>option").map(function() { return $(this).text(); }).get();
        var selectedValue = $('select#opt2').val();
        $.ajax({
            url: '@Url.Action("RetournerPostes", "Home", new { area = "Avion" })',
            data: {avion: selectedValue, types: stringArray},
            type: 'POST',
            dataType: 'JSON',
        //Rest Code

3 Comments

So I get no error but "avion" and "types" are null in my controller
Yes exact same except I changed var selectedValue = $('select#opt2').val(); for var selectedValue = $('select#opt1').val();
I have changed code for you. hope this will resolve your problem
0

if your types = "GC,VSG,AA7" then you are passing a string, not a list of strings, so:

public async Task<ActionResult> RetournerPostes(string avion, string types){
    var myArray = types.Split(','); // split into an array
}

1 Comment

Tried it, this way I was not even getting to my controller method
0
types= []
var sel = $('#selectID');
for (var i=0, n=sel.options.length;i<n;i++) { // looping over the options
  if (sel.options[i].value) types.push(sel.options[i].value);
}

then pass types to your controller without changing ur server code

1 Comment

I'm getting an error telling me that I can't get the length of an undefined object

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.