Template editor not applied when using Ajax for partial view - asp.net-mvc

I have a template editor, Currency.cshtml like this:
#model decimal?
...
string value =
// Some calculations that returns value formatted as currency
Currency: #value<br/>
#Html.TextBox("", value, attributes)
I have a view which uses this template like this:
#Html.EditorFor(m => m.Amount.Value, "Currency", someAdditionalViewData)
When this view is rendered as a partial directly inside another view, the result is as expected: both the text, and the editor show the formatted variable "value", like so:
Currency: 1.223,18 <== well formatted
[ 1.223,18] <== this is the input type=text, well formatted
However, if I get the view using Ajax (Ajax.ActionLink), I get the firs part formatted, but the second one unformatted, like so:
Currency: 1.223,18 <== well formatted
[ 1223.18] <== this is the input type=text, not formatted!!
Any idea why this can be going on? Should I change the final #Html.TextBox("", value, attributes) in my template for something else?

I don't know the reason, but after investigating for some time, I can assure that the final call to #Html.TextBox("", value, attributes), on an Ajax request behaves in a different way than on a PartialView render.
In a PartialView render, it creates the desired input element, with the provided formatted value. However, in an ajax request, it looks up for info on the model and creates its own version of the textbox, including the unobstrusive validation attributes and so on.
The only way to solve this problem is not to use the TextBox method, but create an input writing it directly with a TagBuilder. Pay attention to the way of getting the Id and name for the input element.
#{
string value = ... // Format as desired
// Create the required attributes
// NOTE: HtmlAttributes is simply a custom "fluent" attribute builder,
// inherited from IDictionay<string,object>
var attributes = HtmlAttributes.Empty
.Add("type", "text")
.Add("value", value) // formatted value
.Add("id", ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(""))
.Add("name", ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(""))
.AddStyle("text-align", "right");
// You can add extra attributes, i.e. unobtrusive validation, provided by user...
var extraAttibutes = ...
attributes.Merge(extraAttributes);
// Use the tag builder to create the input
TagBuilder tb = new TagBuilder("input");
foreach (var attribute in attributes)
{
tb.Attributes.Add(attribute.Key, attribute.Value.ToString());
}
var textBox = tb.ToString(TagRenderMode.SelfClosing);
// Then, either write it directly to the writer...
ViewContext.Writer.Write(textBox);
}
#* ...or use Html.Raw *#
#Html.Raw(textBox)
Of course, there are a lot of details omitted (how to get unobtrusive or any other extra attributes for the tag and so on). The best soultion would be to discover why the behavior changes from a "complete" render and an ajax request render. All the View metadata looks exactly the same, but works in a different way.

I also had a partialview that used an editortemplate which worked when initially loading the page. However when using ajax to call for the partial the editortemplate wasn't used.
For me turned out I had to move it to a different folder:
From
Views/[Controllername]/EditorTemplates
To
Views/Shared/EditorTemplates

Related

Why does the view get two different Model.Id? [duplicate]

