1

I am using MVC framework in my project and using model binding to fill a view and then retrieve the values from the view using two controllers.

I am able to fill the view with the model properties but when i am trying to retrieve the values on click of a submit button. The model returns null properties.

Here is the ViewModels:

public partial class QuestionAnswer
{
    public string QuestionText { get; set; }
    public string QuestionId { get; set; }
    public string AnswerText { get; set; }
    public string SectionName { get; set; }
    public bool IsChecked { get; set; }
}
public partial class pluralQuestionAnswer
{
    public Dictionary<int, QuestionAnswer> QnAs { get; set; }
    public Dictionary<string, int> SectionNameWithRank { get; set; }
    public Dictionary<string, int> SectionNameWithQuestionCount { get; set; }
}

Here are the controllers:

namespace Temp.Controllers
    {
        public partial class AIPController : Controller
        {
            public ActionResult FirstInitiative()
            {
                pluralQuestionAnswer ViewModel = new pluralQuestionAnswer();
                //code to fill the model ViewModel
                return View(ViewModel);
            }

            [HttpPost]
            public ActionResult SaveSelectedInitiatives(pluralQuestionAnswer ViewModel, string Save)
            {
                //some code that uses ViewModel
                //this is where ViewModel shows all properties as null
                return View();
            }
        }
    }

FirstInitiative is used to generate the view by passing a viewModel and SaveSelectedInitiatives is used to get value from the viewModel retrieved from the view.

Here is the View:

@using Models;
@model pluralQuestionAnswer

@{
    ViewBag.Title = "FirstInitiative";
    int sectionIterator;
    int sectionPracticeIterator;
    int totalPracticeIterator = 1;
}

<header></header>

<section>

    <header>
        <h1>First Initiative</h1>
    </header>

    @using (Html.BeginForm("SaveSelectedInitiatives", "AIP", FormMethod.Post))
    {
        <div class="inititive-table" style="overflow:auto">
            <div class="inititive-table-header">
                <div class="initiative-questioncolumn">
                    Common Minimum Practices
                </div>
                <div class="initiative-answercolumn">
                    Your response
                </div>
            </div>
            @for (sectionIterator = 1; sectionIterator <= Model.SectionNameWithRank.Count; sectionIterator++)
            {
                <div class="initiative-section">
                    <div class="initiative-section-name">
                        <span>@Model.SectionNameWithRank.Keys.ElementAt(sectionIterator - 1)</span>
                    </div>
                    @for (sectionPracticeIterator = 1; sectionPracticeIterator <= Model.SectionNameWithQuestionCount[Model.SectionNameWithRank.Keys.ElementAt(sectionIterator - 1)]; sectionPracticeIterator++)
                    {
                        <div class="initiative-section-question initiative-questioncolumn">
                            <label>
                                @*model binding*@
                                @Html.HiddenFor(x => x.QnAs[totalPracticeIterator].QuestionId)

                                <span style="width: 20px; float:left">@Html.CheckBoxFor(x => x.QnAs[totalPracticeIterator].IsChecked)</span>
                                <span style="display: block; margin-left: 20px">@Html.Raw(Model.QnAs[totalPracticeIterator].QuestionText)</span>
                            </label><br />
                        </div>
                        <div class="initiative-section-answers initiative-answercolumn">
                            @Model.QnAs[totalPracticeIterator].AnswerText
                        </div>

                        {totalPracticeIterator += 1;}
                    }
                </div>
            }
        </div>
        <div>
            <input type="submit" class="zs-left zs-button zs-button-action zs-atp-button" name="Save" value="Save" />
        </div>
    }
</section>
uj2
  • 13
  • 3
  • You don't have any input tags, so data isn't sent. Use Fiddler or DevTool to view which information is getting(if any) to your api. – Orel Eraki Oct 05 '17 at 11:12
  • 1
    Firstly, you cannot generate the correct for controls to bind to a `Dictionary` using any `HtmlHelper` methods. And your `nested `for` loop makes no sense since the `DefaultModelBinder` requires collection indexers to start ` aero and be consecutive (unless you include an input for the indexer) –  Oct 05 '17 at 11:13
  • You probably need to see https://stackoverflow.com/q/5191303/11683, https://stackoverflow.com/q/22248739/11683 and potentially https://stackoverflow.com/q/25333332/11683. – GSerg Oct 05 '17 at 11:17
  • Refer [this answer](https://stackoverflow.com/questions/42532704/dictionaryshort-dictionaryenfunction-bool-model-binding-not-work/42544486#42544486) to understand why you should never use a `Dictionary` –  Oct 05 '17 at 11:18
  • In case of complex properties, using `EditorTemplates` is cleaner and better approach. You are missing basic HTML logic here, only `input` tags are posted from client to server, you have only one i.e. `QuestionId`. Additionally read `Class` and `Arguments` naming conventions. – G J Oct 05 '17 at 14:16
  • @Gaurav: I only need to read one property i.e. QuestionId. If i am binding only a single property of a class in the viewmodel shouldn't i be able to retrieve only that – uj2 Oct 06 '17 at 03:08
  • Additionally it is recommended to validate `AntiForgeryToken` on form post – G J Oct 12 '17 at 20:45

2 Answers2

0

You may want to rename your inputs to match the KeyValuePair schema of your model, so replace below:

@Html.HiddenFor(x => x.QnAs[totalPracticeIterator].QuestionId)

With

@Html.Hidden(string.Format("QnAs[{0}].Key", i), /*<You Need Key's value here> Like: (_kvp.Key) */)
@Html.Hidden(string.Format("QnAs[{0}].Value.QuestionId", i), x.QnAs[/*<You Need Key's value here> Like: (_kvp.Key) */].QuestionId)

You may try, below code to get KeyValuePair:

var _kvp = Model.QnAs.ElementAt(totalPracticeIterator)

So, the posted data from browser would look like:

QnAs[0].Key = 1
QnAs[0].Value.QuestionId = 123
QnAs[1].Key = 32
QnAs[1].Value.QuestionId = 55
G J
  • 477
  • 9
  • 23
0

In your Dictionary objects, change the key from int type to string type and this should work.

braceyourself
  • 227
  • 4
  • 20
  • But that will change the underlying model!, what if only `int` is required? – G J Oct 24 '17 at 09:05
  • @GauravSinghJantwal you can always use int.ToString() to convert int to string. that means you can still have numbers but in the form of String – braceyourself Oct 25 '17 at 06:06