Model is null on postback in ajax loaded partial view - ajax

I'm using the following pattern https://github.com/filamentgroup/Ajax-Include-Pattern
to load partial views through ajax.
View:
#using(Html.BeginUmbracoForm("PostContactInformation", "JoiningSurface", null, new Dictionary<string, object> { { "class", "joinform" } })) {
#Html.AntiForgeryToken()
<div data-append="#Url.Action("RenderJoiningContactInformation", "JoiningSurface", new { ContentId = CurrentPage.Id })"></div>
}
With Action:
public ActionResult RenderContactInformation(int ContentId)
{
var viewModel = ContactViewModel();
viewModel.Content = Umbraco.TypedContent(ContentId);
return PartialView("RenderContactInformation", viewModel);
}
Loads partial view perfectly.
// No need to add partial view i think
Post action works correctly as well:
public ActionResult PostContactInformation(ContactViewModel model)
{
//code here
return RedirectToUmbracoPage(pageid);
}
The problem is, that i need to add model error to CurrentUmbracoPage if it exists in post...
For example:
public ActionResult PostContactInformation(ContactViewModel model)
{
ModelState.AddModelError(string.Empty, "Error occurred");
return CurrentUmbracoPage();
}
In this case i get null values for current model. And this happens only when i use ajax.
If i load action synchronously like that:
#using(Html.BeginUmbracoForm("PostJoiningContactInformation", "JoiningSurface", null, new Dictionary<string, object> { { "class", "joinform" } })) {
#Html.AntiForgeryToken()
#Html.Action("RenderContactInformation", "JoiningSurface", new { ContentId = CurrentPage.Id })
}
everything works like it should.
But i need to use ajax. Is there a correct way to pass values on postback in this case? I know that i can use TempData, but i'm not sure that this is the best approach.
Thanks for your patience

The problem is that Umbraco context is not accessible when you're trying to reach it through ajax call. Those calls are a little bit different.
Check my answer in this thread: Umbraco route definition-ajax form and I suggest to go with WebAPI and UmbracoApiControllers to be able to access those values during the Ajax call.

Related

MVC 3 How to tell what view a controller action is being called from-

Is there a way to tell what view a controller action is being called from?
For example, I would like to use "ControllerContext.HttpContext.Request.PhysicalPath" but it returns the path in which the controller action itself is located:
public ActionResult HandleCreateCustomer()
{
// Set up the customer
//..code here to setup the customer
//Check to see of the calling view is the BillingShipping view
if(ControllerContext.HttpContext.Request.PhysicalPath.Equals("~/Order/BillingShipping"))
{
//
return RedirectToAction("OrderReview", "Order", new { id = customerId });
}
else
{
return RedirectToAction("Index", "Home", new { id = customerId });
}
}
If you have a fixed number of locations that it could possibly be called from, you could create an enum where each of the values would correspond to a place where it could have been called from. You'd then just need to pass this enum value into HandleCreateCustomer, and do your condition statement(s) based on that.
At the moment I am using something of the sort:
In the View I am populating a TempData variable using:
#{TempData["ViewPath"] = #Html.ViewVirtualPath()}
The HtmlHelper method ViewVirtualPath() is found in the System.Web.Mvc.Html namespace (as usual) and is as follows and returns a string representing the View's virtual path:
public static string ViewVirtualPath(this HtmlHelper htmlHelper)
{
try{
return ((System.Web.WebPages.WebPageBase)(htmlHelper.ViewDataContainer)).VirtualPath;
}catch(Exception){
return "";
}
}
I will then obviously read the TempData variable in the controller.
I found another way.
In the controller you want to know what page it was called from.
I added the following in my controller
ViewBag.ReturnUrl = Request.UrlReferrer.AbsolutePath;
Then in the View I have a 'Back' button
#(Html.Kendo().Button().Name("ReturnButton")
.Content("Back to List").Events(e => e.Click("onReturn"))
.HtmlAttributes(new { type = "k-button" })
)
Then the javascript for the onReturn handler
function onReturn(e) {
var url = '#(ViewBag.ReturnUrl)';
window.location.href = url;
}

how to iterate ViewBag or how can I copy the values of viewBag from one Action to another Action