This is a follow on from the following question:
MVC 3 + $.ajax - response seems to be caching output from partial view
There is a detailed description of the problem over there. However, I have now managed to narrow down the problem, that seems to be with the Html.EditorFor helpers, hence the new question.
The issue:
I post data to the server using $.ajax, then return the html of the partial view that holds the input controls. The problem is that, despite passing a newly created object to the Partial Views model, the various #Html.EditorFor and #Html.DropDownListFor helpers return the OLD DATA!.
I can prove that the model has correctly passed in a new object to the helpers, by printing the value out beside the Html helper. Ie:
#Html.EditorFor(model => model.Transaction.TransactionDate)
#Model.Transaction.TransactionDate.ToString()
As the following image shows, the #Html.EditorFor is returning the wrong data:
[Note that the value beside the Comentario text box is a date time, because I was testing replacing the default values with a value that would change with each post, ie, a DateTime.]
If I replace the #Html.EditorFor for TransactionDate with a plain old #Html.TextBox():
#Html.TextBox("Transaction_TransactionDate", Model.Transaction.TransactionDate)
Then it renders the correct TransactionDate value for a new Transaction object, ie, DateTime.MinValue (01/01/0001...).
Therefore...
The problem is with the #Html.EditorFor helpers. The problem also happens with TextBoxFor and DropDownListFor.
The problem being that these helpers seem to cache the old value.
What am I doing wrong??!
EDIT:
I have just tried debugging in the custom Editor template for dates, and in there, ViewData.TemplateInfo.FormattedModelValue shows the correct value, ie, "01/01/0001". However, once it gets to Fiddler, the response is showing the old date, eg, "01/09/2011" in the image above.
As a result, I just think that there is some caching going on here, but I have none set up, so nothing makes any sense.
There is no caching involved here. It's just how HTML helper work. They first look at the ModelState when binding their values and then in the model. So if you intend to modify any of the POSTed values inside your controller action make sure you remove them from the model state first:
[HttpPost]
public virtual ActionResult AjaxCreate(Transaction transaction)
{
if (ModelState.IsValid)
{
service.InsertOrUpdate(transaction);
service.Save();
}
service.ChosenCostCentreId = transaction.IdCostCentre;
TransactionViewModel viewModel = new TransactionViewModel();
ModelState.Remove("Transaction");
viewModel.Transaction = new Transaction();
ModelState.Remove("CostCentre");
viewModel.CostCentre = service.ChosenCostCentre;
...
return PartialView("_Create", viewModel);
}
Even if you do not specify caching, it sometimes can occur. For my controllers which handle AJAX and JSON requests, I decorate them as follows:
[OutputCache(Location = OutputCacheLocation.None, NoStore = true)]
This specifically declares no caching should occur.
UPDATE
Based on an answer Darin Dimitrov gave here, try adding the following line to your controller action:
ModelState.Clear();
i have never seen this but basically if you are using ajax to request this data, you need to set nochache: i am assuming you using jQuery.ajax here so will show the code:
$.ajax({
url: "somecontroller/someAction,
cache: false, // this is key to make sure JQUERY does not cache your request
success: function( data ) {
alert( data );
}
});
just a stab in the dark, i assume you have probably already covered this already. have you tried to create a new model first and then populate that new instance of the model with your data, and then send this to your view!
Finally not sure what DB server your using but have you check to see that DB results are not cached and that you are not just requesting SQL results from the DB cache... i dont use MsSQL but i hear that it has outputCaching until something is change on the DB server itself?? anyway just a few thoughts
This was unexpected behavior for me, and although I understand the reason why it's necessary to give ModelState precedence, I needed a way to remove that entry so that the value from Model is used instead.
Here are a couple methods I came up with to assist with this. The RemoveStateFor method will take a ModelStateDictionary, a Model, and an expression for the desired property, and remove it.
HiddenForModel can be used in your View to create a hidden input field using only the value from the Model, by first removing its ModelState entry. (This could easily be expanded for the other helper extension methods).
/// <summary>
/// Returns a hidden input field for the specified property. The corresponding value will first be removed from
/// the ModelState to ensure that the current Model value is shown.
/// </summary>
public static MvcHtmlString HiddenForModel<TModel, TProperty>(this HtmlHelper<TModel> helper,
Expression<Func<TModel, TProperty>> expression)
{
RemoveStateFor(helper.ViewData.ModelState, helper.ViewData.Model, expression);
return helper.HiddenFor(expression);
}
/// <summary>
/// Removes the ModelState entry corresponding to the specified property on the model. Call this when changing
/// Model values on the server after a postback, to prevent ModelState entries from taking precedence.
/// </summary>
public static void RemoveStateFor<TModel, TProperty>(this ModelStateDictionary modelState, TModel model,
Expression<Func<TModel, TProperty>> expression)
{
var key = ExpressionHelper.GetExpressionText(expression);
modelState.Remove(key);
}
Call from a controller like this:
ModelState.RemoveStateFor(model, m => m.MySubProperty.MySubValue);
or from a view like this:
#Html.HiddenForModel(m => m.MySubProperty.MySubValue)
It uses System.Web.Mvc.ExpressionHelper to get the name of the ModelState property. This is especially useful when you have "Nested" models since the key name isn't obvious.
Make sure you're not doing this:
#Html.EditorFor(model => model.Transaction.TransactionDate.Date)
I did this, and the model never got the value back. It worked perfectly once I remove the .Date.

MVC4 Razor - #Html.DisplayFor not binding to model

