1

I have a partial view inside a form that append to the form each time a button is clicked. It is a text area with a set of responses. How can I pass my model from the view to the controller, add it to the list model and back to view?

I am passing ReviewFormViewModel I want to pass the ListAdhoc to the partial controller and add items to it then pass it back to the view.

public class ReviewFormViewModel
{
    ...// other fields
    public List<AdhocViewModel> ListAdhoc { get; set; }
}

public class AdhocViewModel
{
    public int? ReviewId { get; set; }
    public String AdhocQuestion { get; set; } //free form
    public int? SelectedAnswer { get; set; } // for binding int? for optional
    public String Comments { get; set; }
    public List<AdhocOptionsVM> ListAdhocOptions { get; set; }
}

public class AdhocOptionsVM
{
    public int AnswerId { get; set; }
    public String RatingName { get; set; }
    public Decimal Rating { get; set; }
    public String ActiveFl { get; set; }

}

controller for partial view

 public PartialViewResult Adhoc()
    {
        //pass model object on button click and add each item to the model everytime
        var AdhocObj = new AdhocViewModel();

        AdhocObj.ListAdhocOptions = new List<AdhocOptionsVM>();
        var query = db.dbQuestionOptions.Where(qo => qo.ActiveFl == "Y").OrderByDescending(qo => qo.Rating).ToList();

        foreach (var item in query)
        {
            var AdhocAnsOptionsVMObj = new AdhocOptionsVM();
            AdhocAnsOptionsVMObj.AnswerId = item.AnswerId;
            AdhocAnsOptionsVMObj.RatingName = item.RatingName;
            AdhocAnsOptionsVMObj.Rating = item.Rating;
            AdhocAnsOptionsVMObj.ActiveFl = item.ActiveFl;

            AdhocObj.ListAdhocOptions.Add(AdhocAnsOptionsVMObj);
        }


        return PartialView("Adhoc", AdhocObj);
}

and partial view that is using the ReviewFormViewModel aswell:

<div class="adhoc">
@using (Html.BeginCollectionItem("adhoc"))
{
    <div class="panel panel-success">
        <div class="panel-heading">
            @Html.HiddenFor(m => m.ReviewId)
            @Html.HiddenFor(m => m.AdhocId)

            @Html.TextAreaFor(m => m.AdhocQuestion, htmlAttributes: new { @style = "width:650px", @placeholder = "Enter Adhoc Question here" })<br />
        </div>
        <div class="panel-body">
            @foreach (var optAnswer in Model.ListAdhocOptions)
            {
                <div class="radio">
                    <responselabel>@Html.RadioButtonFor(m => m.SelectedAnswer, optAnswer.AnswerId, new { id = optAnswer.AnswerId }) @optAnswer.RatingName</responselabel><br />
                </div>
            }
            <div>@Html.ValidationMessageFor(m => m.SelectedAnswer)</div><br />

            @Html.TextAreaFor(m => m.Comments, htmlAttributes: new { @style = "width:650px", @placeholder = "Comments" })<br /><br />
        </div>
        <button type="button" class="delete">Delete</button>
    </div>

}

main view

@model CustomerFeedback.Areas.ProjectManagers.Models.ReviewFormViewModel

@{
    ViewBag.Title = "CreateFormsIndex";
}

<h4 align="center">Project Review Form</h4>

<div class="container-fluid">
    <div class="row">
        <div class="col-md-12">
            <div class="text-center">
                <h4>
                    @Html.DisplayName(Model.ProjectId) @Html.DisplayName(Model.ProjectName)
                </h4>
                <h4>
                    PM: @Html.DisplayName(Model.FullName)
                </h4>
            </div>
        </div>
    </div>
</div>

<div class="container">
    <br />
    <div class="panel-group">
        @using (Html.BeginForm())
        {

            @Html.AntiForgeryToken()
            @Html.ValidationSummary(true, "", new { @class = "text-danger" })
            @Html.HiddenFor(m => m.ProjectId)
            @Html.HiddenFor(m => m.AccountId)
            @Html.HiddenFor(m => m.ReviewDate)

            <div class="panel panel-default">
                <div class="panel-body">
                    <div class="panel-group">
                        <div class="panel-heading">
                            <h4 class="panel-title">
                                Required Questions
                            </h4>
                        </div>
                        @for (int i = 0; i < Model.ListReqQuestions.Count; i++)
                        {
                            <div class="panel panel-success">
                                <div class="panel-heading">
                                    @Html.HiddenFor(m => m.ListReqQuestions[i].QuestionId)
                                    @Html.DisplayFor(m => m.ListReqQuestions[i].QuestionText)
                                </div>
                                <div class="panel-body">
                                    @foreach (var optAnswer in Model.ListReqQuestions[i].ListQuestionOptions)
                                    {
                                        <div class="radio">
                                            <responselabel>@Html.RadioButtonFor(m => m.ListReqQuestions[i].SelectedAnswer, optAnswer.AnswerId, new { id = optAnswer.AnswerId }) @optAnswer.RatingName</responselabel><br />
                                        </div>
                                    }
                                    <div>@Html.ValidationMessageFor(m => m.ListReqQuestions[i].SelectedAnswer)</div><br />

                                    @Html.TextAreaFor(m => m.ListReqQuestions[i].Comments, htmlAttributes: new { @style = "width:650px", @placeholder = "Comments" })<br /><br />
                                </div>
                            </div>
                        }

                        <div class="panel-heading">
                            <h4 class="panel-title">
                                Optional Questions
                            </h4>
                        </div>
                        @for (int i = 0; i < Model.ListOpQuestions.Count; i++)
                        {
                            <div class="panel panel-success">
                                <div class="panel-heading">
                                    @Html.HiddenFor(m => m.ListOpQuestions[i].QuestionId)
                                    @Html.DisplayFor(m => m.ListOpQuestions[i].QuestionText)
                                </div>
                                <div class="panel-body">
                                    @foreach (var optAnswer in Model.ListOpQuestions[i].ListQuestionOptions)
                                    {
                                        <div class="radio">
                                            <responselabel>@Html.RadioButtonFor(m => m.ListOpQuestions[i].SelectedAnswer, optAnswer.AnswerId, new { id = optAnswer.AnswerId }) @optAnswer.RatingName</responselabel><br />
                                        </div>
                                    }
                                    <div>@Html.ValidationMessageFor(m => m.ListOpQuestions[i].SelectedAnswer)</div><br />

                                    @Html.TextAreaFor(m => m.ListOpQuestions[i].Comments, htmlAttributes: new { @style = "width:650px", @placeholder = "Comments" })<br /><br />
                                </div>
                            </div>
                        }
                        @*on click (new adhoc question) add a new freeform question with list of answers*@
                        <div class="panel panel-success" id="adhoc">
                            @* renders partial adhoc view *@
                        </div>
                        <br />
                        <div class="center">
                            <input type="button" value="New Adhoc Question" class="btnAdhoc btn-success" />
                        </div>
                        <br />
                        <div class="center">
                            <input type="submit" value="Save" name="Command" class="btn btn-success" />
                            <input type="submit" value="Submit" name="Command" class="btn btn-success" />
                            <input type="submit" value="Cancel" name="Command" class="btn btn-success" />
                            <input type="submit" value="Attach" name="Command" class="btn btn-success" />
                        </div>

                    </div>
                </div>
            </div>
        }
    </div>
