0

I am new to mvc and am trying to do something like in the below image (im not using partial views). I have an IEnumerable property on Model Like in this image, (i have not enough reputation to show image directly)

but on controller if i put FormCollection as parameter to accepting post method when i process same name fields i get an extra character ',', which might be also used by user...

Any idea....

[EDIT] My view

@model Models.Question
@{
    ViewBag.Title = "Add";
}

<h2>Add</h2>


@using (Html.BeginForm())
{
@Html.AntiForgeryToken()

<div class="form-horizontal">
    <h4>Question</h4>
    <hr />
    @Html.ValidationSummary(true, "", new { @class = "text-danger" })
    @Html.HiddenFor(model => model.SurveyId)
    <div class="form-group">
        @Html.LabelFor(model => model.Name, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.Name, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.Name, "", new { @class = "text-danger" })
        </div>
    </div>

    <div class="form-group">
        @Html.LabelFor(model => model.QuestionTypeId, "Question type", htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.DropDownListFor(model => model.QuestionTypeId, new SelectList(ViewBag.QuestionTypes, "QuestionTypeId", "Name"), new { @class = "form-control col-md-10" })
            <button type="button" class="btn btn-default addAnswer hide"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span></button>
            @Html.ValidationMessageFor(model => model.QuestionTypeId, "", new { @class = "text-danger" })
        </div>
    </div>
    <div class="form-group hide">
        @Html.LabelFor(model => model.Answers, "Options", htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10" id="allAnswers">
            @Html.ValidationMessageFor(model => model.Answers, "", new { @class = "text-danger" })
        </div>
    </div>

    <div class="form-group">
        @Html.LabelFor(model => model.Sort, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.Sort, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.Sort, "", new { @class = "text-danger" })
        </div>
    </div>

    <div class="form-group">
        @Html.LabelFor(model => model.Mandatory, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            <div class="checkbox">
                @Html.EditorFor(model => model.Mandatory)
                @Html.ValidationMessageFor(model => model.Mandatory, "", new { @class = "text-danger" })
            </div>
        </div>
    </div>

    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" value="Create" class="btn btn-default" />
        </div>
    </div>
</div>
}

<div>
@Html.ActionLink("Back to List", "Question", "Survey", new { id = Model.SurveyId }, null)
</div>

@section Scripts {
@Scripts.Render("~/bundles/jqueryval", "~/Scripts/QuestionAdd.js")
}

Controller

[HttpGet]
    public ActionResult Add(long id)
    {
        var question = new Models.Question();
        question.SurveyId = id;
        ViewBag.QuestionTypes = BLL.Questions.GetQuestionTypes();
        return View(question);
    }
    [HttpPost]
    public ActionResult Add(FormCollection coll)
    {
        if (ModelState.IsValid)
        {
            var question = new Models.Question();
            question.Name = coll["Name"];
            byte qid = 0, sort = 0;
            bool mandatory = false;
            byte.TryParse(coll["QuestionTypeId"], out qid);
            byte.TryParse(coll["Sort"], out sort);
            bool.TryParse(coll["Mandatory"], out mandatory);
            question.QuestionTypeId = qid;
            question.Sort = sort;
            question.Mandatory = mandatory;
            foreach (var answer in coll["Answers"])
                question.Answers.Add(new Models.Answer() { Value = answer + "" });
            if (question != null)
            {
                if (BLL.Questions.Insert(question) != null)
                    ViewBag.Message = "Successfully inserted";
                else
                    ViewBag.Message = "Insert could not be done";
                return RedirectToAction("Index", "Question", new { questionId = question.QuestionId });
            }
        }
        return View();
    }

When (+) clicked

$('#allAnswers').append(
    '<div class="input-group col-lg-4">' +
    '<input class="form-control" name="Answers"> ' +
    '<div class="input-group-btn">' +
    ' <button type="button" class="btn btn-default removeAnswer"><span class="glyphicon glyphicon glyphicon-trash" aria-hidden="true"></span></button>' +
    '</div> </div>');
14
  • Can you post the code that populates your model and the controller code that returns the view and is posted too. The view html that renders the dropdown would help as well. Commented Jun 13, 2017 at 14:42
  • @Wheels73 edited...thanks Commented Jun 13, 2017 at 14:49
  • You bind your view to "Models.Question"... I would have thought that the same model will be posted! Have you tried changing the post signature to receive "Models.Question" instead of form collection? Commented Jun 13, 2017 at 14:55
  • nope model is Question and on Question Model i have this property public virtual List<Answer> Answers { get; set; } Commented Jun 13, 2017 at 14:57
  • that's fine.... the form will contain the contents of your model. Change the post signature and see if it all binds correctly and has the same problem. I've never used form collection in MVC to receive the contents of a post! It may not change anything but will eliminate it Commented Jun 13, 2017 at 15:01

1 Answer 1

1

As discussed.. here is how i would approach this.

I have a view model to hold the questions and answers and the selected question to which we will be adding answers.

public class CurrentViewModel
{
    public string QuestionSelected { get; set; }
    public List<SelectListItem> Questions { get; set; }
    public List<string> Answers { get; set; }
}

Then in your controller we have an action to return the view, an action to get the answers for the selected question, an action to add a new answer for the selected question and also a save method which just demonstrates the final model content posted.

public class TestController : Controller
    {
        public ActionResult Test()
        {
            var model = new CurrentViewModel()
            {
                Questions = new List<SelectListItem>()
                {
                    new SelectListItem()
                    {
                        Text = "Question 1",
                        Value = "Question 1"
                    },

                    new SelectListItem()
                    {
                        Text = "Question 2",
                        Value = "Question 2"
                    }
                },
                Answers = new List<string>()
            };

            return View("Test", model);
        }

        public PartialViewResult GetAnswers(CurrentViewModel model)
        {
            model.Answers = new List<string>();
            //model.Answers = Get Answers from some service based on QuestionSelected?!
            model.Answers.Add("Answer 1");
            model.Answers.Add("Answer 2"); //Add manuall for example
            return PartialView("_Answers", model);
        }

        public PartialViewResult AddAnswer(CurrentViewModel model)
        {
            model.Answers.Add("Add answer here...");
            return PartialView("_Answers", model);
        }

        public ActionResult SaveQuestionsAndAnswers(CurrentViewModel model)
        {
            if (model.Questions.Count == 0)
            {

            }

            return View("Test", model);
        }

    }

Then we have a main view for showing the questions dropdown and a partial view that will show us the answers.

Main View

@model TestMVC.Models.CurrentViewModel

    @{
        ViewBag.Title = "Test";
    }

    <link href="~/Content/StyleSheets/jquery-ui.css" rel="stylesheet" />
    <script src="~/Content/Scripts/jquery-2.2.3.js"></script>
    <script src="~/Content/Scripts/jquery-ui-1.11.4.js"></script>

    <div id="divBodyContent">
        <div>
            <h3>Q & A</h3>
        </div>

        @using (Html.BeginForm("SaveQuestionsAndAnswers", "Test", FormMethod.Post, new { id = "frmQandA" }))
        {
            @Html.Label("lblQ", "Questions", new { @class = "form-control inline" })
            @Html.DropDownListFor(model => model.QuestionSelected, Model.Questions, "Select--", new { @class = "form-control inline", id="ddQuestions" })

            <div id="divAnswers">
                @Html.Partial("_Answers")
            </div>

        <div style="margin-top: 1%;">
            <button id="btnSave" type="submit" class="btn btn-primary" style="margin-left: 4px; margin-bottom: 10px; width: 7%">save</button>
        </div>

        }
    </div>

    <script>
        $("#ddQuestions").on("change", function() {
            var myData = $('#frmQandA').serialize();

            $.ajax({
                type: "POST",
                url: "@Url.Action("GetAnswers", "Test")",
                data: myData,
                contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
                success: function(data) {
                    $("#divAnswers").html(data);
                }
            })});

    </script>

Partial View

@model TestMVC.Models.CurrentViewModel

<link href="~/Content/StyleSheets/jquery-ui.css" rel="stylesheet" />
<script src="~/Content/Scripts/jquery-2.2.3.js"></script>
<script src="~/Content/Scripts/jquery-ui-1.11.4.js"></script>

<div>
    @for (var counter = 0; counter <= (Model.Answers.Count - 1); counter++)
    {
        @Html.TextBoxFor(model => model.Answers[counter], new {@class = "form-control inline"})
    }

    <div style="margin-top: 1%;">
        <button id="btnAddAnswer" type="button" class="btn btn-primary" style="margin-left: 4px; margin-bottom: 10px; width: 7%">Add</button>
    </div>


</div>

<script>

    $("#btnAddAnswer").on("click", function() {
        var myData = $('#frmQandA').serialize();

        $.ajax({
            type: "POST",
            url: "@Url.Action("AddAnswer", "Test")",
            data: myData,
        contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
        success: function (data) {
            $("#divAnswers").html(data);
        }
    })});

</script>

I've tested this locally and it works. In a nutshell, when we need a new answer adding to the model, we add it in server side so we can then bind to the model in the browser. We do this via an Ajax call on the change event of the questions drop down. The partial view iterates through each answer via a counter which generates a unique id and textbox for each answer. Adding a new answer is also achieved via ajax. These can then all be posted back.

Hope that helps.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.