1

Before doing a form submit for my MVC webpage, I want to see a list of key fields to the controller. However the parameter on the controller is null. My JavaScript is as follows;

   $("#savePropertiesToBeAssigned").click(function (e) {
        e.preventDefault();
        var $propertyIds = $("#propertyRows").find($(".selectProperty:checked"));
        var propertyIds = [];
        $.each($propertyIds, function () {
            var propertyId = $(this).data("msurvey-property-id");
            propertyIds.push(propertyId);
        });

        var $form = $(this).closest("form")[0];
        $form.action += "?PropertyIds=" + propertyIds + "&page=" + GetHiddenField("msurvey-page");
        $form.submit();
    });

The MVC Action is;

   [HttpPost]
    public async Task<ActionResult> AddFromContract(int[] PropertyIds, int? page)
    {
        var pageNumber = page.GetValueOrDefault(1);
        return RedirectToAction("AddFromContract", new { page = pageNumber });
    }

The PropertyIds comes through wrongly as null, whilst the page has the correct value. EDIT - I am displaying the View in response to a comment:

@{
    ViewBag.Title = "Add properties from the contract into the survey";
}

@using (Html.BeginForm("AddFromContract", "PropertySurvey", FormMethod.Post))
{
    <div id="hiddenFields"
         data-msurvey-page="@ViewBag.Page"></div>
    <input type="hidden" id="propertyIds" name="propertyIds" />
    <fieldset>
        <legend>@ViewBag.Title</legend>
        @if (ViewBag.PagedList.Count > 0)
        {
            <section id="PropertyList" style="margin-right: 28px;">
                <p>
                    The properties below have already been added to the contract <b>@SessionObjectsMSurvey.SelectedContract.ContractTitle</b>, but are NOT in this survey
                    <b>@SessionObjectsMSurvey.SelectedContract.SurveyTitle.</b>
                </p>
                <table class="GridTbl">
                    <thead>
                    <tr>
                        <th>UPRN</th>
                        <th>Block UPRN</th>
                        <th>Address</th>
                        <th style="text-align: center;">
                            Select All<br/>
                            <input type="checkbox" id="selectAll"/>
                        </th>
                    </tr>
                    </thead>
                    <tfoot>
                    <tr>
                        <td colspan="3" style="text-align: right;">Select the properties you want to include in the survey, and click on the Save button.</td>
                        <td style="text-align: center;">
                            <button id="savePropertiesToBeAssigned" class="btn-mulalley">
                                Save
                            </button>
                        </td>
                    </tr>
                    </tfoot>
                    <tbody id="propertyRows">
                    @foreach (var property in ViewBag.PagedList)
                    {
                        <tr>
                            <td>@property.UPRN</td>
                            <td>@property.BlockUPRN</td>
                            <td>@property.Address</td>
                            <td style="text-align: center;">
                                <input type="checkbox"
                                       name="selectProperty"
                                       class="selectProperty"
                                       data-msurvey-property-id="@property.PropertyId"/>
                            </td>
                        </tr>
                    }
                    </tbody>
                </table>
                @Html.PagedListPager((IPagedList) ViewBag.PagedList, page => Url.Action("AddFromContract", new {page}))
            </section>
        }
        else
        {
            <p>Either no properties have been entered, or all of them have been assigned to the survey.</p>
        }
    </fieldset>
}
@section scripts
{
    @Scripts.Render("~/bundles/page/propertyTransfer")
}
arame3333
  • 9,887
  • 26
  • 122
  • 205
  • 1
    Wouldn't it be better if you work with `hidden` input elements? – MinusFour Jan 13 '16 at 15:55
  • 1
    As @MinusFour says, you should place them in named inputs (hidden or not). [This](http://stackoverflow.com/questions/5876809/do-http-post-methods-send-data-as-a-querystring) will explain what is going on. Since the Action is an `[HttpPost]`, mvc will be looking at what is after the Http Headers for the data and not the querystring. – Matt R Jan 13 '16 at 16:09
  • OK, well I tried that. I found I have to convert the array into a comma separated string field and use the string type parameter in the controller method for this to work - which it did. Of course I can now split the string and I have my array back again. The question is; is this the best way to do this? The problem appears to be to do with posting an array of integers rather than whether I use a hidden field. – arame3333 Jan 13 '16 at 16:16
  • 1
    Why don't you send the them using jQuery AJAX? – Sirwan Afifi Jan 13 '16 at 17:10
  • If you want to bind to `int[] PropertyIds` then your query string needs to be `?PropertyIds=someIntValue&PropertyIDs=anotherIntValue&etc`. But this code makes no sense. Why are you creating a query string to a POST method. What are you trying to do? And what is the element with `class="selectProperty"`? None of this code should be necessary if your view is setup correctly. Perhaps showing your view would lead to getting the correct answer. –  Jan 13 '16 at 23:28
  • I am now showing the View as requested. I am always keen to do the right thing, I was not aware that you should never use a querystring for a Post method. I am not doing that now anyway, I am now using a hidden field as suggested by MinusFour. As a result I have got it working now, but the array of integers is not accepted in the method, I always get null. So instead the parameter is a comma separated string which I convert as follows: var propertyIdList = propertyIds.Split(',').Select(x => Convert.ToInt32(x)).ToList(); Is this the best way to fix this? – arame3333 Jan 14 '16 at 08:23
  • @arame3333, Have just seen your edit. If you were to use hidden inputs as per MinusFour's suggest, then you need one for each row (but it needs to be a `for` loop not a `foreach loop so that the `name` attribute is correctly generated with indexers (and you don't need any script at all. –  Jan 14 '16 at 12:21
  • But its unclear what your trying to do. You already have an input (checkbox) in each row and you could easily set the value attribute to `value="@property.PropertyId"` which would post back the `PropertyId` values of all selected checkboxes (again without any scripts). Whats unclear is that you script is currently getting the `PropertyId` value for all checkboxes (whether checked or not) which you already know on the server (so what is the point?) –  Jan 14 '16 at 12:25
  • Apologies. I have just edited the code which was missing the check to find out if the checkboxes have been checked. I am not aware of the value attribute for the checkbox, I will try that next. – arame3333 Jan 14 '16 at 14:34

1 Answers1

1

There is no need for any javascript to solve this. You already have checkbox elements in you form, and if they have the correct name and value attributes, they will bind to your model in the POST method.

Delete the <input type="hidden" id="propertyIds" name="propertyIds" /> and change the the view to

@foreach (var property in ViewBag.PagedList)
{
    <tr>
        <td>@property.UPRN</td>
        ....
        <td style="text-align: center;">
            <input type="checkbox" name="propertyIds" value="@property.PropertyId" />
        </td>
    </tr>
}

and delete the script.

When the form submits, the value of all checked checkboxes will be sent to the controller, and because the checkbox has name="propertyIds" they will bind to your int[] PropertyIds in the POST method.

You will also need to change <div id="hiddenFields" data-msurvey-page="@ViewBag.Page"></div> to

<input type="hidden" name="page" value="@ViewBag.Page" />

Side note: I recommend you start using view models rather that using ViewBag, including a view model for the collection, which would contain properties int ID and bool IsSelected so that you can strongly type your view to your model.