This action simply defines which stage of the booking process the user is at an performs some of the more complex validation that cannot be handled by the model.
The very first time it is called, I need to check a JSON cache stored as a property for my Umbraco page to see if the page (a tour) has any tour dates available in the future.
if (model.Stage == 0)
{
List<DateTime> instanceDates = new List<DateTime>();
string instances = model.Content.GetProperty("tripInstances").Value.ToString();
string jsonInstances = instances.Trim('+');
List<JSONTourInstance> tourInstances = JsonConvert.DeserializeObject<List<JSONTourInstance>>(jsonInstances);
foreach (JSONTourInstance item in tourInstances)
{
DateTime date = new DateTime();
if (DateTime.TryParse(item.Date, out date))
{
if (date >= DateTime.Now)
{
instanceDates.Add(date);
}
}
}
if (instanceDates.Count() == 0)
{
BookingEnquiryModel bookingmodel = new BookingEnquiryModel();
return View(bookingmodel);
}
}
If there are not dates available in the future then we don't really want to show the booking process and instead want to show an enquiry form so that the user can ask about any tour dates in the distant future. This is where my main issue occurs. How do I successfully redirect to another controller that will be handling the Enquiry form and have this display on my page instead of the booking view that is generated by the orginal call to action?
I have tried multiple methods. As you can see above, I have tried returning a view with the Enquiry model passed in instead. This simply produces an error as the original call to action is expecting a BookingModel.
I have also tired a RedirectToAction("ShowEnquiryForm", "BookingEnquiry") but this does nothing at all.
There must be some way of calling one controller, realizing not all of the prerequisites to go down that route are met and instead going down another route with another controller but how is this done?
I am still relatively new to .Net and MVC in general and haven't done much MVC outside of Umbraco however, general MVC doesn't seem to be of any help here as Umbraco seems to break the rules a bit to acheive what it needs to do.
Any help or pointers here would be greatly appreciated.
Since you're rendering the result of the action using @Html.Action(), a redirect will do nothing. The result is just embedded in the template currently shown by Umbraco.
But View() has an overload where you can specify the view to render. For instance return View("~/Views/Partials/NewForm.cshtml", emptyModel).
There are other options too, but I think this will solve your immediate needs, no?
Thanks for your response. So would I be correct in thinking I need to replace my @Html.Action() in my initial template view and instead directly reference the view and model I need? Or are you saying that I should be returning View() from my controller as I have done in my example above but pass in the view reference and model as you have defined in your answer?
I would use client-side validation like jQuery validate or just plain HTML 5 validation to prevent the user from getting as far.
Otherwise, you can put a variable, or even the invalid model in ViewData and use use to check whether you should show the form again. Google found this blog entry from Markus Johansson that might shed some light. :)
As Lars says a redirect won't do anything from a child action.
You can simply
return AlternativeActionName();
From a controller action and pass it any relevant parameters.
The only thing to be careful with is the view name which will get rendered. From what I remember it is done from Controllers ViewContext which will have been set on your initial action.
You may need to specify a specific view name explicitly from your actions. eg
return View("ViewNameHere", bookingmodel);
I may be wrong on the view name part - I just have some memory having to do this before!
Are you saying to call this from a Controller? If so, this does not work. As I mentioned above in my code example I have already tried this method and it errors out.
Thanks for your comments. The problem is if I call another form in any of my views, I cannot validate it because when I use:
return CurrentUmbracoPage()
when a model is invalid to allow the user to go back and change their response, they are redirected back to the original action rather than seeing the form they have just filled in.
On submission of the form all other variables should be the same as on page load and so the controllershould return the view with the correct form for which validation errors can be shown.
Are you still looking for a solution? If not then I may have just not got your problem right in my head :)
Still looking for a solution. Basically, I show my booking form to start with:
@Html.Action("ShowBookingForm", "Booking")
This is a multi-stage booking process so it basically involves multiple forms being generated from the same action. To start with the user is shown a calendar input which when clicked populates a drop-down with the times a trip is available on the day. Selecting a time and clicking submit sends a web service request to our software on the server which returns whether or not there is any availability. If there is no availability for the particular tour, we need to display the enquiry form. Otherwise we go to stage two of the booking process.
This is where I encounter my problem. Using the methods you describe above, I can display the enquiry form. However, if the user submits an invalid enquiry form, they are routed back to the first stage of the booking process rather than seeing the enquiry form they have just completed with the options they selected.
As I mentioned above in my original post, that is what I used although that does not go into enough depth with regards to routing to alternative controllers. Unfortunately Umbraco doesn't seem to comply with a lot of the standard MVC methods and this is why workarounds such as those detailed in Sebaastians post exist.
As long as you pass the model, you can add whatever information you need to it. For instance which partial view to include, which values should be shown to the user etc.
Controller pseudo-code assuming controller from tutorial:
// set model.StepIndex, then...
var views = new[]{"Step1", "Step2", "Summary"};
model.CurrentView = views[model.StepIndex];
model.CurrentValues = new { FirstName=submittedModel.FirstName };
Don't you have a class representing the model?
The first step in the tutorial you are following is to create a model class.
You are in charge of that class, and can do whatever you want with it.
By CurrentValues, I mean the values you are sending back and forth between view and controller. You should add any relevant property, such as "FirstName".
If this is all new to you, I suggest going through a few MVC tutorials as such, and then come back to Umbraco.
This displays the booking form initially if there are tours available otherwise it displays the enquiry form.
Following the availability check in the booking form, in my booking controller I have the following (currently it will always fire due to the true == true statement):
if (model.Stage == 1)
{
EnquiryModel enquiry = new EnquiryModel();
if (true == true)
{
TempData["ShowEnquiryForm"] = true;
return View("~/Views/Partials/AvailabilityForm.cshtml", enquiry);
}
GroupTourAvailabilityData[] availabilityData = null;
Dictionary<int, GroupTourAvailabilityData[]> availability = CheckSessionAvailability(model);
availability.TryGetValue(model.Tour_Instance.Instance_Id.Value, out availabilityData);
if (availabilityData.Where(x => x.Availability > 0).Count() > 0)
{
// Do nothing
}
else
{
TempData["ShowEnquiryForm"] = true;
return View("~/Views/Partials/AvailabilityForm.cshtml", enquiry);
}
}
This then proceeds with the booking process if there is availability otherwise it sets the tempdata value of showenquiryform so that on page load, the alternative controller for the enquiry form is displayed instead.
This is the managed by the following controller actions:
public class EnquiryController : SurfaceController
{
[ChildActionOnly]
public ActionResult ShowEnquiryForm(EnquiryModel model)
{
model = model ?? new EnquiryModel();
return View(model);
}
[HttpPost]
public ActionResult Submit(EnquiryModel model)
{
if (!ModelState.IsValid)
{
TempData["ShowEnquiryForm"] = true;
return CurrentUmbracoPage();
}
TempData["message"] = "Your enquiry has been received successfully";
return RedirectToCurrentUmbracoPage();
}
}
Because again set the tempdata value of ShowEnquiryForm on form failure, when the page reloads, the enquiry form with their previous entries is displayed. Upon successful submission, a partial in my initial view looks for a tempdata value of message and displays a model to notify them their enquiry has been received whilst displaying the booking selection wizard.
This may be a roundabout way of doing it but i from over a year now of working with Umbraco and .Net MVC this seems like the only viable solution for this issue.
Redirect to another Controller, Model and View during Controller processing
Hi all,
I am currently in the process of developing a very complex, multi-stage booking engine for one of our client sites which we built using Umbraco v7. The booking engine itself is a variation of the tutorial posted here http://umbraco.com/follow-us/blog-archive/2015/2/13/creating-multi-step-forms-using-a-surfacecontroller/ by Sebastiaan Janssen. It's a great tutorial if you haven't seen it already.
In my template, I call the relevant action on my controller as follows:
This action simply defines which stage of the booking process the user is at an performs some of the more complex validation that cannot be handled by the model.
The very first time it is called, I need to check a JSON cache stored as a property for my Umbraco page to see if the page (a tour) has any tour dates available in the future.
If there are not dates available in the future then we don't really want to show the booking process and instead want to show an enquiry form so that the user can ask about any tour dates in the distant future. This is where my main issue occurs. How do I successfully redirect to another controller that will be handling the Enquiry form and have this display on my page instead of the booking view that is generated by the orginal call to action?
I have tried multiple methods. As you can see above, I have tried returning a view with the Enquiry model passed in instead. This simply produces an error as the original call to action is expecting a BookingModel.
I have also tired a RedirectToAction("ShowEnquiryForm", "BookingEnquiry") but this does nothing at all.
There must be some way of calling one controller, realizing not all of the prerequisites to go down that route are met and instead going down another route with another controller but how is this done?
I am still relatively new to .Net and MVC in general and haven't done much MVC outside of Umbraco however, general MVC doesn't seem to be of any help here as Umbraco seems to break the rules a bit to acheive what it needs to do.
Any help or pointers here would be greatly appreciated.
J
Since you're rendering the result of the action using
@Html.Action()
, a redirect will do nothing. The result is just embedded in the template currently shown by Umbraco.But
View()
has an overload where you can specify the view to render. For instancereturn View("~/Views/Partials/NewForm.cshtml", emptyModel)
.There are other options too, but I think this will solve your immediate needs, no?
Hi Lars,
Thanks for your response. So would I be correct in thinking I need to replace my @Html.Action() in my initial template view and instead directly reference the view and model I need? Or are you saying that I should be returning View() from my controller as I have done in my example above but pass in the view reference and model as you have defined in your answer?
Cheers,
Jason
Give returning a specific view from the controller a go.
Carl's examples are also good alternatives.
Great. That works a treat. Thanks #h5yr
I've just found one big error with this solution. If you use something like this:
and redirect to a partial view that contains this:
When it comes to your controller, if you use
Then it will return the original page and model that was called using:
Rather than returning to the enquiry form to allow the user to correct their mistakes. Anyone know how to get around this?
I would use client-side validation like jQuery validate or just plain HTML 5 validation to prevent the user from getting as far.
Otherwise, you can put a variable, or even the invalid model in ViewData and use use to check whether you should show the form again. Google found this blog entry from Markus Johansson that might shed some light. :)
Hi Lars,
Using Javascript only validation is a very bad idea especially when it comes to validating models so I am afraid this isn't an option.
Cheers,
Jason
As Lars says a redirect won't do anything from a child action.
You can simply
From a controller action and pass it any relevant parameters.
The only thing to be careful with is the view name which will get rendered. From what I remember it is done from Controllers ViewContext which will have been set on your initial action.
You may need to specify a specific view name explicitly from your actions. eg
I may be wrong on the view name part - I just have some memory having to do this before!
Thanks
Carl
Just realised you need to call another controller.
I'd recommend adding adding a "checker" controller and action which returns a view with :
That way you can seperate the logic of checking and doing
Thanks
Carl
Hi Carl,
Are you saying to call this from a Controller? If so, this does not work. As I mentioned above in my code example I have already tried this method and it errors out.
Cheers,
Jason
Hi Jason.
no - You need to have one controller which will return something like :
With the view I gave above, Which will then uses the true/false to determine which controller / action to call next.
Thanks
Carl
Hi Carl,
Thanks for your comments. The problem is if I call another form in any of my views, I cannot validate it because when I use:
when a model is invalid to allow the user to go back and change their response, they are redirected back to the original action rather than seeing the form they have just filled in.
Any ideas?
Not sure I follow.
On submission of the form all other variables should be the same as on page load and so the controllershould return the view with the correct form for which validation errors can be shown.
Are you still looking for a solution? If not then I may have just not got your problem right in my head :)
Thanks
Carl
Hi Carl,
Still looking for a solution. Basically, I show my booking form to start with:
This is a multi-stage booking process so it basically involves multiple forms being generated from the same action. To start with the user is shown a calendar input which when clicked populates a drop-down with the times a trip is available on the day. Selecting a time and clicking submit sends a web service request to our software on the server which returns whether or not there is any availability. If there is no availability for the particular tour, we need to display the enquiry form. Otherwise we go to stage two of the booking process.
This is where I encounter my problem. Using the methods you describe above, I can display the enquiry form. However, if the user submits an invalid enquiry form, they are routed back to the first stage of the booking process rather than seeing the enquiry form they have just completed with the options they selected.
I hope this sheds some light on my issue.
Cheers,
Jason
Hi Jason.
I see....
With my solution you would need to role that initial action into my "checker" controller and view.
That way you can determine if you need to show:
you'd need to store the current state somewhere as the user works through things.
I'd recommend storing it on the user session personally.
the view would need to take an int (or enum to be neat) so for example...
Thanks
Sorry you're still struggling, Jason.
Maybe this tutorial on the Umbraco blog will help?
Hi Lars,
As I mentioned above in my original post, that is what I used although that does not go into enough depth with regards to routing to alternative controllers. Unfortunately Umbraco doesn't seem to comply with a lot of the standard MVC methods and this is why workarounds such as those detailed in Sebaastians post exist.
Cheers,
Jason
As long as you pass the model, you can add whatever information you need to it. For instance which partial view to include, which values should be shown to the user etc.
Controller pseudo-code assuming controller from tutorial:
View pseudo-code replacing tutorial switch:
Hi Lars,
I don't know where you are getting this information from though as CurrentView and CurrentValues are not valid model attributes.
Cheers,
Jason
Don't you have a class representing the model?
The first step in the tutorial you are following is to create a model class.
You are in charge of that class, and can do whatever you want with it.
By CurrentValues, I mean the values you are sending back and forth between view and controller. You should add any relevant property, such as "FirstName".
If this is all new to you, I suggest going through a few MVC tutorials as such, and then come back to Umbraco.
Unless you could be persuaded to spend a few bucks on the Umbraco Forms package. It really really simplifies stuff like this. :)
http://umbraco.com/products-and-support/forms/
I have ended up solving this issue as follows as none of the other suggestions seem to work:
In my template I have the following:
This displays the booking form initially if there are tours available otherwise it displays the enquiry form.
Following the availability check in the booking form, in my booking controller I have the following (currently it will always fire due to the true == true statement):
This then proceeds with the booking process if there is availability otherwise it sets the tempdata value of showenquiryform so that on page load, the alternative controller for the enquiry form is displayed instead.
This is the managed by the following controller actions:
Because again set the tempdata value of ShowEnquiryForm on form failure, when the page reloads, the enquiry form with their previous entries is displayed. Upon successful submission, a partial in my initial view looks for a tempdata value of message and displays a model to notify them their enquiry has been received whilst displaying the booking selection wizard.
This may be a roundabout way of doing it but i from over a year now of working with Umbraco and .Net MVC this seems like the only viable solution for this issue.
Cheers,
Jason
is working on a reply...