I am trying to find me feet with MVC4 Razor and I'm stuck with this simple problem.
When I use #Html.DisplayFor the model is always sent back as NULL, but when I use #Html.TextBoxFor this model is fully populated, what am I missing?
Thanks in advance
This is a common issue that many people miss in the asp.net mvc framework. Not just the difference in the helpers such as HiddenFor, DisplayFor, TextBoxFor - but how exactly the framework sets up automatically collecting and validating these inputs. The magic is all done with HTML5's data-* attributes. You will notice when looking at the input tag generated that there are going to be some extra properties in the form of data-val, data-val-required, and perhaps some additional data properties for types, for example numerics would be data-val-number.
These data attributes allow the jQuery extension jquery.validate.unobtrusive.js to parse the DOM and then decide which fields to validate or generate error messages.
The actual collection of posted data is reflected from the name property. This is what should map up to the model that is in the c# or vb [HttpPost] method.
Use HiddenFor when you want to provide posted data that the user does not need to be aware of.
Use DisplayFor when you want to show records but not allow them to be editted.
Use TextBoxFor when you want to allow user input or allow the user to edit a field.
EDIT
"the purpose of this view is to enable the user to view the data before submitting it to the database. Any ideas how I can achieve this?"
You could accomplish this with a duo of HiddenFor and DisplayFor. Use HiddenFor to have the values ready to be posted, and DisplayFor to show those values.
DisplayFor will not do the Model binding. TextBoxFor will do because it creates a input element in the form and the form can handle it when it is being posted. If you want to get some data in the HttpPost action and you dont want to use the TextBoxFor, you can keep that pirticulare model proeprty in a hidden variable inside the form using the HiddenFor HTML helper method like this.
#using(Html.BeginForm())
{
<p>The Type Name is</p> #Html.DisplayFor(x=>x.TypeName)
#Html.HiddenFor(x=>x.TypeName)
<input type="submit" value="Save" />
}
Use both DisplayFor and HiddenFor. DisplayFor simply displays the text and is not an input field, thus, it is not posted back. HiddenFor actually creates <input type="hidden" value="xxxx"/>
DisplayFor builds out a HTML label, not an input. Labels are not POSTed to the server, but inputs are.
I know this is a bit of an old question but you can roll your own, custom combined display control as shown below. This renders the model value followed by a hidden field for that value
#Html.DisplayExFor(model => Model.ItemCode)
Simply use what the framework already has in place
public static MvcHtmlString DisplayExFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> ex)
{
var sb = new StringBuilder();
sb.Append(htmlHelper.DisplayFor(ex));
sb.Append(htmlHelper.HiddenFor(ex));
return MvcHtmlString.Create(sb.ToString());
}
Do you mean during a form post? If you use DisplayFor, this creates a element which does not contain any form values. Typically you use these in conjunction with each other to create a label for your textbox, then using the Html.TextBoxFor to allow users to modify the data element.
Example:
#Html.DisplayFor(x=>x.Item)
#Html.TextBoxFor(x=>x.Item)
Will Render
Item <a text input field following>
Or in HTML
<label for="Item">Item</label><input type="Text" id="Item" name="Item"/>

Html.DisplayFor and Html.EditorFor displays different values

I'm completely stumped.
I'm doing a searchform in MVC2 (I've done dozen others on this project, all working fine.)
Global.asax has this route:
routes.MapRoute("OnlineHelpSearchIndex",
"Help/Search/{expression}/{page}",
new { controller = "OnlineHelp", action = "Search", expression = UrlParameter.Optional, page=1 });
The expression is a base64 encoded string. I decode it in controller, pass it to a model which has a property named Expression, and display it in a PartialView in a TextBox. (Then when the user clicks a link or presses enter, I encode the string in javascript and send it to "/Help/Search/"+value)
I have several searchboxes built this way (each with a route SomeModule/Search/{expression}), and one of them is not working.
<%:Html.DisplayFor(m => m.Expression)%>
<%: Model.Expression %>
<%:Html.TextAreaFor(m => m.Expression)%>
<%:Html.TextBoxFor(m => m.Expression)%>
<%:Html.EditorFor(m => m.Expression)%>
The first two display the correct expression, the other three displays the expression in the url.
I tried hardcoding a string into the model, the first two displayed the hardcoded string, the other three displayed whatever was in the url. How is it possible?
(I even tried with JS disabled, so it is a server side issue)
I know this is an old thread, but I figured I would answer it anyway. The reason this is happening is intentional, it is due to ModelState. See this question for another case:
Asp.net MVC ModelState.Clear
Long story short, you're POSTing form data to a controller and returning a View, and using Helpers. Therefore, MVC assumes this is a failure on validation and is returning the ModelState value, not the value of your Model data. The first two are displaying correctly because they are not editors, the other 3 are editors, so they're showing ModelState.
Either call ModelState.Clear() in the controller to blow it away, or implement another design pattern, such as POST, Redirect, GET.
Try to change the name of the expression parameter in both the routes.MapRoute and in your OnlineHelp/Search controller/action method:
routes.MapRoute("OnlineHelpSearchIndex",
"Help/Search/{exprs}/{page}",
new { controller = "OnlineHelp", action = "Search", exprs = UrlParameter.Optional, page=1 });
(or, if you prefer, you can change the name of the Expression property of your model).
This often happens working with form fields created by HtmlHelper methods such as TextBoxFor/EditorFor, when the ViewModel has one or more properties that share the same name of a Router/Controller parameter: you can easily check this looking at the generated HTML code, your input-type fields created by the HtmlHelper methods will most likely have an id='Expression' attribute which causes the whole problem.

