EditorFor + TModel - asp.net-mvc-3

the signature for this very useful method states I can indicate a type:
public static MvcHtmlString EditorFor<TModel, TValue>(
this HtmlHelper<TModel> html,
Expression<Func<TModel, TValue>> expression
)
... the docs very understandably state that TModel is "The type of model". It must be my particular bent that this description conveys no meaning to me whatsoever. I've googled for an explanation but found zilch.
I'm in a view where #model Website.Models.Product but want to create an editor for something of a different type. I thought I could:
#Html.EditorFor(#ViewBag.AClassOfTheOtherType)
or maybe (I'm obviously guessing):
#Html.EditorFor(TheOtherType)
but that is not acceptable syntax and so I thought:
#Html.EditorFor(x => x...)
but the lambda expression seems to be bound to #model... so I thought, "ah!":
#Html.EditoFor<TheOtherType>(...)
but VS thinks the < starts an HTML tag and indicates the end of my EditorFor call (which fails).
aaaaahhhhh!
how do I do this (in case I actually need to ask)?

Haven't tested this, but couldn't you do:
#Html.EditorFor(x => ViewBag.AClassOfTheOtherType)
You don't necessarily need to use the variable passed into the Lambda.

Model - is the Model - the M in MVC
TModel could just as well be sdserweJJG - it's only consistently called TModel out of convention.
Within the helper method for EditorFor you will see something like:
TValue val = expression.Compile()(htmlHelper.ViewData.Model);
this is where the extension method compiles the lambda passed in - for example the x=>x.Model.Property part - and gets back the actual Model data to use to build the actual display controls.
The Model is passed to the view when you call return View(viewModel); from your controller action.
What you are trying to do doesn't make sense because the method was designed to work with the views Model.
You can however use #Html.Editor as this will take the actual value in the way you are trying:
#Html.Editor(ViewBag.AClassOfTheOtherType)
The sourcecode for MVC is freely available to download and view - it's well worth taking the time to do so :)

the answer is (drumroll please)... yes, one can bind the lambda expression with the type declarator. the only problem is the Visual Studio editor, which thinks one is ending the C# part and entering the HTML part with the opening < and thus disallows proper code. Solution:
#{ Html.EditoFor<TheOtherType>(...) }

Related

Understanding Html.ActionLink(..., ..., ...) syntax in MVC3

I'm doing a tutorial on MVC 3 and I stumbled upon the helper #Html.ActionLink(genre.Name, "Browse", new {genre = genre.Name}).
Now I understand what the values do and that the third value is a route parameter value but this is the first time I'm seeing this kind of syntax which is really bugging me for some reason.
What I mean exactly is new {genre = genre.Name}. I've come to understand that "new" precedes object/type declaration, however, this time it's simply the "new" keyword and the curly brackets. How exactly is this processed?
The syntax new { prop = val } creates an anonymous type. It's essentially the same as creating an instance of a class, except you're declaring the class and the instance all in one shot. Some people think that anonymous types are not statically typed or are not type safe. This isn't true. The types of the properties are inferred from the values they are assigned. This construction is used frequently in MVC and in linq.
Note that this syntax is not specific to MVC. You can use it anywhere it's convenient. I make a fair amount of use of anonymous types in day-to-day coding.
It's simple.. the first parameter is the link you want to display, so genre.Name can corresponds to Rock. The second argument is the action, the third argument your Controller class. The last parameter is the route values in the form of an anonymous object (an object you will never use again, the MVC engine uses the anonymous object in this case).
So your action(method) takes a string argument.
For example:
"Home" is the link the user sees (the first argument), Home (the second argument) is the action (method) to your Controller class, and it takes a string argument.
class HomeController
{
public ActionResult GenreAction(string genre)
{
}
}
When a request is made, it becomes Home/GenreAction/genre
It's a C# language feature called Anonymous Type, introduced with C# 3.5 if I'm not mistaken.

Accessing "this" from a Helper method in ASP.NET MVC 3

I have a Helper method that I need to use across multiple views. In an attempt to accomplish this, I tried to implement the approach shown by Scott Guthrie here: http://weblogs.asp.net/scottgu/archive/2011/05/12/asp-net-mvc-3-and-the-helper-syntax-within-razor.aspx. For the sake of reference, my helper method looks like the following:
#helper MyMethod(string parameter)
{
MyNamespace.MyClass.HelperMethod(this.Request, parameter)
}
As you can see, I need to get access to the HttpRequestBase object associated with the view. The code works fine if I define the method at the top of _Layout.cshtml. However, I need to use it in other views. So, as mentioned, I used the approach highlighted by scottgu. Unfortunately, I get a runtime error now that says:
"CS0026: Keyword 'this' is not valid in a static property, static method, or static field initializer"
How can I get over this hump?
If it's the HttpRequestBase object you need, try passing in HttpContext.Current.Request instead of this.Request.

Creating a containing element whose properties can affect how child elements are rendered

I have a partial view that sometimes needs to collect data and sometimes just needs to display the saved data and not allow editing.
I would like to use the same partial view for both needs because the formatting is complex. However, I don't want to just apply the "disabled" tag to specific controls: I want (on the server side) to render the read-only data as text, not as controls, so that it can't be posted back.
(To complicate things, there is one field, for comments, that can be edited even when all the other fields are read-only, so there will be a post back.)
I'm thinking about a general solution to this problem. The simplest thing to do would be to apply the following code pattern to all the fields:
#{ if(condition) {
#Html.TextBoxFor(model=>model.Field)
}
else
{
#Html.DisplayFor(model=>model.Field)
}
}
But that's inelegant and can lead to harder to read code. Also, since the pattern has to be manually applied, it would be easy to make mistakes.
I was toying with writing some extension methods to supplement TextBoxFor, et al, that would take an additional parameter that indicated whether to invoke TextBoxFor or DisplayFor.
But what I'd like even better would be something that I could set on a containing element that would automatically affect how the child elements are rendered, the way you can set the Visible property on an ASP.NET WebForms Panel control.
So now I'm treading out into the field of the hypothetical. To achieve such a thing, I would need a server-side containing element (perhaps following the same pattern as BeginForm) that had awareness of its own scope and could affect how the rendering extension calls it contained were invoked.
Is such a thing even possible?
I would look at this post: http://kazimanzurrashid.com/posts/asp-dot-net-mvc-viewmodel-usage-and-pick-your-best-pattern
It has an approach that remove logic, procedural code from Views and promotes better OOP and encapsulation. You may find it useful, at least informative.
I'm not really recommending this, but you could do something like this:
public static MvcHtmlString Ternary<TModel, TValue>(this HtmlHelper<TModel> html, bool test, Expression<Func<TModel, TValue>> expression, Func<Expression<Func<TModel, TValue>>, MvcHtmlString> truthy, Func<Expression<Func<TModel, TValue>>, MvcHtmlString> falsey)
{
return ((test) ? truthy : falsey).Invoke(expression);
}
and then use it like this:
#Html.Ternary(condition, x => x.Field, Html.TextBoxFor, Html.DisplayFor));
Separate views, like Shark said, is really probably the right way to go.

