2

I have a number of awards in my view and within each award there is a corresponding list of qualifications. I have created a ViewModel to display each award and with a click of a button a modal should appear with its relevant qualifications which can be marked as completed/updated by the user. However on the Post of the data it is not binding to my ViewModel in my controller method. The data is appearing in my view as expected with each Award only showing its relevant qualifications. I have used FormCollection to access some of the fields for testing purposes and the data is being posted back. Any help would be great!

ViewModel

public class CandidateExtended
{
    public CandidateExtended()
    {
        this.Qualifications = new List<Qualification_Extended>();
    }

    public int AwardID { get; set; }
    public int FrameworkID { get; set; }
    public string ULN { get; set; }
    public string Forename { get; set; }
    public string Surname { get; set; }
    public string TitleShort { get; set; }
    public string TitleFull { get; set; }
    public DateTime DOB { get; set; }
    public string Award { get; set; }
    public int AwardLevel { get; set; }
    public string Status { get; set; }
    public string Completion { get; set; }
    public string SelectedRoute { get; set; }

    public List<Qualification_Extended> Qualifications { get; set; }

    public void addQualification(Qualification_Extended qualification)
    {
        Qualifications.Add(qualification);
    }
}

Controller

  [HttpGet]
    public ActionResult Index()
    {

        var awardDetails = (from award in db.award
                            join candidate in db.candidate
                            on award.ULN equals candidate.ULN
                            join framework in db.framework
                            on award.QAN equals framework.QAN
                            where award.OrganisationIdentityID == organisationID
                            select new AwardDetails_Extended
                            {
                                AwardID = award.AwardID,
                                ULN = award.ULN,
                                AwardStatus = award.AwardStatus,
                                Forename = candidate.Forename,
                                Surname = candidate.Surname,
                                DOB = candidate.DOB,
                                FrameworkID = framework.FrameworkID,
                                TitleFull = framework.TitleFull,
                                TitleShort = framework.TitleShort, 
                                AwardLevel = framework.AwardLevel, 
                                Award = framework.Award,
                                Completion = framework.Completion
                            }).ToList();


        var qualificationDetails = (from candidateQualification in db.candidateQualification
                                    join qualification in db.qualification
                                    on candidateQualification.QualificationID equals qualification.QualificationID
                                    select new Qualification_Extended
                                    {
                                        ID = candidateQualification.ID,
                                        QualificationID = candidateQualification.QualificationID,
                                        ULN = candidateQualification.ULN,
                                        FrameworkID = candidateQualification.FrameworkID,
                                        Achieved = candidateQualification.Achieved,
                                        DateAchieved = candidateQualification.DateAchieved
                                    }).ToList();


        List<CandidateExtended> candidateVM = new List<CandidateExtended>();

        foreach (var item in awardDetails)
        {
            CandidateExtended vm = new CandidateExtended();
            vm.AwardID = item.AwardID;
            vm.FrameworkID = item.FrameworkID;
            vm.ULN = item.ULN;
            vm.Forename = item.Forename;
            vm.Surname = item.Surname;
            vm.DOB = item.DOB;
            vm.TitleShort = item.TitleShort;
            vm.TitleFull = item.TitleFull;
            vm.Award = item.Award;
            vm.AwardLevel = item.AwardLevel;
            vm.Status = item.AwardStatus;
            vm.Completion = item.Completion;
            vm.SelectedRoute = item.SelectedRoute;

            foreach (var qualification in qualificationDetails)
            {
                if (qualification.ULN == item.ULN && qualification.FrameworkID == item.FrameworkID)
                {
                    vm.addQualification(qualification);
                }
            }

            candidateVM.Add(vm);
        }

        return View(candidateVM);      
    }

View

@using (Html.BeginForm("UpdateAward", "Organisation", FormMethod.Post))
{
@Html.HiddenFor(a => award.AwardID) 

<div class="row">
        <div class="col-md-12">
        <div class="row org-row-main">
            <div class="col-md-7"><h4 class="org-type">Qualification</h4></div>
            <div class="col-md-2"><h5 class="org-completed">Completed</h5></div>
            <div class="col-md-3"><h5 class="org-date">Date</h5></div>
        </div>

        <hr class="org-hr"/>

            @for (int i = 0; i < award.Qualifications.Count(); i++)
            {

                var qualification = award.Qualifications[i];

            <div class="row org-row">
                <div class="col-md-7">
                    @Html.HiddenFor(a => award.Qualifications[i].ID)
                </div>
                <div class="col-md-2">
                        @Html.CheckBoxFor(a => award.Qualifications[i].Achieved)
                </div>
                <div class="col-md-3">@Html.TextBoxFor(a => award.Qualifications[i].DateAchieved, "{0:dd/MM/yyyy}")
                </div>
            </div>
            }
        </div>
</div> 

<button type="submit" class="btn admin-button" style="margin-top: 0;">Save</button>

}

