MVC 3 - Additional View Data not appearing in Editor For template - asp.net-mvc-3

In my view, I have a statement like this:
<li>#Html.EditorFor(model => model.SomeEnum, "Enum", new { ShowDefaultText = false })</li>
I have a Enum (SomeEnum) and I have a editor for template for enums. In my editor for template I'm trying to check the ViewData object for the anonymous class I passed it. According to he Html.EditorFor documentation, the third parameter is additional view data that will be with the ViewDataDictionary object.
However, in my template when looking at the ViewData class, I don't see the anonymous class/property in it. Am I doing something wrong, am I looking at the wrong object in my editor for template?

Use the following ViewData syntax
#{
var boolValue = Convert.ToBoolean(ViewData["ShowDefaultText"]);
}
#if (!boolValue)
{
...............
}

Related

EditorFor parameters in Action MVC3

Having a bit of trouble figuring something out.
I've got an Action:
public ActionResult FareTypeSelector(SearchTypes searchType, SearchSource searchSource)
{
IFareTypeOptionsRepository fareTypeOptionRespoitory = new FareTypeOptionsRepository();
FareTypeOptions fareTypeOptions = fareTypeOptionRespoitory.GetFareTypeOptions(searchSource, searchType, _authentication.UserLoggedIn.CallCentreUser, _authentication.UserLoggedIn.AgencyProfile.BranchCode);
return View();
}
I've created an 'Editor', i.e. a file in EditorTemplates called FareTypeSelector.cshtml.
I want to bind my editor to a property of the model of the page that contains the editor. But I also want to pass some parameters into my action, i.e. (SearchTypes searchType, SearchSource searchSource). The idea being that the data displayed in the editor is based on this information passed in. Now I can't quite figure out if:
Is this possible?
whats the markup needed in the main view to render
this, pass the parameters and bind the resulting selected value into the main model?
Ta in advance
EditorTemplates are used for Data items from your model, not Action methods. They're using only in your view to render a specific model (or member of a model)

EditorTemplate for "Floats" not being called in ASP.NET MVC 3

I have a property of type "float" in my ViewModel. It's being displayed as a TextBox with a default value of "0".
I added an "EditorTemplates" folder inside the "Shared" folder & created a new "Float.cshtml" file with the following content:
#Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue == 0 ? "" : ViewData.TemplateInfo.FormattedModelValue, new { #class = "text-box single-line" })
However, still when I run the application, float fields are still being displayed with a default value of 0.
Thanks
UPDATE
I am just trying to see how ASP.NET reacts to custom templates, till now, the engine is not processing my custom template, something similar to:
LatLng.cshtml
#model float
#Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, new { #class = "text-box single-line "}) Latitude
On the ViewModel,
[UIHint("LatLng")]
public float? Latitude { get; set; }
On the View,
#Html.EditorFor(model => model.Latitude)
Nothing is changing, default template is being used.
Float is not actually a .NET type, it's a C# type. Float maps to System.Single, so you need to create a Single.cshtml and not a Float.cshtml.
You can also get around this by specifying a UIHint attribute on the model data, or by specifying the template to use in your Editor or EditorFor methods.
An easy workaround is if you just set ViewData.TemplateInfo.FormattedModelValue to return a string in the model, so you don't have to do that weird logic on the view. If you need it to post back a new value (for editing purposes), you just have to add some logic in the controller to turn the string back into a float.

HtmlHelper and htmlAttributes help