ASP.NET MVC Strongly Typed HTML Helpers

Lets take this line: #Html.LabelFor(m => m.UserName)
which is on a page with this line: #model CurrencyMvc.Models.RegisterModel
I assume that when the page view renders LabelFor is called automatically with a reference to the model described, and that the Lambda function tells it how to get the info it needs from the model?
Its not clear to me why we're passing a function in when we could pass the actual value e.g. m.Username.
Oh and when this helper is called where does "m" come from?
There are 2 classes that are used for razor pages (the second derives from the first):
System.Web.Mvc.WebViewPage
System.Web.Mvc.WebViewPage<T>
So when you use a strongly typed view by specifying a model, your view derives from the generic version and the Html property is a generic HtmlHelper<TModel>. Since it is a good practice to always use strongly typed views I will no longer talk about the first class since it is of no interest.
Let's take a look at the signature of the LabelFor extension method:
public static MvcHtmlString LabelFor<TModel, TValue>(
this HtmlHelper<TModel> html,
Expression<Func<TModel, TValue>> expression
)
{
...
}
As you can see from this definition the LabelFor method is an extension method for the HtmlHelper<TModel> class which takes 1 argument. This method is available only when you have a strongly typed view. The argument represents a lambda expression which is limited only to a member access expressions (the helper will throw an exception if you try to use some fancy stuff). It takes the model as argument and a property of this model must be returned.
Thanks to this information the helper is capable of determining the name of the member that is being specified and thus generate the correct markup. And since the argument is a lambda expression it is also capable of determining the metadata of this property (you might have decorated your view model property with attributes such as [DisplayName], ... allowing you to specify additional metadata). If the helper took only a value as you asked: Html.LabelFor(Model.SomeValue) you understand that inside this LabelFor method all you are going to get is this value. You will never be able to access the metadata of the view model which is a fundamental notion in ASP.NET MVC.
I assume that when the page view renders LabelFor is called
automatically with a reference to the model described, and that the
Lambda function tells it how to get the info it needs from the model?
I'm not entirely sure I get what you mean with this part, I guess you mean how #LabelForknows which model to use?
Well yes, if you look at the syntax which is like this:
public static MvcHtmlString LabelFor<TModel, TValue>(
this HtmlHelper<TModel> html,
Expression<Func<TModel, TValue>> expression
)
You can see the first parameter starts with this which makes it an extention method. When you add the line #model CurrencyMvc.Models.RegisterModel this HtmlHelper<TModel> becomes your RegisterModel.
Its not clear to me why we're passing a function in when we could pass
the actual value e.g. m.Username.
Most of the time a "lambda expression" is simply a Func<T> but with the razor #Html.xfor (such as #Html.LabelFor) you pass in an Expression<Func<TModel, TValue>> which is a tree data structure for a lambda expression. In layman's terms; kind of an uncompiled Func.
If you'd pass in m.Username the method would simply have "Dale Burrell". But for example, html textbox is generated as
<input type="text" name="Username" value="Dale Burrell">
So as you can see, it actually needs the m.Username variable name
Oh and when this helper is called where does "m" come from?
That's just a variable. Just like foreach(var m in dataset){} "where does the m come from?" -- you made it up. You can replace the m with anything
I know it has been some time but I think the link below will be quite helpful for the ones still looking for a good explanation.
http://odetocode.com/blogs/scott/archive/2012/11/26/why-all-the-lambdas.aspx

