Your view does not have a form
tag
@model MyBlogger.ViewModel.PostsViewModel
@{
ViewBag.Title = "EditPostTag";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>EditPostTag</h2>
@using (Html.BeginForm()) // add this
{
....
}
Edit (further to comments and some misunderstanding by OP)
Using a view model as you have done is always good practice however you are not taking advantage of it by continuing to use ViewBag
and using it to hold a data model instead of including just the properties you need to the view. I recommend it be
public class PostViewModel // its for a single post - not plural?
{
public int ID { get; set; }
[Required(ErrorMessage = "Please enter a title")]
public string Title { get; set; }
[Display(Name = "Tags")] // plus [Required] is at least one tag must be selected
public List<int> SelectedTags { get; set; }
public SelectList TagsList { get; set; }
// Its unclear if you really need the following 2 properties (see notes)
[Display(Name = "User")]
[Required(ErrorMessage = "Please select a user")]
public int UserID { get; set; }
public SelectList UserList { get; set; }
}
Side note: Its a bit unclear why you are allowing the user to select another user to be associated with the Post
object. I suspect that when you save the Post
you should be just assigning the current user in the controllers POST method
Your controller methods would then be (assume this is PostController
)
public ActionResult Edit(int id)
{
Post post = db.Posts.Include(i => i.Tags).FirstOrDefault(i => i.Id == id); // First() will throw an exception is the item is not found
if (post == null) { return HttpNotFound(); }
PostViewModel model = new PostViewModel()
{
ID = post.ID,
Title = post.Title,
SelectedTags = post.Tags.Select(t => t.Id)
}; // include UserId property?
ConfigureEditModel(model);
return View(model);
}
[HttpPost]
public ActionResult Edit(PostViewModel model)
{
if (!ModelState.IsValid)
{
ConfigureEditModel(model);
return View(model);
}
// map your view model to a data model, save it and redirect
}
private void ConfigureEditModel(PostViewModel model)
{
model.TagsList = new SelectList(db.Tags, "Id", "Name");
model.UserList = new BlogUsers(db.Tags, "UserID", "Email"); // ??
}
Side note: Either SelectList
or IEnumerable<SelectListItem>
is acceptable (I find SelectList
easier to read but its a millisecond or two slower because it uses reflection to generate the IEnumerable<SelectListItem>
) but there is no point using the 4th parameter as you did with new SelectList(db.BlogUsers, "UserID", "Email", postsViewModel.posts.Id);
- your binding to a property and the selected item will be the value of the property, so trying to set the Selected
property is just ignored)
And finally the view (simplified to show only helpers without html attributes)
@model MyBlogger.ViewModel.PostViewModel
@using (Html.BeginForm())
{
@Html.ValidationSummary(true)
// @Html.HiddenFor(model => model.posts.Id) not required
@Html.LabelFor(m => m.Title)
@Html.TextBoxFor(m => m.Title)
@Html.ValidationMessageFor(m => m.Title)
// Is the UserId property required?
@Html.LabelFor(m => m.UserID)
@Html.DropDownListFor(m => m.UserID, Model.UserList, "Please select")
@Html.ValidationMessageFor(m => m.UserID)
@Html.LabelFor(model => model.SelectedTags)
@Html.ListBoxFor(m => m.SelectedTags, Model.TagsList)
// Add ValidationMessageFor() if at least one should be selected
<input type="submit" value="Save" class="btn btn-default" />
}
Side note: Since the parameter of your method is named id
, the value of the id
property will be added to the route parameters so it is not necessary to add a hidden input for the view models ID
property (the DefaultModelBinder
reads route values in addition to form values, so the view models ID
property will be correctly bound (to 12
in you case)