Render action return View(); form problem - asp.net-mvc

I'm new to MVC, so please bear with me. :-)
I've got a strongly typed "Story" View. This View (story) can have Comments.
I've created two Views (not partials) for my Comments controller "ListStoryComments" and "CreateStoryComment", which do what their names imply. These Views are included in the Story View using RenderAction, e.g.:
<!-- List comments -->
<h2>All Comments</h2>
<% Html.RenderAction("ListStoryComments", "Comments", new { id = Model.Story.Id }); %>
<!-- Create new comment -->
<% Html.RenderAction("CreateStoryComment", "Comments", new { id = Model.Story.Id }); %>
(I pass in the Story id in order to list related comments).
All works as I hoped, except, when I post a new comment using the form, it returns the current (parent) View, but the Comments form field is still showing the last content I typed in and the ListStoryComments View isn’t updated to show the new story.
Basically, the page is being loaded from cache, as if I had pressed the browser’s back button. If I press f5 it will try to repost the form. If I reload the page manually (reenter the URL in the browser's address bar), and then press f5, I will see my new content and the empty form field, which is my desired result.
For completeness, my CreateStoryComment action looks like this:
[HttpPost]
public ActionResult CreateStoryComment([Bind(Exclude = "Id, Timestamp, ByUserId, ForUserId")]Comment commentToCreate)
{
try
{
commentToCreate.ByUserId = userGuid;
commentToCreate.ForUserId = userGuid;
commentToCreate.StoryId = 2; // hard-coded for testing
_repository.CreateComment(commentToCreate);
return View();
}
catch
{
return View();
}
}

The answer is to use return RedirectToAction(). Using this enforces the PRG pattern and achieves my goal.
My earlier comment to my original post did cause an error, that I guess I'm still confused about, but this works:
return RedirectToAction("Details", "Steps", new { id = "2" });

I, and this is a personal opinion, think you've tackled this the wrong way.
Personally I would;
Create a view called ListStories.
Create a partial view that lists the
stories.
Create a partial view to create a
story.
When you want to add a story, simply
show the add story html.
Then when the user presses a button
you do a jQuery postback, add the
new story and return a PartialView
of either the new story or all the
stories.
If you return a partial view of all
stories then replace the bounding
div that contains all the stories
with the new data.
If you return only a single story
then append it to the end of the div
containing the stories.
I know this means a lot of re-work and it sounds complex and like a lot of work but doing it like this means greater flexibility later on because you can re-use the partial views or you can make a change once and all views using that partial view are now updated.
also, using jQuery means that the adding of stories looks seemless w/out any obvious post back which is nice.

Since the problem seems to be caching, you can simply disable/limit caching.
Add the following attribute to your actions:
[OutputCache(Duration = 0, VaryByParam = "none")]
This will tell the browser to cache the page, but for 0 seconds. When the post reloads the page, you should see the desired results.

The answer is to make sure your form action is properly set. If you have used renderaction and not set the form controller and action manually then the action will be the current URL.
Try:
<% using (Html.BeginForm("ActionName", "ControllerName")) {%>
Instead of:
<% using (Html.BeginForm()) {%>

Related

Create Linked Database Record in MVC5 Controller

I'm just starting out with ASP.NET MVC, and anything but the basics throws me at the moment, so please bear with me.
I am writing a little application á la UserVoice, whereby users can post ideas, and other users can vote for those posts.
I have a View which displays the detail of a particular Post (suggestion). On that View I have button labelled Vote which, when clicked, should create a record in my database's PostVote table linked to the Post. I just can't get this work, though! Here's what I have:
Post > Details.cshtml
Vote
PostVoteController.cs
[HttpPost]
public ActionResult VoteForPost(int id, Post post)
{
if (ModelState.IsValid)
{
db.PostVote.Add(new PostVote
{
PostID = id,
VoteTime = DateTime.Now,
VoteUser = UserHelper.GetUsername()
});
db.SaveChanges();
}
return View(post);
}
The only reason I'm requiring the post parameter in my method is because, as I understand it, unless I want to use Ajax I need to use an ActionResult and return a View, and so I've tried to copy the code in the Edit method of the PostController which was generated automatically by Visual Studio and works fine. The idea is just to reload the Post Details View after the new PostVote record has been created,
I realise I've probably written nonsense, but nothing I'm trying works. When I click the Vote button, I'm directed to a page with a URL of http://localhost:58974/PostVote/VoteForPost/1, which I sort of understand, but is obviously wrong. I can't figure out how to just pass the ID of the Post to which the new PostVote record should be linked as a variable rather than it forming part of the URL. Obviously I get a 404 error because the page doesn't exist.
Update
Ok, thanks to the comments posted I've now got this in my View:
#using (Html.BeginForm("VoteForPost", "PostVote", new { id = Model.PostID }, FormMethod.Post))
{
<button type="submit">Vote</button>
}
This is successfully creating the records as desired, but is still trying to redirect the browser to http://localhost:58974/PostVote/VoteForPost/1 afterwards. Bearing mind that the Controller (PostView.cs) doing the record creation is not the one which displayed the Post Details in the first place (that's Post.cs), how do I get my method to just return me back to the Details View I'm already looking at?

Can you have multiple url.actions within one view?

I have two controllers and two models (completely separate). I then have one view where I use an url.action to create a new record using the first controller and model. I then have another url.action to create a record for the other controller and model. My issue is that submitting one will submit the other. Is it bad practice to use a url.action to display the create view - because it works well when I only use one url.action. Or is it bad practice to have multiple. How else could this goal be achieved without having to use another view?
the view is pretty straight forward.
....
<div>
#*#Html.Action("Create", "MPost", new { TaskId = Model.TaskId, TaskTitle = Model.Title })*#
</div>
<div>
#Html.Action("Vote", "Vote", new { TaskId = Model.TaskId})
</div>
There is nothing wrong with using multiple Html.Action() calls within your Views, however you need to consider if that's the best option for you to use.
Determining What You Want To Do
Html.Action() is going to call the controller action when the View is initially rendered and it will perform whatever actions are in the View and then it may or may not return another View / Partial View.
You mentioned the following in your post :
My issue is that submitting one will submit the other. Is it bad
practice to use a url.action to display the create view - because it
works well when I only use one url.action.
This sounds like you actually want these to function as links and when they are clicked that you want to call them. If that is the case, you may be better off either creating a link that points to the appropriate action via the Url.Action() or Html.ActionLink() helpers :
<a href='Url.Action("Create", "MPost", new { TaskId = Model.TaskId, TaskTitle = Model.Title })'>Create New Post!</a>
These will perform basic GET requests to your Controller Action as you might expect. If you need to actually POST values, then you should consider using a <form> and submitting it :
<form action='#Url.Action("Vote", "Vote", new { TaskId = Model.TaskId})'>
<input type='submit' value='Cast Your Vote!' />
</form>
Generally, if you have two separate URLs that you might be posting values to, you should consider placing them each within their own <form> to avoid any confusion or possibly posting to the wrong area. Using links will work just fine as well, but keep in mind that they will redirect the user to the View that is returned from the Controller Action that you are hitting.

Display partialview based on a url

I have a partialview in _Layout.cshtml that I only want to display for certain urls.
My first thought was in my partial I would use a string as the model #model String .
In the actionmethod that is called I would return this
return PartialView("_MyPartial", new string{Request.FilePath});
In the partial I would have an if block wrapping my outer div that would check the model to see if the url it contained was the url that can display the partial.
I don't like this way because I would have to hardcode the url in if block check
#if( Model == "/Test/Home")
{
<div>
Just an example
</div>
}
What would be the best way to do this?
Thanks
You shouldn't need to use hard coded strings, even if you did the validation within your view like you initially intended.
You can use
Request.Url.AbsolutePath
to get your current url and
Url.Action("action", "controller")
to generate the inacceptable locations.
That said, I would keep your logic determining whether to show the partial view within your controller.
if(showPartialView)
return PartialView("_MyPartial");
else
return new EmptyResult();
Deciding actions based on the request is the responsibility of the Controller. Since the controller chooses the view, why not have it choose the partial as well? Figure out what, if any, partial you want in your controller, and pass it to the view on your view model.

passing the original controller and action name - mvc

I have an ascx control for fruits that contains following code:
<div id = "fruits">
....
Ajax stuff1 UpdateTargetId = "fruits"
Ajax stuff2 UpdateTargetId = "fruits"
<%Html.RenderPartial("PagerAjax", (Pager)ViewData["pager"]); %>
</div>
now this fruit control can appear on a number of webpages and the pager would need to know what page the user is on so that it can pull next set of fruits accordingly. For example, if I am on red-fruit page, then PagerAjax should know I am only pulling out red fruits. So, basically I would need to know ControllerName (assume it's home) and actionName (assume it's redFruits()). (Example may seem inefficient but it makes sense for the real purpose.) Now, I could do something like this:
<%Html.RenderAction("PagerAjax", (Pager)ViewData["pager"], ViewContext.RouteData.Values["action"], controllerFilter = ViewContext.RouteData.Values["controller"] }); %>
However, as you noticed, the above RenderAction is inside the div that is being updated by Ajax callback, which means it will contain action and controller of the Ajax stuff rather than the original URL's.
What should be an efficient workaround?
You could pass the original ViewContext.RouteData.Values["action"] as parameter when calling your AJAX action. This way the action knows what was the original action and store it in the model (or ViewData as in your case I see that your views are not strongly typed). Now your markup could become:
<% Html.RenderPartial(
"PagerAjax",
(Pager)ViewData["pager"],
new ViewDataDictionary() { { "originalAction", ViewData["originalAction"] } }
); %>

Asp.net MVC, after using jeditable (Edit in place)

Ok, i can use jeditable to edit-in-place some content on a page and the content will be saved to a database. But whats the best way to re get that text-content from db to show into a place holder?
p id="paraNo34" class="editable"
-->What i will write here so that it will get content from a
db's table: [Content], where id=="paraNo34".
/p
The problem is if i will use some hard coded text like
p id="paraNo34" class="editable"
-->Some text here
/p
I will able to edit-in-place using jeditable but when i will refresh page it will show the same "Some text here" as its not getting data from db.
Your pseudocode implies that you want the view to be responsible for fetching the required data, which is an anti-pattern in MVC. You need to retrieve the text in your controllers action and pass it to the view, either using ViewData or a custom view model, e.g.:
public ActionResult Index(string id)
{
// call some method to fetch data from db
ViewData["ID"] = id;
ViewData["Content"] = content;
return View();
}
And the view looks something like:
<p id='<%= ViewData["ID"] %>' class="editable">
<%= Html.Encode(ViewData["Content"]) %>
</p>
A better approach would be to create a strong-typed view model (Stephen Walther has a blog post about view models here), but the above example should illustrate how data can be passed from the controller to the view.

Resources