1

I have a newbie question, which I have tried to understand for the past few days. Hopefully someone can be kind enough to help me understand the programming flow.

Assuming I have a model, the information is stored in the database:

public class Student
{
    public int studentID { get; set; }
    public string studentName { get; set; }
    public strin studentGrade {get; set; }
}

public class StudentDBContext : DbContext
{
    public DbSet<Student> Students { get; set; }
}

and I want to display it into the view, with additional checkbox so I can select which students to be promoted into the next grade. I read that one way to do it is by putting into view model:

public class StudentViewModel
{
    public bool promoted { get; set; }
    public Student stu { get; set; }
}

But I am stuck on is this the way to do it? and if yes, how do you put into the view where it will display all the students with a checkbox next to it. Afterwards, I want to update all the grade for the students whose checkboxes are ticked. For example:

Student A, Student B, Student D promoted from Grade 1 to Grade 2. So I want to display the students, tick Student A, B and D and submit to update the Grade.

Step by step example will be much appreciated.

Update 1:

Controller:

    [HttpGet]
    public ViewResult CheckBox()
    {
        var studentViewModels = db.Students.Select(m => new StudentViewModel()
        {
            stu = m
        }).ToList();

        return View(studentViewModels);
    }

    [HttpPost]
    public ActionResult CheckBox(IList<studentViewModel> list)
    {

        foreach (var stuUpdate in list.Where(m => m.promoted))
        {
            var stuRow = db.Students.Find(stuUpdate.stu.studentID);
            stuRow.studentName = stuRow.studentName + "1";
            db.Entry(stuRow).State = EntityState.Modified;
            db.SaveChanges();
            return RedirectToAction("CheckBox");
        }
        return RedirectToAction("CheckBox");
    }

View:

@model IList<School.ViewModels.StudentViewModel>

@using (Html.BeginForm())
{
<table>
<tr>
    <th>

    </th>
    <th>
        student ID
    </th>
    <th>
        student name
    </th>
    <th>
        student grade
    </th>
</tr>
@foreach (var item in Model) {
    <tr>
        <td>
            @Html.CheckBoxFor(modelItem => item.promoted)
            @Html.HiddenFor(modelItem => item.stu.studentID)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.stu.studentID)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.stu.studentName)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.stu.studentGrade)
        </td>
    </tr>
}

</table>
<input type="submit" value="save" />
}

However currently hit by the following error: Value cannot be null. Parameter name: source

Source Error:
foreach (var stuUpdate in list.Where(m => m.promoted))
tereško
  • 58,060
  • 25
  • 98
  • 150
blurryMVC
  • 93
  • 2
  • 12

1 Answers1

2

A very basic "step by step" (done in SO, so I probably did a few mistakes, but you've got the idea).

You have always a few ways to do these kind of things, so... just really take it as a sample, and find other examples to get other ideas.

well, first, in your controller, you will have a GET action (to see the list).

[HttpGet]
public ViewResult StudentList() {
   //retrieve all students. With the Select, we create a new instance of StudentViewModel for each student.
  //assuming StudentDbContext being a property of your controller, if it's not, you can instantiate a new one (in a using clause)
   var studentViewModels = StudentDbContext.Students
              .Select(m => new StudentViewModel() {
                   stu = m 
                  //we don't say nothing about promoted : 
                  //it will be there as "false" by default, which is probably what we want.
              }).ToList();
    //now we've got a list of StudentViewModel. This will be the model of our view
    return View(studentViewModels);

}

Then we've got a view, StudentList.cshtml

in this view, we will display a table, with a line for each student : the studentId (hidden in this case), the name (display only), the grade (display only), and a checkbox.

We need a for loop (not a foreach) to get fine model binding.

@model IList<StudentViewModel>

@using (Html.BeginForm()) {
<table>
  <tr>
     <th>Student name</th>
     <th>Student grade</th>
     <th>Promote</th>
  </tr>
  @for (var i = 0; i < Model.Count(); i++) {
   <tr>
      <td>@Html.HiddenFor(m => Model[i].Student.studentID)
          @Html.DisplayFor(m => Model[i].Student.studentName)
      </td>
      <td>@Html.DisplayFor(m => Model[i]Student.studentGrade)</td>
      <td>@Html.CheckBoxFor(m => Model[i].promoted)</td>
  </tr>
  }
</table>
<input type="submit" value="save" />
}

This form will lead to another POST action (same name as the GET one, depending of what you have in your Html.BeginForm)

[HttpPost]
public ActionResult StudentList(IList<StudentViewModel> list) {
    //we treat only the lines where checkbox has been checked
    foreach (var studentViewModel in list.Where(m => m.promoted) {
       var student = StudentDBContext.GetById(studentViewModel.Student.studentID);//get student entity instance from context
       student.studentGrade ="<new value>";
       StudentDBContext.SaveChanges();//save changes, you must have such a method somewhere.
    }
    return Action("StudentList");
}

Little detail :

Try to respect some really basic "usual" practices : for example in c#, Properties should begin by an uppercase letter (so StudentGrade, StudentName, Promoted, etc).

Raphaël Althaus
  • 59,727
  • 6
  • 96
  • 122
  • Thank you for the guidance. However I get error saying value cannot be null in the POST method. is there something I miss? Also thank you for the pointer about the properties. I am actually doing self learning on MVC, is there any guide or tutorial that can help me learn it? – blurryMVC Oct 20 '12 at 06:15
  • @blurryMVC if your actual code is what you edited in your question, change, as pointed in my answer, your `foreach` loop in your view to a `for` loop – Raphaël Althaus Oct 22 '12 at 06:59
  • Thank you for the kind advice, however with a minor adjustment on the post method. Initially it will only update the first row, only after I moved out "return Action("StudentList");" outside the for loop it update both. Also may I know how come foreach not working while for loop working? I read forums and I thought foreach is to replace for loop itself. Is there special situation where foreach should not be used? – blurryMVC Oct 22 '12 at 11:27
  • @blurryMVC for instead of foreach in editable lists : http://stackoverflow.com/questions/8894442/mvc-razor-view-nested-foreachs-model. And right, I returned too fast ;) – Raphaël Althaus Oct 22 '12 at 12:05
  • wow, thanks for the help and link. I will try to digest it. :) – blurryMVC Oct 22 '12 at 13:13