How to test HtmlHelpers that call Partial?

I've been looking at this Stackoverflow question and have the answer implemented. It works all fine and dandy until I get to call HtmlHelper.Partial in my helper method, which is listed below. I know it might not be the best code, but this is until I can refactor more of the app. The error it throws is
Previous method 'ViewContext.get_TempData();' requires a return value or an exception to throw.
Am I missing mocking something, or is there a better way to render a usercontrol?
Edit Ok I did miss something, I didn't call mocks.Replay(). Now have another error which it wants something named controller in routeData...progress.
Edit #2 Clarifying I'm trying to mock the call to HtmlHelper.Partial(partialPath, model), I just want that to return whatever partialPath I send in I suppose, or at least not blowup. I did find this page http://andrevianna.com/blog/?p=8 which was very helpful and I almost got things working. This was helpful as well http://farm-fresh-code.blogspot.com/2009/10/mocking-htmlhelper-class-with.html
public static string RenderRateDetails(this HtmlHelper html, string partialPath, RatesViewData model, RateDetailType type)
{
switch (type)
{
case RateDetailType.AR:
if (model.ExistingRateDetailAR != null)
return html.Partial(partialPath, model).ToString();
break;
case RateDetailType.AP:
if (model.ExistingRateDetail != null)
return html.Partial(partialPath, model).ToString();
break;
}
return string.Empty;
}
I think the example given at 'farm fresh code' is the right way to go, you can't directly mock the HtmlHelper, but you can build an instance where all of it's dependencies are mocked.
When you're code calls html.Partial(partialPath, model).ToString(), the HtmlHelper calls properties and methods on the dependencies that you mocked, and you get errors if these don't return reasonable default values.
In this case it looks like the TemplateData property of the mocked ViewContext object was called, and I imagine it returned null, hence:
Previous method 'ViewContext.get_TempData();' requires a return value or an exception to throw.
Once you mock this property, you should be able to get past this error, but you might need to mock a few more things before you get it all working.
It might save you some time to take a look at the MVC source code to see what gets called in the Partial method. You can get that here http://aspnet.codeplex.com/releases/view/58781.
EDIT
BTW. The TempData property returns a System.Web.Mvc.TempDataDictionary. Mocking the property to return an empty instance of one of those should solve the immediate problem.
Have you considered using Display and Editor templates for your user controls rather than extending HtmlHelper?
I used to do the same thing quite a lot in the early MVC versions, but I have switched almost completely to using templates now.

Resources