I'm fairly new to MVC 3 and am using the Razor view engine. I'm using the Html.Hidden extension method to output input elements of type hidden. What I woudl also like to do is add a custom attribute to hold a dynamic value. I was under the impression in HTML5 wee could write custom html element attributes that are prefixed with 'data-'. I'm trying to do something like below;
#Html.Hidden("hdnID", mymodel.somevalue, new { data-uniqueid = mymodel.somevalue })
hoping to render;
<input type="hidden" value="mymodel.somevalue" data-uniqueid="mymodel.somevalue"/>
The htmlAttributes part (new { data-uniqueid = mymodel.somevalue }) is giving the error,
"Invalid anonymous type member declarator. Anonymous type members must be declared with a member assignment, simple name or member access".
Can I add user-defined attribute to html elements using the HtmlHelper classes?
Regards,
Use:
#Html.Hidden("hdnID", mymodel.somevalue, new { #data_uniqueid = mymodel.somevalue })
The underscore gets automatically converted to a dash.
Doh! I was being silly. You can't have '-' in the anon type declaration:
data-uniqueid = ...it must be
datauniqueid = ....
In that case, your best best is to write out the hidden input by hand:
<input type="hidden" value="#mymodel.somevalue" data-uniqueid="#mymodel.somevalue"/>
You can step around the member validation by constructing a dictionary object. As follows:
#Html.TextBoxFor(model => model.Phone, new Dictionary<string, object>
{
{
"data-call-results-target", "#search-results-area"
},
{
"data-action-path", "/Controler/Method"
}
})

ASP.Net MVC 3 EditorTemplate for DateTime Fields Error

