Nick
Nick

Reputation: 3965

Foreach with data from the viewbag

Below is my code.

Model

public class ShiftsModel
{
    public string UID { get; set; }
    public string Date { get; set; }
    public string Time { get; set; }
    public string Location { get; set; }
}

Controller

public class HomeController : Controller
{
    public string xmlPath = HostingEnvironment.MapPath("~/App_Data/data.xml");

    public ActionResult Index()
    {
        XDocument xml = XDocument.Load(xmlPath);

        var shifts = (from b in xml.Descendants("Shift")
                      select new ShiftsModel
                     {
                         UID = (string)b.Attribute("UID"),
                         Date = (string)b.Element("Date"),
                         Time = (string)b.Element("Time"),
                         Location = (string)b.Element("Location")
                     }).ToList();

        return View(shifts);
    }

}

I'd now like to reference this in my Index.cshtml file like so:

@foreach(var shift in (List<object>ViewBag.shifts)) {
<tr>
    <td>
        <input type="text" id="date" name="date" placeholder="Date" value="@(ViewBag.date)" }>
    </td>
    <td>
        <input type="text" id="time" name="time" placeholder="Shift time" value="@(ViewBag.time)" }>
    </td>
    <td>
        <input type="text" id="location" name="location" placeholder="Location" value="@(ViewBag.location)" }>
    </td>
</tr>
}

However, I get an error on the List<object>ViewBag.shifts line saying:

Represents a strongly typed list of objects that can be accessed by index.

Any suggestions as to what I'm doing wrong please? Thank you :)

Upvotes: 3

Views: 28322

Answers (2)

teo van kot
teo van kot

Reputation: 12491

As I can see you just don't pass your collection to View through ViewBag in your controller.

You should pass it like this:

public ActionResult Index()
{
    XDocument xml = XDocument.Load(xmlPath);

    var shifts = (from b in xml.Descendants("Shift")
                  select new ShiftsModel
                 {
                     UID = (string)b.Attribute("UID"),
                     Date = (string)b.Element("Date"),
                     Time = (string)b.Element("Time"),
                     Location = (string)b.Element("Location")
                 }).ToList();
    ViewBag.shifts = shifts; // this line will pass your object
    return View();
}

Then on your View:

    @foreach(var shift in (List<ShiftsModel>ViewBag.shifts)) {
    <tr>
        <td>
            <input type="text" id="date" name="date" placeholder="Date"
                   value="@(shift.Date)" }>
        </td>
        <td>
            <input type="text" id="time" name="time" placeholder="Shift time"
                   value="@(shift.Time)" }>
        </td>
        <td>
            <input type="text" id="location" name="location" placeholder="Location"
                   value="@(shift.Location)" }>
        </td>
    </tr>
    }

But MVC way to solve your problem is use Strongly typed View like this:

Controller:

public ActionResult Index()
{
    XDocument xml = XDocument.Load(xmlPath);

    var shifts = (from b in xml.Descendants("Shift")
                  select new ShiftsModel
                 {
                     UID = (string)b.Attribute("UID"),
                     Date = (string)b.Element("Date"),
                     Time = (string)b.Element("Time"),
                     Location = (string)b.Element("Location")
                 }).ToList();
    ViewData.Model = shifts; // this line will pass your object but now to model
    return View();
}

View:

@model List<ShiftsModel> @*this is where your model is defined on view*@


@for(int i = 0; i < Model.Count(); i++) {
<tr>
    <td>
        @Html.TextBoxFor(x=> Model[i].Date, new { placeholder = "Date" })
    </td>
    <td>
        @Html.TextBoxFor(x=> Model[i].Time, new { placeholder = "Shift time" })
    </td>
    <td>
        @Html.TextBoxFor(x=> Model[i].Location, new { placeholder = "Location" })
    </td>
</tr>
}

You need for loop not foreach to solve MVC issues with array binding if you will post this model to the controller.

Upvotes: 7

Thomas Mulder
Thomas Mulder

Reputation: 779

As teo van kot points out, you are not assigning your shifts to your viewbag.. But it is even better, and more appropriate to pass your ShiftsModel as the model and not via the ViewBag... Make sure your Index.cshtml file has the following using statement:

@model IEnumerable<ShiftsModel>

And if you pass your model like you already did: return View(shifts); then you can iterate through your model like this:

@foreach(var shift in Model) 
{
   // do something with your shift
}

Upvotes: 1

Related Questions