Robert Prine Robert Prine - 3 months ago 9
HTML Question

How can I return a view from another model in a controller?

I stumbled across a better way to display my comment form within the post window with

<p id="createPostButton">@Html.Action("Create", "Comment", new { id = Model.PostId })</p>
(I was originally just trying to create a button to take you to the comment page sending the post id). However, I now get an error when I try to pass back the post/details/{id} view in the CommentController. It keeps trying to look in comment or shared folders passing either post OR details/{id} instead of post/details/{id}.

Post/Details Razor HTML file:

@model FantaC.Models.Post

@{
ViewBag.Title = @Html.DisplayFor(model => model.PostName);
}

<h2>@Html.DisplayFor(model => model.PostName)</h2>

<div class="row">
<div class="col-md-8 whiteBorder scroll">
<div class="postName">
<h4>Written by: @Html.DisplayFor(model => model.UserName)</h4>
<img src="@Html.DisplayFor(model => model.PostImage)" />
</div>
<div class="postContent">
<p>@Html.DisplayFor(model => model.PostContent)</p>
</div>
</div>
<div class="col-md-4 whiteBorder scroll">
<h4>Comments</h4>

@foreach (var comment in Model.PostComments)
{
<h5>@Html.DisplayFor(modelItem => comment.UserName)</h5>
<h5>@Html.DisplayFor(modelItem => comment.CommentSubject)</h5>
<p>@Html.DisplayFor(modelItem => comment.CommentContent)</p>
<p>
@Html.ActionLink("Edit", "../Comment/Edit", new { id = comment.CommentId }) |
@Html.ActionLink("Details", "../Comment/Details", new { id = comment.CommentId }) |
@Html.ActionLink("Delete", "../Comment/Delete", new { id = comment.CommentId })
</p>
}

<p id="createPostButton">@Html.Action("Create", "Comment", new { id = Model.PostId })</p> <!--**********This is the line that is important-->

@*@=Html.RenderAction("Create", "Comments", new { postId = Model.PostId });*@
@*@Html.Partial("Comments")*@
</div>
</div>
<p>
@*@Html.ActionLink("Add a Comment", "Create", "Comment")*@
@Html.ActionLink("Comment", "Create", "Comment", new { id = Model.PostId }, null) |
@Html.ActionLink("Back to List", "Index")




The Comment/Create Razor HTML file that is getting pulled in by the Html.Action:

@model FantaC.Models.Comment

@{
Layout = null;
}

@{
ViewBag.Title = "Create";
}

<h2>Create</h2>


@using (Html.BeginForm())
{
@Html.AntiForgeryToken()

<div class="form-horizontal">
<h4>Comment</h4>
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })

<div class="form-group">
@Html.LabelFor(model => model.CommentSubject, htmlAttributes: new { @class = "control-label col-md-10 displayBlock" })
<div class="col-md-12">
@Html.EditorFor(model => model.CommentSubject, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.CommentSubject, "", new { @class = "text-danger" })
</div>
</div>

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

@*<div>
@Html.ActionLink("Back to List", "Index")
</div>*@

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


The important section from the CommentController:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(string id, [Bind(Include = "CommentSubject,CommentContent")] Comment model)
{
if (ModelState.IsValid)
{
ApplicationUser user = System.Web.HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>().FindById(System.Web.HttpContext.Current.User.Identity.GetUserId());

var commentId = (23817 + db.Comment.Count()).ToString().PadLeft(10, '0');

var comment = new Comment
{
CommentId = commentId,
PostId = id,
UserName = user.UserName,
PostTime = DateTime.Now,
CommentSubject = model.CommentSubject,
CommentContent = model.CommentContent
};

db.Comment.Add(comment);
db.SaveChanges();

return View("Details/" + id, "Post");
}

return View(model);
}


I also tried
return View("../post/details/" + id);
to no avail. How can I get back up to the post view url (post/details/{id} from the CommentController?

As a side note, I had it almost working by taking out the returns and making the method
void
, but after clicking the submit comment button the whole comment/create form would disappear. I would be fine going back to that way of doing things if anyone knows a way to make the form stay after clicking the create comment button.

Thanks for any help! :)

Edit:

I forgot to mention that I tried this too. It returns an error that looks like this:

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.InvalidOperationException: Child actions are not allowed to perform redirect actions.

Source Error:

Line 32: }
Line 33:
Line 34: @Html.Action("Create", "Comment", new { id = Model.PostId })
Line 35:

Answer

You should follow the PRG (POST-REDIRECT-GET) pattern. After saving the comment, you should redirect to the post details page.

You may use the RedirectToAction method to return a RedirectResponse back to the browser which will make a new GET request to the post details action method.

So replace

return View("Details/" + id, "Post");

with

return RedirectToAction("Details" , "Post", new {id=id});