MVC #Html.Display()

I have something like:
<input type="text" name="TerrMng" id="TerrMng"/>
in HTML. What is the equivalent of the above using #Html.Display?
I tried using: #Html.Display("TerrMng", TerrMng)
but was not successful. Note that I like to use #Html.Display but not sure how to translate the ID value so that it shows up.
The Display method is not for creating input boxes. You'd want to use:
#Html.TextBoxFor(m => m.TerrMng);
or the templated helper method:
#Html.EditorFor(m => m.TerrMng);
I'm assuming that you want to use modelbinding. If not, if you really just want to use a helper to simply make an input tag, use:
#Html.TextBox("TerrMng");
This would be sent to the client:
<input id="TerrMng" type="text" value="" name="TerrMng">
The first 2 methods above would result in the exact same html, if model.TerrMng was "" or String.Empty. If for some reason you don't want the value attribute, you'll need to type it out yourself.
This should do the trick if you are just wanting to display the data and not allow the user to edit the information.
#Html.DisplayFor(m => m.TerrMng);
Edit:
what-is-the-html-displayfor-syntax-for is another question on stackoverflow that may give you some more guidance.
Edit:
TerrMng does not exist on PageLoad so you cannot use the Html.Display in that way. You need to create it and fill its value with the value received from the jQuery. In this case where you would have to do the following:
HTML
#Html.Display("TerrMng"); // This creates the label with an id of TerrMng
jQuery
$("#TerrMng").val(TerrMng); // This puts the value of the javascript variable into the label
You could try something based on this. This is not exact but you could get some idea.
#Html.TextBoxFor(yourmodel => model.yourModelFieldname, null)
#Html.Display() is used instead of #Html.DisplayFor() when your model is not known at compile time, or if you prefer to work with strings, rather than with strong types. For example, these 2 are equivalents (given that your model is some class):
#Html.DisplayFor(m => m.MyProperty)
and
#Html.Display("MyProperty")
But the additional cool feature of the Display() method is that it can also do the lookup in the ViewData, and not just in your Model class. For example, here is a way to display the HTML for the property on a random object, given that we know it has a property named "Blah" (the type of the object doesn't really matter):
#{ ViewData["itsawonderfullife"] = SomeObject; }
<div>#Html.Display("itsawonderfullife.Blah")</div>
This way, we are telling HtmlHelper to look into the ViewData, instead of our Model, and to display the property Blah of a given SomeObject.

How do I access a variable value assigned to an HTML.Hidden variable in a MVC Controller Action Method

I am writing my first ASP.Net webpage and using MVC.
I have a string that I am building in a partial view with a grid control (DevExpress MVCxGridView). In my partial view I am using a HTML.Hidden helper as shown below.
' Create a hidden variable to pass back a comma-delimited string
Response.Write(Html.Hidden( "exclusionList", Model.ExclusionList))
The value of of this hidden element is assigned in client side javaScript:
exclusionListElement = document.getElementById("exclusionList");
// ...
exclusionString = getExclusionString();
exclusionListElement.value = exclusionString;
This seems to work without problem.
In my controller action method:
<AcceptVerbs( HttpVerbs.Post )> _
Public Function MyPartialCallback(updatedItemList As myModel) As ActionResult
Dim myData As myModel = GetMyModel()
Return PartialView( "MyPartial", myModel.myList )
End Function
The updatedItemList parameter is always nothing and exclusion list exists no where in the Request.Forms.
My questions are:
What is the correct way to use Html.Hidden so that I can access data in a MVC Controller Action method.
Is adding "cargo" variables to Request.Form the best and only way to send data back to a server side MVC Controller Action method? It just seems like twine and duct-tape approach. Is there a more structured approach?
If you need to get the exclusionList variable back, you just need to add a property to your view model that matches that name exactly. Make sure it is of the correct type (string it looks like in this case) and then it should auto populate that property in the view model for you.
And yes, there is no need for the Response.Write call. Instead just use the Html.HiddenFor(...) helper in your view.
Look at the generated HTML. Note down the name attribute of the hidden field. Use this name as action parameter name:
Public Function MyPartialCallback(exclusionList As string)

Resources