Hi
I’m following the guides below to create an MVC form in Umbraco 6.1.3. I have a SurfaceController with an action for rendering out my form, and also an HttpPost action for handling form submission. My question is in regards to showing a confirmation message in the view at the conclusion of the submission i.e. “thanks for contacting us” etc. The example uses “TempData” to set a flag but then proceeds to do a full redirect using RedirectToCurrentUmbracoPage(). Can anyone explain how the TempData is persisted across round trips and/or if this is still actually the recommended approach. I couldn't get it working this way.
the comments in http://issues.umbraco.org/issue/U4-1339 are interesting reading.
"When a form submission is successful, you redirect ..." that's definitely a new concept to me. A conventional MVC site wouldn't operate in this way would it?
Maybe the issue is that the use of RedirectToCurrentUmbracoPage() is flawed in this scenario? unless you do actually want to navigate away from the current process flow (by redirecting to the current url)...
[HttpPost]
public ActionResult HandleSearchForm(SearchFormModel model)
{
//model not valid, do not save, but return current umbraco page
if (ModelState.IsValid)
{
//do a search add results to tempdata
string examineQuery = string.Empty;
var query = new Dictionary<string, string>();
var displayDictionary = new Dictionary<string, string>();
query = CreateQuery(model, out displayDictionary);
var results = _searchService.Search(query, out examineQuery).ToList();
SaveSearch(displayDictionary, results);
//not the best way to do this see http://issues.umbraco.org/issue/U4-1339
HttpContext.Items.Add("SearchResults", results);
HttpContext.Items.Add("generatedQuery", examineQuery);
AddToSession(displayDictionary, results);
return CurrentUmbracoPage();
}
TempData.Add("SearchFormError", true);
return RedirectToCurrentUmbracoPage();
}
The session stuff I need because after a search users can email the results to themselves and that is done via contour and a workflow. The actual display of results is done by passing stuff back using HttpContext.Items ideally it should be in TempData or ViewBag.
I tried varying combinations of ViewBag/ViewData/TempData and RedirectToCurrentUmbracoPage() / CurrentUmbracoPage(), but nothing worked. I had a look through the 6.1.3 source for those two methods and although they are remapping ViewData content I couldn't see any obvious bug. I'm not sure what i'm missing here.
[ChildActionOnly]
public ActionResult ShowCommentForm()
{
var addCommentViewModel = new AddCommentViewModel();
return PartialView("Forms/AddComment", addCommentViewModel);
}
to this in my View:
@Html.Partial("Forms/AddComment", new AddCommentViewModel())
It appears that the form render Action was conflicting with how Umbraco processes my form httpPost Action i.e. ViewData was being reinitialized somewhere in the pipeline?
Edit: I would also argue that the model for my Form isn’t “Umbraco RenderModel” or a derivative of. So, given the independent model i'm using in my example, I think using an Action rather than a Partial to render the form in my template view is actually more correct.
Also, I would’ve thought the preference would be to stay in the context of the current HttpPost when processing a form action rather than navigating away at the conclusion using RedirectToCurrentUmbracoPage()?
I tried to mock up how I wanted my Action to work solely using CurrentUmbracoPage(), but noticed that the master/template view that this form Action sits inside wouldn’t reflect the content that was just added during my form action. This is the sequence I was trying to achieve:
Render my template view which contains a Partial with a “BeginUmbracoForm” form
Submit the umbraco form (i.e. HttpPost) to the SurfaceController action that the form is wired to.
The SurfaceController Action “does some stuff” (e.g. add content to CMS)
The original View is rendered again with a confirmation message that the form action is complete. (ViewBag being used to pass this ‘state’ from the action to the view..)
This template View also contains logic that lists out the content just added in the form action i.e.
I think my point/question has always been in regards to having to redirect with RedirectToCurrentUmbracoPage(). Why is this compulsory in Umbraco MVC? The PRG pattern is optional in a standard MVC website, and having to use Session definitely doesn't sound like convention. Or is it?
@Andrew.. you don't HAVE to use RedirectToCurrentUmbracoPage. But you need to do something, you can't just return a view because we're in an Umbraco context, we need to "go to a page" somehow so that the view knows about the currentpage it's running on and can do querying on it.
Also make sure to read up on "Understanding the routing process": http://our.umbraco.org/documentation/reference/mvc/forms
yep, I appreciate I'm working in an Umbraco context, and also could use a custom/hijacked route instead.
The "something" I want to do is stay on the current umbraco page:
[NotChildAction]
[HttpPost]
public ActionResult AddComment(AddCommentViewModel addCommentViewModel)
{
//model not valid, do not save, but return current umbraco page
if (!ModelState.IsValid)
return CurrentUmbracoPage();
//persistence
SaveAndPublishCatWallComment(addCommentViewModel);
ViewBag.CommentSuccess = true;
return CurrentUmbracoPage();
}
but when doing this "ViewBag.CommentSuccess" is null in the umbraco view i try to use it in.
I'll keep thinking about this further before replying again in case i'm missing something obvious :)
thanks for the help so far!
I think I've suggested TempData this a few times now.. ;-)
[NotChildAction]
[HttpPost]
public ActionResult AddComment(AddCommentViewModel addCommentViewModel)
{
//model not valid, do not save, but return current umbraco page
if (!ModelState.IsValid)
return CurrentUmbracoPage();
//persistence
SaveAndPublishCatWallComment(addCommentViewModel);
TempData.Add("CommentSuccess", true);
return CurrentUmbracoPage();
}
Edit: to confirm, yes I have tried TempData and it works as per the Umbraco examples.
yes, but by NOT navigating away i.e. using CurrentUmbracoPage instead of RedirectToCurrentUmbracoPage, then I shouldn't need to use Session (aka TempData) and therefore ViewData/ViewBag should be available in Views. Unless the umbraco pipeline is preventing this for some reason (bug).
In summary (for anyone reading back through this thread later trying to make sense of me)... :o)
I think my issue is trying to house all of the form “process flow” under a single content-managed Umbraco url. Maybe that’s the part that’s not ideal and where all the confusion/ambiguity arises i.e. Not all Routes need to fall under the guise of Umbraco content pages, if you have specific processes in your website then these can have their own dedicated Routes, that's more than acceptable. You can always use hijacked routes to relate these back to CMS template views if there’s content-managed aspects, and there inevitably always is.
Using the earlier “Comments Wall” and “Add Comment” as examples I would suggest this as a typical scenario:
A Template View that incorporates a form. (Can be included via Partial or Action, although I believe Action is semantically correct).
Eg. Url: /comments-wall
This “BeginUmbracoForm” form HttpPosts to an Umbraco Surface controller action.
The surface controller action will return CurrentUmbracoPage() until validation passes.
After validation is successful, the appropriate business logic is performed.
The final step of RedirectToAction of my defined route. This route is wired up to a system defined content page (Umbraco View) used to display a summary. In my example, a confirmation message and a list of the latest comments added.
So in the grand scheme of things you just don't want to use TempData to display stuff right ?
You can just use CurrentUmbracoPage if you don't want to redirect, like good old webforms, this just means people can resubmit your form by doing F5 then you can just use ViewData to store whatever information you are trying to pass.
Yeah it was just a personal aversion to using TempData, although im sure its not an inefficient data structure to use these days?
You can just use CurrentUmbracoPage if you don't want to redirect,
like good old webforms, this just means people can resubmit your form
by doing F5 then you can just use ViewData to store whatever
information you are trying to pass.
I mentioned above that I tried this but couldn’t get ViewData/ViewBag to work under Umbraco MVC, although that was in 6.13 and i haven't tried in 6.1.4 yet.
You should really just use TempData when redirecting, yes it uses Session but that is ok, if you move to LB environments you can just use the SQL session state provider. I've fixed up a few issues relating to this though, see:
ViewData has always worked when not redirecting so you can always use that instead and should solve all your issues. Some people forget though that if you are trying to access ViewData in a child action you need to reference it from the Parent view context... which is why you may have though that ViewData wasn't working. See the comments in that issue.
I'm going to create a nice attribute today that you can use on child actions: [MergeParentContextViewData] which will automatically merge in the parent view context's view data to the child action (but keeping all custom keys in the child action ViewData). This will automatically be applied to the macro controller that executes for partial view macros.
Umbraco 6 MVC form action advice
Hi I’m following the guides below to create an MVC form in Umbraco 6.1.3. I have a SurfaceController with an action for rendering out my form, and also an HttpPost action for handling form submission. My question is in regards to showing a confirmation message in the view at the conclusion of the submission i.e. “thanks for contacting us” etc. The example uses “TempData” to set a flag but then proceeds to do a full redirect using RedirectToCurrentUmbracoPage(). Can anyone explain how the TempData is persisted across round trips and/or if this is still actually the recommended approach. I couldn't get it working this way.
Thanks
http://umbraco.com/follow-us/blog-archive/2013/7/14/moving-from-webforms-to-mvc.aspx
http://our.umbraco.org/documentation/reference/Mvc/surface-controllers
Andrew,
I had similar issue see http://our.umbraco.org/forum/templating/templates-and-document-types/43163-Umbraco-with-mvc and also http://issues.umbraco.org/issue/U4-1339 so becuase its page redirect tempdata is empty for the time being I have put stuff in httpcontext.current.items and then reference that in the view. Not ideal but could not figure out how to get round this.
Regards
Ismail
thanks for the links, Ismail.
the comments in http://issues.umbraco.org/issue/U4-1339 are interesting reading. "When a form submission is successful, you redirect ..." that's definitely a new concept to me. A conventional MVC site wouldn't operate in this way would it?
Maybe the issue is that the use of RedirectToCurrentUmbracoPage() is flawed in this scenario? unless you do actually want to navigate away from the current process flow (by redirecting to the current url)...
Andrew,
I to would NOT want to redirect so what i have is
Regards
Ismail
thanks, Ismail. I have to say using Session doesn't appeal to me much either :p I'll try out your example tomorrow. thanks!
Andrew,
The session stuff I need because after a search users can email the results to themselves and that is done via contour and a workflow. The actual display of results is done by passing stuff back using HttpContext.Items ideally it should be in TempData or ViewBag.
Regards
Ismail
I tried varying combinations of ViewBag/ViewData/TempData and RedirectToCurrentUmbracoPage() / CurrentUmbracoPage(), but nothing worked. I had a look through the 6.1.3 source for those two methods and although they are remapping ViewData content I couldn't see any obvious bug. I'm not sure what i'm missing here.
It turns out my issue was mistakenly using an action to render my form rather than a partial. (as per this doc http://our.umbraco.org/documentation/reference/Mvc/forms/turorial-child-action)
So I changed:
to this in my View:
It appears that the form render Action was conflicting with how Umbraco processes my form httpPost Action i.e. ViewData was being reinitialized somewhere in the pipeline?
Edit: I would also argue that the model for my Form isn’t “Umbraco RenderModel” or a derivative of. So, given the independent model i'm using in my example, I think using an Action rather than a Partial to render the form in my template view is actually more correct.
Also, I would’ve thought the preference would be to stay in the context of the current HttpPost when processing a form action rather than navigating away at the conclusion using RedirectToCurrentUmbracoPage()? I tried to mock up how I wanted my Action to work solely using CurrentUmbracoPage(), but noticed that the master/template view that this form Action sits inside wouldn’t reflect the content that was just added during my form action. This is the sequence I was trying to achieve:
This template View also contains logic that lists out the content just added in the form action i.e.
Given this scenario, #5 doesn’t reflect the new content just added. (NB using a breakpoint i could see that this logic is run after the form action)
Won't Umbraco MVC work unless we follow the Post/Redirect/Get (PRG) Pattern?
After posting make sure to:
And in order to use ChildActions you need to do (if your controller is named CommentController):
So: perform the action ShowCommentForm in the Comment controller. In ShowCommentForm you would do something like this to get your comments in a list:
The Comments view can iterate through the List
Of course your CommentViewModel has other properties so you might want to consider adding LastComments as a property to your Model.
Sebastiaan,
This is still http://issues.umbraco.org/issue/U4-1339 and doing return RedirectToCurrentUmbracoPage() wont get round it?
Regards
Ismail
thanks for that info Sebastiaan, but if i use:
then I need to use TempData (aka Session) to send state/messages across round trips?!
Andrew,
You will but that data will be lost as the model is recreated which is http://issues.umbraco.org/issue/U4-1339
Regards
Ismail
@Ismail The issue you're referring to has to do with ModelState not TempData
So yes, when redirecting use TempData (as usual in MVC):
In your view (remember, you can only read TempData ONCE, so might be wise to put it in a variable):
I think my point/question has always been in regards to having to redirect with RedirectToCurrentUmbracoPage(). Why is this compulsory in Umbraco MVC? The PRG pattern is optional in a standard MVC website, and having to use Session definitely doesn't sound like convention. Or is it?
@Andrew.. you don't HAVE to use RedirectToCurrentUmbracoPage. But you need to do something, you can't just return a view because we're in an Umbraco context, we need to "go to a page" somehow so that the view knows about the currentpage it's running on and can do querying on it. Also make sure to read up on "Understanding the routing process":
http://our.umbraco.org/documentation/reference/mvc/forms
If you want to use "pure" MVC you could consider hijacking routes:
http://our.umbraco.org/documentation/reference/mvc/custom-controllers
yep, I appreciate I'm working in an Umbraco context, and also could use a custom/hijacked route instead.
The "something" I want to do is stay on the current umbraco page:
but when doing this "ViewBag.CommentSuccess" is null in the umbraco view i try to use it in.
I'll keep thinking about this further before replying again in case i'm missing something obvious :) thanks for the help so far!
I think I've suggested TempData this a few times now.. ;-)
Edit: to confirm, yes I have tried TempData and it works as per the Umbraco examples.
yes, but by NOT navigating away i.e. using CurrentUmbracoPage instead of RedirectToCurrentUmbracoPage, then I shouldn't need to use Session (aka TempData) and therefore ViewData/ViewBag should be available in Views. Unless the umbraco pipeline is preventing this for some reason (bug).
In summary (for anyone reading back through this thread later trying to make sense of me)... :o)
I think my issue is trying to house all of the form “process flow” under a single content-managed Umbraco url. Maybe that’s the part that’s not ideal and where all the confusion/ambiguity arises i.e. Not all Routes need to fall under the guise of Umbraco content pages, if you have specific processes in your website then these can have their own dedicated Routes, that's more than acceptable. You can always use hijacked routes to relate these back to CMS template views if there’s content-managed aspects, and there inevitably always is.
Using the earlier “Comments Wall” and “Add Comment” as examples I would suggest this as a typical scenario:
A Template View that incorporates a form. (Can be included via Partial or Action, although I believe Action is semantically correct).
Eg. Url: /comments-wall
The final step of RedirectToAction of my defined route. This route is wired up to a system defined content page (Umbraco View) used to display a summary. In my example, a confirmation message and a list of the latest comments added.
Eg. Url: /comments/success
This achieves these goals:
• PRG pattern as per conventional MVC application
• removes the dependency on TempData (Session)
happy days
So in the grand scheme of things you just don't want to use TempData to display stuff right ?
You can just use CurrentUmbracoPage if you don't want to redirect, like good old webforms, this just means people can resubmit your form by doing F5 then you can just use ViewData to store whatever information you are trying to pass.
Yeah it was just a personal aversion to using TempData, although im sure its not an inefficient data structure to use these days?
I mentioned above that I tried this but couldn’t get ViewData/ViewBag to work under Umbraco MVC, although that was in 6.13 and i haven't tried in 6.1.4 yet.
Hi Andrew,
You should really just use TempData when redirecting, yes it uses Session but that is ok, if you move to LB environments you can just use the SQL session state provider. I've fixed up a few issues relating to this though, see:
http://issues.umbraco.org/issue/U4-1339
ViewData has always worked when not redirecting so you can always use that instead and should solve all your issues. Some people forget though that if you are trying to access ViewData in a child action you need to reference it from the Parent view context... which is why you may have though that ViewData wasn't working. See the comments in that issue.
I'm going to create a nice attribute today that you can use on child actions: [MergeParentContextViewData] which will automatically merge in the parent view context's view data to the child action (but keeping all custom keys in the child action ViewData). This will automatically be applied to the macro controller that executes for partial view macros.
is working on a reply...