A Partial View passing a collection using the Html.BeginCollectionItem helper

陌路散爱 提交于 2019-11-25 21:53:42

问题


I made a small project to understand the answer from Stephen Muecke here: Submit same Partial View called multiple times data to controller?

Almost everything works. The javascript adds new fields from the Partial View, and I can tell they\'re bound to the model by the \"temp\" values inserted by the controller method for the partial view.

However, when I submit the new fields the AddRecord() method throws an exception showing that the model isn\'t getting passed in (\"Object reference not set to an instance of an object\").

Also, when I view the page source, the BeginCollectionItem helper is inserting a hidden tag as it should around the table in the main view that displays pre-existing records, but not around the new fields that the javascript adds.

What am I doing wrong? I\'m pretty new at this so thanks for your patience!

My main view:

@model IEnumerable<DynamicForm.Models.CashRecipient>

@using (Html.BeginForm(\"AddDetail\", \"CashRecipients\", FormMethod.Post))
{
    @Html.AntiForgeryToken()
    <div id=\"CSQGroup\">
    </div>
}

<div>
    <input type=\"button\" value=\"Add Field\" id=\"addField\" onclick=\"addFieldss()\" />
</div>

<script>
    function addFieldss()
    {   
        //alert(\"ajax call\");
        $.ajax({
            url: \'@Url.Content(\"~/CashRecipients/RecipientForm\")\',
            type: \'GET\',
            success:function(result) {
                //alert(\"Success\");
                var newDiv = document.createElement(\"div\"); 
                var newContent = document.createTextNode(\"Hi there and greetings!\"); 
                newDiv.appendChild(newContent);  
                newDiv.innerHTML = result;
                var currentDiv = document.getElementById(\"div1\");  
                document.getElementById(\"CSQGroup\").appendChild(newDiv);
            },
            error: function(result) {
                alert(\"Failure\");
            }
        });
    }
</script>

My Partial View:

@model DynamicForm.Models.CashRecipient
@using HtmlHelpers.BeginCollectionItem

@using (Html.BeginCollectionItem(\"recipients\"))
{
    <div class=\"editor-field\">
        @Html.LabelFor(model => model.Id)
        @Html.LabelFor(model => model.cashAmount)
        @Html.TextBoxFor(model => model.cashAmount)
        @Html.LabelFor(model => model.recipientName)
        @Html.TextBoxFor(model => model.recipientName)
    </div>
    <div class=\"form-group\">
        <div class=\"col-md-offset-2 col-md-10\">
            <input type=\"submit\" value=\"Save\" class=\"btn btn-default\" />
        </div>
    </div>
}

My model:

public class CashRecipient
{
    public int Id { get; set; }
    public string cashAmount { get; set; }
    public string recipientName { get; set; }  
}

In my controller:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AddDetail([Bind(Include = \"Id,cashAmount,recpientName\")] IEnumerable<CashRecipient> cashRecipient)
{
    if (ModelState.IsValid)
    {
        foreach (CashRecipient p in cashRecipient) {
            db.CashRecipients.Add(p);
        }
        db.SaveChanges();
        return RedirectToAction(\"Index\");

    }

    return View(cashRecipient);
}

public ActionResult RecipientForm()
{
    var data = new CashRecipient();
    data.cashAmount = \"temp\";
    data.recipientName = \"temp\";
    return PartialView(data);
}

回答1:


First start by creating a view model to represent what you want to edit. I'm assuming cashAmount is a monetary value, so therefore should be a decimal (add other validation and display attributes as required)

public class CashRecipientVM
{
    public int? ID { get; set; }
    public decimal Amount { get; set; }
    [Required(ErrorMessage = "Please enter the name of the recipient")]
    public string Recipient { get; set; }  
}

Then create a partial view (say) _Recipient.cshtml

@model CashRecipientVM
<div class="recipient">
    @using (Html.BeginCollectionItem("recipients"))
    {
        @Html.HiddenFor(m => m.ID, new { @class="id" })
        @Html.LabelFor(m => m.Recipient)
        @Html.TextBoxFor(m => m.Recipient)
        @Html.ValidationMesssageFor(m => m.Recipient)
        @Html.LabelFor(m => m.Amount)
        @Html.TextBoxFor(m => m.Amount)
        @Html.ValidationMesssageFor(m => m.Amount)
        <button type="button" class="delete">Delete</button>
    }
</div>

and a method to return that partial

public PartialViewResult Recipient()
{
    return PartialView("_Recipient", new CashRecipientVM());
}

Then your main GET method will be

public ActionResult Create()
{
    List<CashRecipientVM> model = new List<CashRecipientVM>();
    .... // add any existing objects that your editing
    return View(model);
}

and its view will be

@model IEnumerable<CashRecipientVM>
@using (Html.BeginForm())
{
    <div id="recipients">
        foreach(var recipient in Model)
        {
            @Html.Partial("_Recipient", recipient)
        }
    </div>
    <button id="add" type="button">Add</button>
    <input type="submit" value="Save" />
}

and will include a script to add the html for a new CashRecipientVM

var url = '@Url.Action("Recipient")';
var form = $('form');
var recipients = $('#recipients');
$('#add').click(function() {
    $.get(url, function(response) {
        recipients.append(response);
        // Reparse the validator for client side validation
        form.data('validator', null);
        $.validator.unobtrusive.parse(form);
    });
});

and the script to delete an item

$('.delete').click(function() {
    var container = $(this).closest('.recipient');
    var id = container.find('.id').val();
    if (id) {
        // make ajax post to delete item
        $.post(yourDeleteUrl, { id: id }, function(result) {
            container.remove();
        }.fail(function (result) {
            // Oops, something went wrong (display error message?)
        }
    } else {
        // It never existed, so just remove the container
        container.remove();
    }
});

And the form will post back to

public ActionResult Create(IEnumerable<CashRecipientVM> recipients)


来源:https://stackoverflow.com/questions/40539321/a-partial-view-passing-a-collection-using-the-html-begincollectionitem-helper

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!