I have a base Controller like follow
public abstract class BaseController
{
protected ActionResult LogOn(LogOnViewModel viewModel)
{
SaveTestCookie();
var returnUrl = "";
if (HttpContext != null && HttpContext.Request != null && HttpContext.Request.UrlReferrer != null)
{
returnUrl = HttpContext.Request.UrlReferrer.LocalPath;
}
TempData["LogOnViewModel"] = viewModel;
return RedirectToAction("ProceedLogOn", new { returnUrl });
}
public ActionResult ProceedLogOn(string returnUrl)
{
if (CookiesEnabled() == false)
{
return RedirectToAction("logon", "Account", new { area = "", returnUrl, actionType, cookiesEnabled = false });
}
var viewModel = TempData["LogOnViewModel"] as LogOnViewModel;
if (viewModel == null)
{
throw new NullReferenceException("LogOnViewModel is not found in tempdata");
}
//Do something
//the problem is I missed the values which are set in the ViewBag
}
}
and another Controller
public class MyController : BaseController
{
[HttpPost]
public ActionResult LogOn(LogOnViewModel viewModel)
{
// base.LogOn is used in differnet controller so I saved some details in view bag
ViewBag.Action = "LogonFromToolbar";
ViewBag.ExtraData = "extra data related only for this action";
return base.LogOn(viewModel);
}
}
the problem is I missed the view bag values in ProceedLogOn action method.
I have the values in Logon method in BaseController.
How can I copy the values of ViewBag from one Action to another Action?
So I can not simply say this.ViewBag=ViewBag;
because ViewBag doesn't have setter. I was thinking of Iterating through viewbag.
I tried ViewBag.GetType().GetFields() and ViewBag.GetType().GetProperties() but they return nothing.
ViewData reflects ViewBag
You can iterate the values you've stored like this :
ViewBag.Message = "Welcome to ASP.NET MVC!";
ViewBag.Answer = 42;
foreach (KeyValuePair<string, object> item in ViewData)
{
// if (item.Key = "Answer") ...
}
This link should also be useful
I'm afraid I don't have the answer how to copy ViewBag.
However, I would never use ViewBag that way.
ViewBag is some data the Controller gives to the View to render output if someone does not like to use ViewModel for some reasons. The View should never know anything about the Controller but your ViewBag is holding a ActionName ;).
Anyway, the ProceedLogOn action method has pretty much parameters which is ... not a nice code actually so why hesitate to add more parameters which are currently being hold in MyController.Logon ViewBag? Then inside method ProceedLogOn you have what you want.
;)

Partial View Validation Without JavaScript

I have a partial view in which there is a form. I POST this form using the PRG pattern. I am using the AjaxHelper to create my form. I also need this form to work without javascript. The problem is that when model validation fails, it always changes the url to my partial view.
public ActionResult PostForm(PostFormModel postFormModel)
{
if (ModelState.IsValid)
{
return RedirectToAction("SomewhereElse");
}
else
{
if (Request.IsAjaxRequest())
{
return PartialView("_PostForm")
}
else
{
// What do I do here?
}
}
}
Here's what I have tried:
return PartialView("_PostForm", postFormModel);
This just renders the partial view and doesn't contain any of the parent stuff.
return View("Index", new ParentModel() { PostFormModel = postFormModel });
This actually produces the correct result. It displays the parent view, but the URL is that of the partial http://localhost:22485/Controller/PostForm! I feel like this is really close to the solution. What now?
If you want to change url, you should redirect to another action (using PRG pattern). Insert next code instead of '// What do I do here?':
postModelService.Save(postFormModel); //to Session or to DB
return RedirectToAction("Parent");
New action should look like this:
public ActionResult Parent()
{
var postFormModel = postModelService.Load();
return View("Index", new ParentModel() { PostFormModel = postFormModel });
}
Hope it helps.

Partial ViewModels, Controllers, in Razor

