1
votes

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.

1
Not seeing a form in the provided example. How are you posting it to the action? the Save is going a getNkosi
The last TD is a @Url.Action which was for the single entity submit There is also a form wrapping the table <form asp-controller="Home" asp-action="Translate" method="POST" > </form> Which I used <button type="submit">Save Translation</button> In order to submit the form as a wholePhilWolf91
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 collectionNkosi
Thank you for your suggestion - I believe it has resolved my issue! Just getting new error now :/PhilWolf91

1 Answers

1
votes

@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")