skylake skylake - 2 months ago 10
C# Question

How to have 'details' and 'create' in same view? MVC

The code below (in controller) read a specific row-data from the previous page(gets

id
via
@Html.ActionLink("Details", "Details", new { id=item.Id })
inside the View)

public ActionResult Details(Guid? id) {

if (id == null) {
return Content("id = null..");
}
Review review = db.Reviews.Find(id);
if (review == null)
{
return Content("review = null.. ");
}
return View(review);
}


So far so good, but now I want to allow visitors/user to leave a comment, give a like/dislike and so on. I guess it's a combination of details/create(in another words, read/insert) template for View? What do I've to do in controller to make this work?

From here on, I've no clue what to do since I'm new to MVC.

This is how my database looks like: (picked from EF-model(Database-first)):

User:

public System.Guid Id { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public string Email { get; set; }


Review: (Where the author creates a review)

public System.Guid Id { get; set; }
public System.Guid CreatorUserId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public System.DateTime CreatedDate { get; set; }
public int UserRating { get; set; }
public int LikeCount { get; set; }
public int DislikeCount { get; set; }


CommentReview: (Where review gets its comment from other user)

public System.Guid Id { get; set; }
public System.Guid UserId { get; set; }
public System.Guid ReviewId { get; set; }
public string Comment { get; set; }
public System.DateTime CreatedDate { get; set; }


UserReview: (if user has liked the review already, to avoid multiple likes)

public System.Guid Id { get; set; }
public System.Guid UserId { get; set; }
public System.Guid ReviewId { get; set; }
public bool HasLiked { get; set; }


This is how View for displaying the details about selected row looks like:

<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. Not sure how to add "create"-inputs for e.g. comments etc*@


I'm using
Session["LoggedUserID"]
to get the
Id
for the current user.

Answer

First of all change your entities a little bit for configure relations between them:

public class User
{
    [Key]
    public System.Guid Id { get; set; }
    public string UserName { get; set; }
    public string Password { get; set; }
    public string Email { get; set; }

    public virtual ICollection<CommentToReview> Comments { get; set; }
    public virtual ICollection<UserToReview> Reviews { get; set; }
}

public class Review
{
   [Key]
   public System.Guid Id { get; set; }
   public System.Guid CreatorUserId { get; set; }
   public string Title { get; set; }
   public string Description { get; set; }
   public System.DateTime CreatedDate { get; set; }
   public int UserRating { get; set; }
   public int LikeCount { get; set; }
   public int DislikeCount { get; set; }

   public virtual ICollection<CommentToReview> Comments { get; set; }
   public virtual ICollection<UserToReview> Users { get; set; }
}

public class CommentToReview
{
    [Key]
    public System.Guid Id { get; set; }
    [ForeignKey("User")]
    public System.Guid UserId { get; set; }
    public virtual User User { get; set; }
    [ForeignKey("Review")]
    public System.Guid ReviewId { get; set; }
    public virtual Review Review { get; set; }
    public string Comment { get; set; }
    public System.DateTime CreatedDate { get; set; } 
}

public class UserToReview
{
    [Key]
    public System.Guid Id { get; set; }
    [ForeignKey("User")]
    public System.Guid UserId { get; set; }
    public virtual User User { get; set; }
    [ForeignKey("Review")]
    public System.Guid ReviewId { get; set; }
    public virtual Review Review { get; set; }
    public bool HasLiked { get; set; }
}

now go to ReviewsController and add this actions which add new comment to it :

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult CreateComment([Bind(Include = "Id,UserId,ReviewId,Comment,CreatedDate")] CommentToReview commentToReview)
    {
        if (ModelState.IsValid)
        {
            commentToReview.Id = Guid.NewGuid();
            commentToReview.UserId = Session["LoggedUserID"].ToString();
            commentToReview.CreatedDate = DateTime.Now;
            db.CommentToReviews.Add(commentToReview);
            db.SaveChanges();
            return RedirectToAction("Details", "Reviews", new { id = commentToReview.ReviewId });
        }

        return RedirectToAction("Details", "Reviews", new { id = commentToReview.ReviewId });
    }

Then you should Create below PartialViews under Views/Review Folder :

_CreateCommentPartial.cshtml : For Create a comment in the same page of details

@using (Html.BeginForm("CreatePartial", "Reviews"))
{
@Html.AntiForgeryToken()   
<div class="form-horizontal">
    <h4>CommentToReview</h4>
    <hr />
    @Html.ValidationSummary(true, "", new { @class = "text-danger" })
    @Html.HiddenFor(model=> model.ReviewId)

    <div class="form-group">
        @Html.LabelFor(model => model.Comment, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.Comment, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.Comment, "", 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>
}


@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}

_CommentDetailPartial.cshtml : For show existing comment details

@model MvcApp.Models.CommentToReview


<div class="panel panel-default">
<div class="panel-heading">@Model.User.Email</div>
<div class="panel-body">
    @Model.Comment
</div>
<div class="panel-footer">@Model.CreatedDate.ToString()
</div>
</div>

at the end modify Details view of Review (Views/Reviews/Details.cshtml)

@model MvcApp.Models.Review

@{
   ViewBag.Title = "Details";
}

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

    <dt>
        @Html.DisplayNameFor(model => model.CreatedDate)
    </dt>

    <dd>
        @Html.DisplayFor(model => model.CreatedDate)
    </dd>    
</dl>
<h6>Comments</h6>
<hr />
@foreach(var comment in Model.Comments)
{
    Html.RenderPartial("_CommentDetailsPartial.cshtml", comment);
}

<br />
@{
    var newComment = new MvcApp.Models.CommentToReview { ReviewId = Model.Id         };

    Html.RenderPartial("_CreateComment", newComment);
}
</div>
<p>
    @Html.ActionLink("Edit", "Edit", new { id = Model.Id }) |
    @Html.ActionLink("Back to List", "Index")
</p>

Only be aware that I create all PartialViews and actions under Review Controller and View for simplicity. and also when you add a new comment it refresh the page and maybe its better that you use ajax for better performance.

Comments