Reputation: 417
The ViewData item that has the key 'SelectedRating' is of type 'System.Int32' but must be of type 'IEnumerable'.
This is the error I get when I'm trying to add a DropDownList via partial view. I tried to follow this solution but I somehow failed to do so due to my low experience in coding.
This is how my code look for the moment:
[HttpGet]
public ActionResult Rating()
{
RateReviewVM model = new RateReviewVM()
{
ReviewId = id,
RatingList = new SelectList(Enumerable.Range(1, 10))
};
return View(model);
}
[HttpPost]
public ActionResult Rating(RateReviewVM model) {
if (!ModelState.IsValid)
{
model.RatingList = new SelectList(Enumerable.Range(1, 10));
return View(model);
}
//...
View for _RatingPartial
@model xxxx.ViewModel.RateReviewVM
<h6>Rate the review</h6>
using (Html.BeginForm()) {
<div class="form-group">
@Html.LabelFor(m => m.SelectedRating)
@Html.DropDownListFor(m => m.SelectedRating, Model.RatingList, "-Please select-", new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.SelectedRating)
</div>
<button type="submit" class="btn btn-default submit">Rate</button>
}
}
This is how I try to display _RatingPartial view
@{
var newRating = new xxxx.ViewModel.RateReviewVM { ReviewId = Model.Id };
Html.RenderPartial("_RatingPartial", newRating);
}
Model
public class RateReviewVM {
public System.Guid Id { get; set; }
public System.Guid UserId { get; set; }
public System.Guid ReviewId { get; set; }
public bool HasLiked { get; set; }
public Nullable<int> Rating { get; set; }
public int SelectedRating { get; set; }
public IEnumerable<SelectListItem> RatingList { get; set; }
}
EDIT: ---
Details View(Main)
@model xxxx.Review
@{
ViewBag.Title = "Details";
}
@*@{
//var newRating = new xxxx.ViewModel.RateReviewVM { ReviewId = Model.Id };
@Html.RenderPartial("_RatingPartial", newRating)
}*@
<h2>Details</h2>
<div>
<h4>Review</h4>
<hr/>
<dl class="dl-horizontal">
<dt>
@Html.DisplayNameFor(model => model.Title)
</dt>
<dd>
@Html.DisplayFor(model => model.Title)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Description)
</dt>
<dd>
@Html.DisplayFor(model => model.Description)
</dd>
@* and so on.. *@
</dl>
</div>
<p>
@Html.ActionLink("Back to List", "Index")
</p>
Controller for Details
public ActionResult Details(Guid? id) {
Review review = db.Reviews.Find(id);
return View(review);
}
Model Review for Details
public System.Guid Id { get; set; }
public System.Guid CreatorUserId { get; set; }
[Required]
public string Title { get; set; }
[Required]
public string Description { get; set; }
public System.DateTime CreatedDate { get; set; }
//etc - some irrelevant property
public virtual ICollection<RateReview> Users { get; set; }
Upvotes: 1
Views: 93
Reputation:
The reason for the error is that the value of RatingList
is null
(refer The ViewData item that has the key 'XXX' is of type 'System.Int32' but must be of type 'IEnumerable' for a detailed explanation).
In you case, your using the RenderPartial()
method to pass a model to the view, and you do not set the value of property RatingList
(RenderPartial
does not call your public ActionResult Rating()
method.
You could solve the error by using
@{
var newRating = new xxxx.ViewModel.RateReviewVM
{
ReviewId = Model.Id,
RatingList = new SelectList(Enumerable.Range(1, 10))
};
Html.RenderPartial("_RatingPartial", newRating);
}
so that the property is populated, but since you already have a GET method, then it would be better to use the RenderAction()
method which does call the server method
@{ Html.RenderAction("Rating", yourControllerName, new { id = Model.Id }); }
Note I'm assuming your method is actually public ActionResult Rating(Guid id)
since the current code would throw an exception since id
is not declared.
However, that will not solve all your problems since the POST method returns the review is ModelState
is invalid (as it should), but its a different view from your current one. It will also throw an exception because your not repopulating the RatingList
property.
The correct approach to solve this is to have a view model for Review
which contains another view model for comment.
public class ReviewVM
{
... properties of review including property for collection of existing commnets
public CommentVM Comment { get; set; } // for generating a new comment form
}
and in the Review.cshtml
view, add the form for generating new comment, and post back to to a method which has ReviewVM
as the parameter. Then if ModelState
is invalid, repopulate the properties of ReviewVM
based on its ID, and repopulate the RatingList
property of its Comment
property. Alternatively, if your wanting to allow the user to add multiple comments for a Review, consider a dialog form for adding a new comment and use ajax to post the form.
Upvotes: 1
Reputation: 52
Use ViewBag for your dropdrownlist and it will work for partialView too. and then you don't need ViewModels. your model classes are fine to work with.
First add CreateRate
action to Reviews
Controller
public ActionResult CreateRate(RateReview rate)
{
if (ModelState.IsValid)
{
rate.Id = Guid.NewGuid();
db.RateReviews.Add(rate);
db.SaveChanges();
return RedirectToAction("Details", "Reviews", new { id = rate.ReviewId });
}
return View();
}
Modify Details
action to create a viewbag data as below
public ActionResult Details(Guid? id)
{
Review review = db.Reviews.Find(id);
ViewBag.RatingList = new SelectList(Enumerable.Range(1, 10));
return View(review);
}
Modify your PartialView as below to target CreateRate
action, add a HiddenFor Helper for ReviewId and modify dropdownlistfor to read from ViewBag. I use RateReview Model instead of your ViewModel
@model WebApp.Models.RateReview
@using (Html.BeginForm("CreateRate", "Reviews"))
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>RateReview</h4>
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@Html.HiddenFor(model => model.ReviewId)
<div class="form-group">
@Html.LabelFor(model => model.SelectedRating, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownListFor(model => model.SelectedRating, (ViewBag.RatingList as SelectList), new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.SelectedRating, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
at end your Details
View is just fine :
@model WebApp.Models.Review
@{
ViewBag.Title = "Details";
}
@{
var newRating = new WebApp.Models.RateReview { ReviewId = Model.Id };
Html.RenderPartial("_RatingPartial", newRating);
}
<h2>Details</h2>
<div>
<h4>Review</h4>
<hr />
...
Upvotes: 1