Search code examples
c#asp.net-mvcrazor-pageshtml.beginform

Passing list and a model instance via HTML BeginForm


Currently I'm making a project while studying asp.net mvc5. I've got a many to many relationship in my Db and I'm working on a solution to attach certain actors from one table to movies from another table. Here are the models for them.

public class Movie
{
    public Movie()
    {
        this.Actors = new HashSet<Actor>();
        this.Customers = new HashSet<Customer>();
    }

    public int Id { get; set; }
    [Required]
    public string Name { get; set; }
    [Required]
    public string Genre { get; set; }
    [Required]
    public DateTime ReleaseDate { get; set; }
    [Required]
    public DateTime AddDate { get; set; }
    public virtual ICollection<Actor> Actors { get; set; }
    public virtual ICollection<Customer> Customers { get; set; }

}
public class Actor
{
    public Actor()
    {
        this.Movies = new HashSet<Movie>();
    }
    public int id { get; set; }
    [Required]
    public string Name { get; set; }
    public virtual ICollection<Movie> Movies { get; set; }
    public DateTime BirthDate { get; set; }
}

So I've came up with an idea of checking actors on the AddActors page and adding them to the chosen movie Here is the view of that:

@model Movie_Rentals.ViewModels.AddActorViewModel
@{
    ViewBag.Title = "AddActors";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Add Actors Form</h2>
@using (Html.BeginForm("SaveActors", "Movies"))
{
foreach (var a in Model.Actors)
{
    <ul>
        <input type="checkbox" name="addactors" value="@Model.Actors" />
            @Html.DisplayFor(modelItem => a.Name);
        <br />
    </ul>
}
@Html.HiddenFor(m => m.Movie)
<button type="submit" class="btn btn-primary">Save</button>
}

After submitting the form, we get to the controller

public ActionResult SaveActors(FormCollection formCollection)
    {
        var movieInDb = /*???*/;
        {
            foreach (/*var actor in formCollection*/)
            {
                movieInDb.Actors.Add(actor);
            }
            _context.SaveChanges();
        }
        return RedirectToAction("Details", "Movies", new { id = movieInDb.Id });

    }

The question is how do I get movie and actors from this form that I've passed to the controller? Is there another way to to pass a list of checked actors and a movie to a controller instead of using HTML BeginForm?

UPD. I've found that data will be linked if I use HTML BeginForm, so instead of passing FormCollection to the controller, I've tried to pass movie and the list of actors to see if it will help me in any ways. So now the controller that saves results to Db looks like this

public ActionResult SaveActors(List <Actor> actors, Movie movie)
    {
        foreach (var a in actors)
        {
            movie.Actors.Add(a);
        }
        _context.SaveChanges();

        return RedirectToAction("Details", "Movies", new { id = movie.Id });
    }

But I get System.NullReferenceException. It seems like the list is empty. Also I believe that I don't actually hav an instance of a movie. Now I'm really stuck here.


Solution

  • Your checkbox list won't bind back to the model on post, you need to build it this way so that it uses and index and it should post back to your controller as you want it to.

    @using (Html.BeginForm("Index", "Home", FormMethod.Post))
    {
        <ul>
        @for (int idx = 0; idx < Model.Actors.Count; idx++) {
            <li>
              @Html.HiddenFor(x=>Model.Actors[idx].ID)
              @Html.CheckBoxFor(x=>Model.Actors[idx].IsSelected)
              @Html.DisplayFor(x => Model.Actors[idx].Name)
               @Html.HiddenFor(x => Model.Actors[idx].HobbiesName)
            </li>
    
        }
        </ul>
        }