UpdateAward

[HttpPost]
    public ActionResult UpdateAward(CandidateExtended model, FormCollection collection)
    {

        return RedirectToAction("Index", "Login");

    }
Antoin McCloskey
  • 121
  • 1
  • 2
  • 9
  • If you're passing `List` to the View, then perhaps the form is submitting the same object. Try changing the actionmethod to `public ActionResult UpdateAward(List model, FormCollection collection)` – markpsmith Nov 02 '17 at 16:38
  • The fact that you have `var qualification = award.Qualifications[i];` (which you never seem to use) means you have declared a variable named `award` somewhere which means `@Html.HiddenFor(a => award.Qualifications[i].ID)` does not generate the correct `name` attribute - it needs to be `@Html.HiddenFor(a => a.Qualifications[i].ID)` –  Nov 02 '17 at 22:28
  • Refer also [this answer](http://stackoverflow.com/questions/30094047/html-table-to-ado-net-datatable/30094943#30094943) –  Nov 02 '17 at 22:29
  • I had a foreach with (var award in Model) however I have changed this to forloop for (int j = 0; j < Model.Count(); j++). I am now using @Html.HiddenFor(a => Model[j].Qualifications[i].ID) and I am getting some strange behavior. I test the solution and submit the first award it will bind when posted and I can see all the relevant data. However if I go back and try to submit any of the other awards they are appearing as null and are not binding? Would you have idea why this would be? – Antoin McCloskey Nov 03 '17 at 09:38

2 Answers2

1

First (and you may already have this, but we can't see it): your View should start with a line containing @model List<CandidateExtended> (prefix the inner Type with the proper namespace).

Then in the View you should use Model, which is by definition of the exact type specified after the @model keyword.

We see that you are using award, we can't see where it comes from, presumably it is set using something like var award = Model[j] or foreach (var award in Model).
Never use such temporary or helper variables (for efficiency) in a View to render a Form; the View needs the fully qualified name of all objects, e.g. Model.Item[x].SubItem[y] in order to generate Form field names that can be used for Model Binding.

E.g. this : @Html.HiddenFor(a => award.Qualifications[i].ID)
should be: @Html.HiddenFor(a => Model[j].Qualifications[i].ID)

And make the same change in all other places.

Then do as was already suggested, use the List<...> in your Controller Post method.

Finally also please remove the FormCollection, it is not needed if you have everything set up as described here. Decent MVC code never uses FormCollection, ever.

Peter B
  • 22,460
  • 5
  • 32
  • 69
  • Thanks Peter B. I did have a foreach (var award in Model) but I changed it to a for loop targeting the Model like you said. I therefore used @Html.HiddenFor(a => Model[j].Qualifications[i].ID) as you suggested and it is working. However when I test the solution and submit the first award it will bind when posted and I can see all the relevant data. However if I go back and try to submit any of the other awards they are appearing as null and are not binding? Would you have idea why this would be? – Antoin McCloskey Nov 03 '17 at 09:01
  • I have also changed the name space and FormCollection as suggested. – Antoin McCloskey Nov 03 '17 at 09:19
  • It is hard to guess what could be happening without seeing the current state of affairs, so better 'close' (Accept) this question, and create a new question containing the code that you have now. – Peter B Nov 03 '17 at 09:51
  • Thanks Peter. I have accepted this answer and opened a new question here: https://stackoverflow.com/questions/47093198/c-sharp-mvc-viewmodel-one-posted-result-binds-whilst-others-are-null – Antoin McCloskey Nov 03 '17 at 10:38
0

Try calling the posted method on a separate button instead of BeginForm and it should work.

MCoder
  • 113
  • 5
  • Sorry. Could you explain that a little futher? I dont understand how else I could post the values if not using BeginFom? @MCoder – Antoin McCloskey Nov 02 '17 at 16:32
  • @markpsmith I have changed the actionmethod to List but it is still returning Null on the postback. – Antoin McCloskey Nov 02 '17 at 16:43
  • You are trying to call a post method on Beginform. How would you call that method on click event of a button? Try to create a button and on click event of that button call the UpdateAward method. That may not be your final output but it will show you where the error is. – MCoder Nov 02 '17 at 17:25