Search code examples
c#asp.net-mvcrazorviewbag

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 :)


Solution

  • 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.