</div>

<script>

    $(function () {

        $('.btnAdhoc').click(function (event) {
            event.preventDefault();

            $.ajax({
                url: '/ProjectManagers/Forms/Adhoc',
                //data: JSON.stringify(model),
                type: 'get',
                success: function (result) {
                    $('#adhoc').append(result);
                }
            });
        });
    })
</script>

UPDATE: I've added the AdhocViewModel.

Ive added the view model for those properties. I have a form with a set of questions and responses to be answered. Those are from the database. I have a button, on click will generate the partial view and append to the form (can be many). The partial view consists of a text area (for any question entered), set of responses (from database) and a comment box. I am not sure how to handle this on post (submit). My attempt is to pass the model from the view to the partial controller, add items to it and pass it back to the view for processing. I am not have any success on passing model data

UPDATE 2 Updated code with using BeginCollectionItem helper. Added main view

userdp
  • 35
  • 8
  • Difficult to understand what your trying to do here. No where in your partial have you shown anything relating to properties of `ListAdhoc` –  Feb 02 '17 at 22:48
  • But you have claimed that the partial uses `ReviewFormViewModel` which clearly its not based on your code - it must be `@model AdhocViewModel` –  Feb 02 '17 at 22:58
  • And if that partial is for `AdhocViewModel` it makes no sense because `ReviewFormViewModel` contains a collection of `AdhocViewModel` and in order to generate controls for a collection they need to have indexers and your partial will not do that. –  Feb 02 '17 at 23:04
  • Hi Stephen, I've updated my question – userdp Feb 02 '17 at 23:04
  • Best guess is your wanting to dynamically add new items to your collection (although even that makes no sense based on the code you have shown). If so refer [this answer](http://stackoverflow.com/questions/28019793/submit-same-partial-view-called-multiple-times-data-to-controller/28081308#28081308) for some options –  Feb 02 '17 at 23:07
  • yes, dynamically add new items. Thank you, I will try with indexers – userdp Feb 02 '17 at 23:26
  • For a more complete example using `BeginCollectionItem`,refer [this answer](http://stackoverflow.com/questions/40539321/a-partial-view-passing-a-collection-using-the-html-begincollectionitem-helper/40541892#40541892) –  Feb 02 '17 at 23:31
  • ive updated my code with the example given using begin collection item. i am not getting any data passed back on POST. the model is null. Wouldnt i need to pass the model being used in the main form to the partialviewresult?? – userdp Feb 03 '17 at 02:49
  • For a start your property is named `ListAdhoc` so its `BeginCollectionItem("ListAdhoc")` (not `"adhoc"`) –  Feb 03 '17 at 02:52
  • And the code in your `Adhoc()` method makes no sense - that just need to return a partial for a default instance - one line of code only - `return PartialView("Adhoc", new AdhocViewModel());` –  Feb 03 '17 at 02:54
  • And the main view needs a `foreach(var item in Model.ListAdhoc) { @Html.Partial("Adhoc", item) }` for existing items –  Feb 03 '17 at 02:58
  • the code in the Adhoc() method is to populate a set of responses for each dynamic adhoc question. ill test out changes. – userdp Feb 03 '17 at 03:05
  • the fix to begin collection and main view solved my problem. thank you so much. please put one as a answer – userdp Feb 03 '17 at 03:18

1 Answers1

1

The reason that the collection does not bind is because the parameter in BeginCollectionItem() must match the name of you property. Change it to

@using (Html.BeginCollectionItem("ListAdhoc")) // binds to List<AdhocViewModel> ListAdhoc

In addition you also need a loop in the main view to render existing AdhocViewModel in the collection. Even if none exist initially, it is still required in case you need to return the view because ModelState is invalid. In the main view include

<div class="panel panel-success" id="adhoc">
    @foreach(var item in Model.ListAdhoc)
    {
        @Html.Partial("Adhoc", item)
    }
</div>