1

I have a View whose model is a List of custom types and I want to POST changes for only a single element of the List (model[i]). This POST itself works, except the value is what was originally in the model and not the updated value from the input

View's Model Declaration

@model List<Translation2>

Translation2's type (F#)

type Translation2 = {
    Key: string;
    RowKey: string;
    Value: string;
    English: string;
}

The View with a single entity submit

@model List<Translation2>

<form asp-controller="Home" asp-action="Translate" method="POST" >
<table>
    <thead>
        <th>
            <h2>Key</h2>
        </th>
        <th>
            <h2>English</h2>
        </th>
        <th>
            <h2>Translated</h2>
        </th>
    </thead>
    <tbody>
@for(var i = 0; i < Model.Count(); i++)
{
        <tr>
            <div>
                <td class="key-col">
                    <p style="font-size:large">@Model[i].Key</p>
                    @Html.HiddenFor(m => m[i].RowKey)
                </td>
                <td class="val-col">
                    <input class="fill-void" type="text" asp-for="@Model[i].English" readonly />

                </td>
                <td class="val-col">
                    <input class="fill-void" type="text" asp-for="@Model[i].Value" />
                </td>
                <td>
                    <a href="@Url.Action("Translate", "Home", new {RowKey=@Model[i].RowKey, Key=@Model[i].Key, Value=@Model[i].Value })" >Save Translation</a>
                </td>
            </div>
        </tr>
}
    </tbody>
</table>
</form>

I have also tried to just POST the entire list per ASP.NET Core 1.0 POST IEnumerable<T> to controller but will get

ArgumentNullException: Value cannot be null.
 Parameter name: source

Count

MoveNext in Translate.cshtml

@for(var i = 0; i < Model.Count(); i++)

The View with submitting the entire list

@model List<Translation2>

<form asp-controller="Home" asp-action="Translate" method="POST" >
<table>
    <thead>
        <th>
            <h2>Key</h2>
        </th>
        <th>
            <h2>English</h2>
        </th>
        <th>
            <h2>Translated</h2>
        </th>
    </thead>
    <tbody>
@for(var i = 0; i < Model.Count(); i++)
{
        <tr>
            <div>
                <td class="key-col">
                    <p style="font-size:large">@Model[i].Key</p>
                    @Html.HiddenFor(m => m[i].RowKey)
                </td>
                <td class="val-col">
                    <input class="fill-void" type="text" asp-for="@Model[i].English" readonly />

                </td>
                <td class="val-col">
                    <input class="fill-void" type="text" asp-for="@Model[i].Value" />
                </td>
                <td>
                    <button type="submit">Save Translation</button>

                </td>
            </div>
        </tr>
}
    </tbody>
</table>
</form>

I would really like to know how to solve both ways, if possible, but a solution for either will do at this point.

Community
  • 1
  • 1
PhilWolf91
  • 29
  • 2
  • 9
  • Not seeing a form in the provided example. How are you posting it to the action? the Save is going a get – Nkosi Feb 17 '17 at 14:55
  • The last TD is a @Url.Action which was for the single entity submit There is also a form wrapping the table
    Which I used In order to submit the form as a whole
    – PhilWolf91 Feb 17 '17 at 15:06
  • That action is doing a GET and not a POST. the value in the link does not change after render when you update the input field. Suggestion. wrap each `tr` in it's own form that submits to an action expecting a single model and not a collection – Nkosi Feb 17 '17 at 15:09
  • Thank you for your suggestion - I believe it has resolved my issue! Just getting new error now :/ – PhilWolf91 Feb 17 '17 at 15:20

1 Answers1

1

@Nkosi 's comment on the OP is the correct answer

"That action is doing a GET and not a POST. the value in the link does not change after render when you update the input field. Suggestion. wrap each tr in it's own form that submits to an action expecting a single model and not a collection"

Corrected Code:

@for(var i = 0; i < Model.Count(); i++)
{
    <form asp-controller="Home" asp-action="Translate" method="POST" >
        <tr>
            <div>
                <td class="key-col">
                    <p style="font-size:large">@Model[i].Key</p>
                    @Html.HiddenFor(m => m[i].RowKey)
                </td>
                <td class="val-col">
                    <input class="fill-void" type="text" asp-for="@Model[i].English" readonly />

                </td>
                <td class="val-col">
                    <input class="fill-void" type="text" asp-for="@Model[i].Value" />
                </td>
                <td>
                    <button type="submit">Save Translation</button>
                </td>
            </div>
        </tr>
    </form>
}

Still getting an error ArgumentException: Type 'Translation2' does not have a default constructor Parameter name: type, but the scope of this question has been resolved.

Update: Partial Fix ---

Have added the constructors, however it always uses the default constructor so when I look at the property values after the POST, they all say "DEFAULT"

type Translation2(key:string, rowKey:string, value:string, english:string) =
    public new () = Translation2("DEFAULT", "DEFAULT", "DEFAULT", "DEFAULT")
    member this.Key = key
    member this.RowKey = rowKey
    member this.Value = value
    member this.English = English

Update: For the record --- complicating the constructor doesn't help either... it seems the values aren't being lifted from the view....

type Translation2(key:string, rowKey:string, value:string, english:string) =
    let mutable k = key
    let mutable r = rowKey
    let mutable v = value
    let mutable e = english

    member this.Key with get() = k and set(value) = k <- value
    member this.RowKey with get() = r and set(value) = r <- value
    member this.Value with get() = v and set(value) = v <- value
    member this.English with get() = e and set(value) = e <- value

    public new () = Translation2("DEFAULT", "DEFAULT", "DEFAULT", "DEFAULT")
PhilWolf91
  • 29
  • 2
  • 9
  • You cannot have a form in side a loop like that - your generating form controls with indexers (e.g. `` so it cannot only post back to a parameter which is a collection, but it can only work for the first item because collection indexers must start at zero and be consecutive. What are you actually trying to do here? Having multiple forms makes no sense since only one can be submitted. –  Feb 17 '17 at 22:38
  • 1
    I have a list of objects that need to display in the View. I want to be able to update a single object from the list by passing it an action on a controller. – PhilWolf91 Feb 18 '17 at 23:19
  • 1
    Creating all that extra html (forms and inputs) for each item is a waste of resources. You either have one form and generate controls for all items and post back the whole collection (i.e. the user can make changes to multiple items). Or you display the data as simple text (as in a table) and use a single form in a dialog to edit a row of data –  Feb 18 '17 at 23:22
  • Something like the UI in [this fiddle](http://jsfiddle.net/9madrh7g/2/) where you would use ajax to post back the data for a single row –  Feb 18 '17 at 23:24