This code was converted from some ASP.Net MVC 2 code in this tutorial:
MVC 2 Editor Template with DateTime
It is a custom EditorTemplate for DateTime fields stored as 'EditorTemplates/DateTime.cshtml'.
#Model DateTime?
#Html.TextBox("", (Model.HasValue ? Model.Value.ToShortDateString() : string.Empty), new { #class = "datePicker" })
However I get the following error when using #Html.EditorFor(model => model.NewAbsence.StartDate):
CS1973: 'System.Web.Mvc.HtmlHelper' has no applicable method named 'TextBox' but appears to have an extension method by that name. Extension methods cannot be dynamically dispatched. Consider casting the dynamic arguments or calling the extension method without the extension method syntax.
I've seen some similar posts on here which mention casting the parameter of the EditorFor method, however I cannot seem to get this to work in my example.
Could someone please point out what I will need to change in my code. Thanks.
Actually it's #model with lowercase m:
#model DateTime?
^
instead of:
#Model DateTime?
So to sort of summarize what people are saying, and make it a bit more generic. If your view is declaring that it accepts dynamic models:
#model dynamic
Then things like extension methods will not be able to infer the types of arguments passed to them. Here are two examples (using Razor because it's awesome):
#Html.TextBox("myTextBoxName", Model.MyTextBoxValue)
#Html.DropDownList("myDropDownName", Model.MySelectList))
In these cases, the engine doesn't know what types Model.MyTextBoxValue or Model.MySelectList are, therefore it can't figure out what overloads of the extension methods to compile. So you just help it along with some strong typing:
#Html.TextBox("myTextBoxName", (string)Model.MyTextBoxValue)
#Html.DropDownList("myDropDownName", (SelectList)Model.MySelectList))
By the way, just to stop people from potentially pulling out their hair, that SelectList has to be properly instantiated with something like:
var items = List<SelectListItem>();
...
new SelectList(items, "Value", "Text");
As a temporary work around I am using:
<div class="editor-field date-field">
#Html.EditorFor(model => model.NewAbsence.StartDate)
#Html.ValidationMessageFor(model => model.NewAbsence.StartDate)
</div>
Then using the jQuery selector:
$(".date-field > input").datepicker({
showOn: "button",
buttonImage: "*pathtoimage*"
});
To apply the date picker to the input tags within the 'date-field' div. However this still doesn't format the date value how I want it to display initially, and cuts out the editor template entirely.
The error message comes from your textbox statement. In a template, this becomes a dynamic expression, and .Net doesn't know how to type the Model properties.
#Html.TextBox("", (string)(Model==null ? Model.Value.ToShortDateString() : string.Empty), new { style = "width: 10em;", #class="datefield" })
Explicitly cast your date value as string, and the dynamic expression has the information it needs. I also had a problem with the .HasValue property, but that wasn't the point of your question.

Modifying MVC 3 ViewBag in a partial view does not persist to the _Layout.cshtml

I am using MVC 3 with the Razor view engine. I want to set some values in the ViewBag inside a Partial View and want retrieve those values in my _Layout.cshtml. For example, when you setup a default ASP.NET MVC 3 project you get a _Layout.cshtml file in the "/Views/Shared" folder. In that _Layout.cshtml the Page Title is set like this:
<title>#ViewBag.PageTitle</title>
Then in "/Views/Home/About.cshtml" view the contents of the ViewBag are modified:
#{
ViewBag.Title = "About Us";
}
This works fine. When the About view is rendered the page title is "About Us". So, now I want to render a Partial view inside my About view and I want to modify the ViewBag.Title inside my Partial view. ("/Views/Shared/SomePartial.cshtml")
#Html.Partial("SomePartial")
In this Partial view I have this code:
#{
ViewBag.Title = "About Us From The Partial View";
}
When I debug this code I see the ViewBag.Title get set to "About Us" and then in the Partial view I see it get reset to "About Us From The Partial View", but when the code hits the _Layout.cshtml it goes back to "About Us".
Does this mean that if the contents of the ViewBag are modified in a Partial view, those changes will not appear(be accessible) in the main view (About.cshtml) or the _Layout.cshtml?
Thanks in advance!
If you pass the ViewBag into the partial's viewdatadictionary, then pull it out (and cast), you can do whatever you want and the reference is kept. The cool part is that since it's dynamic, you can even add properties and then they'll show up on the parent page's Viewbag.
Page:
//set the viewbag into the partial's view data
#{Html.RenderPartial("Elaborate", Model, new ViewDataDictionary { {"vb", ViewBag}});}
Partial:
#{
var vb = ((dynamic)ViewData["vb"]);
vb.TheTitle = "New values";
}
Page
#ViewBag.TheTitle = "New value"
I also had this problem, and couldn't find any neat and obvious solution.
The solution I came up with was to implement an Html extension method that returns a 'PageData' class that you define, containing whatever data you need:
[ThreadStatic]
private static ControllerBase pageDataController;
[ThreadStatic]
private static PageData pageData;
public static PageData GetPageData(this HtmlHelper html) {
ControllerBase controller = html.ViewContext.Controller;
while (controller.ControllerContext.IsChildAction) {
controller = controller.ControllerContext.ParentActionViewContext.Controller;
}
if (pageDataController == controller) {
return pageData;
} else {
pageDataController = controller;
pageData = new PageData();
return pageData;
}
}
It finds the top-level controller for the current request, and returns the same PageData object every time the method is called within the same HTTP request. It creates a new PageData object the first time it is called in a new HTTP request.
The partial view gets its own ViewBag.
You can get the page's ViewBag from ((WebViewPage) WebPageContext.Current.Page).ViewBag
You can do this trick in your partial view to override the title in your _Layout.cshtml:
#{
ViewBag.Title = "About Us From The Partial View";
}
......
<script type="text/javascript">
document.title = "#ViewBag.Title";
</script>
As others have pointed out Layout, Views and Partials get their own ViewBag. However, I was able to get it to work with the following:
In the View or Partial.
#{ Html.ViewContext.ViewBag.Title = "Reusable Global Variable"; }
Then in _Layout
#Html.ViewContext.ViewBag.Title
By explicitly using the ViewContext, the views 're-use' the same ViewBag.
If anyone is still looking for a solution to this it appears that you can do it with TempData:
TempData["x"] = x;
TempData is persisted until it is read so you can just read it in your _Layout. You just have to be careful that you read everything so that it is cleared for the next request.
I've tried the following and it works:
In the (parent) view...
#Html.Partial("SomePartial", ViewData, null)
Note: ViewData is passed as the model argument, but you have to specify null for the viewData argument to use the correct overload. You can't use ViewBag because Html.Partial doesn't like dynamics.
Then , in the partial view...
#model ViewDataDictionary
#{
Model["Title"] = "About us from the partial view";
}
Of course, if you need to use the model argument for a real model, you'll have to be more creative.
try #SLaks code with
(((WebViewPage) WebPageContext.Current.Page).ViewBag).PropertyName
I encountered the same problem when I use mvc3, and I found that
this.ViewBag.ViewBag.PropertyName
works in your custom control.
I this is what page data is designed for. Pop this into your view.
#Page.somePropertyName = "Whatever you want";
And then access it in your layout view. Be sure to check that its not null first.
#{
if(Page.somePropertyName != null)
{
//Do stuff
}
}

Resources