Trying to get my ducks in a row with MVC3 + Razor!
I finally understand the concept of a 'View-Model' to wrap my entity classes and tailor them to a View.
Now I'm assembling a page with partial views representing different elements necessary to the page (such as drop down lists, forms, etc.) each of these will be represented by a 'View-Model' that maps to an entity class and back to my database.
First I am trying to create a partial view representing a component that is a drop-down list of elements in the database, that when selected will render another partial view, etc.
I just can't put together why I can't generate this drop-down list, and once I do how the main 'controller' maps all this together?
In short I'm curious - does each partial view need a controller even if it's based on a strongly typed model?
Breaking it down:
My Entity Model-View Wrapper (getting all the elements available from the database
*Updated* - to a working example now, note I don't think I was asking the right question before, but this will give you an idea of what I was trying to accomplish! Next step is to move all these operations 'off' the controller (and populate them in the models default constructor, for ease of use later).
CharactersListViewModel.cs
Going to move avoid the 'View Model' for now until I get a little more comfortable
Creating a partial view that displays a drop down list with the Characters' ID as a value, and name as the text, create strongly-typed view, partial view
controller for main-page in section:
HistoryController.cs
public class HistoryController : Controller
{
public ActionResult Index()
{
var list = new List<SelectListItem>();
using (var _database = new fff_newEntities())
{
foreach(Character c in (from c in _database.Characters select c)){
list.Add(new SelectListItem(){Text = c.CharacterName, Value = c.Id.ToString()});
}
}
ViewBag.Clients = list;
}
}
//
// GET: /History/Details/5
public ActionResult Details(int id)
{
return View();
}
//
// GET: /History/Create
public ActionResult Create()
{
return View();
}
//
// POST: /History/Create
[HttpPost]
public ActionResult Create(FormCollection collection)
{
try
{
// TODO: Add insert logic here
return RedirectToAction("Index");
}
catch
{
return View();
}
}
//
// GET: /History/Edit/5
public ActionResult Edit(int id)
{
return View();
}
//
// POST: /History/Edit/5
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
try
{
// TODO: Add update logic here
return RedirectToAction("Index");
}
catch
{
return View();
}
}
public ActionResult Delete(int id)
{
return View();
}
//
// POST: /History/Delete/5
[HttpPost]
public ActionResult Delete(int id, FormCollection collection)
{
try
{
// TODO: Add delete logic here
return RedirectToAction("Index");
}
catch
{
return View();
}
}
The index to display the whole page including the partial component (my drop down list)
index.cshtml:
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#Html.DropDownListFor(x => x.CurrentCharacterId, (IEnumerable<SelectListItem>)ViewBag.Clients);
On the last line here, #Html.Action(...) where do I actually create the drop-down list?
Sorry if this seems trivial but I can't wrap my head around it and I really want to learn MVC3 + Razor correctly!
A partial view is meant to abstract out some HTML/View Logic so that it can be re-used either in multiple places or for repeating (looping).
Though you can have an action that maps to the partial and if the partial in question does some explicit data access this might be the way to go but if you're just passing down all the data it needs from the controller itself then - no, you don't need a Controller/Action for it.
Since you're doing some explicit data access I would probably make an action for it...
[ChildActionOnly]
public ActionResult Characters()
{
using (var _database = new entities())
{
CharactersViewModel viewModel = new CharactersViewModel();
viewModel.Characters = _database.Characters.ToDictionary(c => c.Id, c => c.CharacterName);
return PartialView(viewModel);
}
}
In your view...
#Html.Action("Characters")
Of course there's nothing wrong with the way you're doing it but I find having it map to an action can make things easier down the road if you ever wanted to retrieve the HTML from this rendered partial view via an ajax request or something of the sort.
Notes:
Try to wrap your entity context object in a using so it can dispose of the connection.
You can use ToDictionary to select your dictionary directly from the query scope.

Return an other action result as string

In my MVC website, I am creating a small forum. For a single post I am rendering my "Single(Post post)" action in my "PostController" like below
<% Html.RenderAction<PostController>(p => p.Single(comment)); %>
Also When a user reply a post I am sending reply as an ajax request to my "CreatePost" action then return "Single" view as result of this action like below
public ActionResult CreatePostForForum(Post post)
{
//Saving post to DB
return View("Single", postViewData);
}
When I do like that only the view is being rendered, Codes in "Single" Actions body isn't beig executed.
What is the best way to do this?
Also I want to return "Single" action result as string in my JsonObject like below
return Json(new{IsSuccess = true; Content= /*HERE I NEED Single actions result*/});
You can use something like this, but be very careful with this. It can actually cause badly traceable errors (for example when you forget to explicitly set view name in Single method).
public ActionResult Single(PostModel model) {
// it is important to explicitly define which view we should use
return View("Single", model);
}
public ActionResult Create(PostModel model) {
// .. save to database ..
return Single(model);
}
Cleaner solution would be to do the same as if it was post from standard form - redirect (XMLHttpRequest will follow it)
For returning ajax views wrapped in json I use following class
public class AjaxViewResult : ViewResult
{
public AjaxViewResult()
{
}
public override void ExecuteResult(ControllerContext context)
{
if (!context.HttpContext.Request.IsAjaxRequest())
{
base.ExecuteResult(context);
return;
}
var response = context.HttpContext.Response;
response.ContentType = "application/json";
using (var writer = new StringWriter())
{
var oldWriter = response.Output;
response.Output = writer;
try
{
base.ExecuteResult(context);
}
finally
{
response.Output = oldWriter;
}
JavaScriptSerializer serializer = new JavaScriptSerializer();
response.Write(serializer.Serialize(new
{
action = "replace",
html = writer.ToString()
}));
}
}
}
It is probably not the best solution, but it works quite well. Note that you will need to manually set View, ViewData.Model, ViewData, MasterName and TempData properties.
My recommendation:
Post your forum reply (and whatever options) via Ajax.
Return your JSONResult, using this method: ASP MVC View Content as JSON to render your content.
In the OnSuccess handler of your ajax call, check if IsSuccess is true. If successful, append the content to the appropriate container using JQuery

Resources