TModel not recognized in a Razor Helper as Lamba Expression - asp.net-mvc

I trying to use a Razor Helper that does some if statements for me. I'm passing it a list with certain rules and based on that rule a label and input text field are created.
The problem I have is I can't get it to work with a Lambda expression as parameter. It won't recognize the TModel part.
The helper method is as follows:
#helper CreateCheckbox(Expression<Func<TModel, bool>> expression, object htmlAttributes, List<Rule> ruleList)
{
}
The error I get is: The type of namespace 'TModel' can not be found.

You have to pick a type for TModel. Because your view doesn't have any generic type parameters, there is no way for it to figure out what type it should substitute in for TModel. You have to give it an actual type to work with.
If that's not an option, you might just make an extension method for the HtmlHelper class, and make it a generic method. See this question for an example of how to do that.

Related

HTML Helper how to use IEnumerable

I want to create a custom HTML Helper where I can pass a LINQ expression as a parameter, like this:
#Html.GetBackgroundColor(model => model.RiskAssessment)
I want to use it to display some custom css in an MVC view, depending on what the RiskAssessment property is.
So I created a helper method like this:
public static string GetBackgroundColor<T, TResult>(this HtmlHelper<T> htmlHelper, Expression<Func<T, TResult>> expression)
{
...
}
However, that won't compile, the error is IEnumerable does not contain a definition for 'RiskAssessment'
So I changed the method to
public static string GetBackgroundColor<T, TResult>(this HtmlHelper<IEnumerable<T>> htmlHelper, Expression<Func<T, TResult>> expression)
{
...
}
which compiles, so now I presumably have all the objects in the collection but I have no idea how to get the object I want as I can't use use the expression on the IEnumerable, there is no Where() method available. I would have thought I could do something like this:
IEnumerable<T> collection = htmlHelper.ViewData.Model;
T obj = collection.Where(expression)
but I just don't know what I am doing wrong here.
Figured it out, simple mistake. The table header row is set up with #Html.DisplayNameFor(model => modelType), and I was trying to call my custom HTML helper with these parameters. I should have been calling the method on each table row, using #Html.GetBackgroundColor(modelItem => item.RiskAssessment), and this works because I can use htmlHelper.ValueFor(expression) within the method to get the property value.
That said, I have no idea how the header row is generated as Html.DisplayNameFor uses the same method signature as my custom method but Intellisense reports that one of the Expression types is unknown. But that is not an issue for me.
Thanks.

Why is lambda notation even needed in Razor?

I think my understanding of lambda notation is lacking because I do not understand why it is even needed in Razor. For example:
instead of this:
#Html.DisplayFor(modelItem => item.FirstName)
why can't we just have this:
#Html.DisplayFor(item.FirstName)
What is the purpose of the lambda syntax and why do we need to add in all of the extra work of typing out the lambda notation?
Can someone help me to understand why this is needed and what benefit it offers?
The purpose of lambda expression on Razor view is returning value given by model from anonymous function (i.e. nameless function). Take a look on your first example:
#Html.DisplayFor(modelItem => item.FirstName)
would be translated to something like:
#Html.DisplayFor(String Function(Model modelItem)
{
return item.FirstName;
})
Here modelItem given as function parameter declared as Model, and return statement as function body to return property value depending on get/set operation.
If we look further to the DisplayFor helper declaration:
public static MvcHtmlString DisplayFor<TModel, TValue>(
this HtmlHelper<TModel> html,
Expression<Func<TModel, TValue>> expression,
string templateName
)
As stated by #SLaks before, expression tree can be parsed as a parameter to generate proper HTML tag into view based from type of your model defined by #model directive instead of executing it.
The second argument, Expression<Func<TModel, TValue>> is a declaration that ensures any given function parameter will always have same type as your model. This way eliminates reflection code that using GetProperty and GetValue required by HTML helper to retrieve given property value at appropriate time with strongly typed model as a benefit.
Here is an example of reflection code inside HTML helper declaration which can be eliminated by lambda syntax:
var model = html.ViewData.Model;
var value = String.Empty;
if (model != null)
{
var type = typeof(TModel);
var propertyInfo = type.GetProperty(templateName);
var propertyValue = propertyInfo.GetValue(model);
value = propertyValue.ToString();
}
Afterwards, let's examine the second example:
#Html.DisplayFor(item.FirstName)
Here DisplayFor will use Object as parameter type, consider we can't determine exactly what type should be pre-assigned thus it sets to System.Object. Since the method doesn't provide model definition type as TModel in generic code, the method probably requires reflection when dealing with property value.
Any improvements and suggestions welcome.
References:
https://msdn.microsoft.com/en-us/library/hh833706%28v=vs.118%29.aspx
http://odetocode.com/blogs/scott/archive/2012/11/26/why-all-the-lambdas.aspx
I want to understand the lambda expression in #Html.DisplayFor(modelItem => item.FirstName)
The HtmlHelper methods take an expression tree as a parameter.
This lets them see the actual property that you pass so that they can observe its attributes.

Asp.Net HtmlHelper with a Generic only work inside a #Razor statement

I would like to understand why the following code does not work while the second work:
#Html.Test<ContestListItemViewModel>() //Does not work
#{
var x = Html.Test<ContestListItemViewModel>();//Work
#Html.Raw(x);//Work
}
The code of this Test helper is very simple:
public static MvcHtmlString Test<TEntityType>(this HtmlHelper htmlHelper)
{
return new MvcHtmlString("<p>Test</P>");
}
The error is during the execution:
An exception of type 'System.Web.HttpCompileException' occurred in
System.Web.dll but was not handled in user code
Compiler Error Message: CS1502: The best overloaded method match for
'System.Web.WebPages.WebPageExecutingBase.Write(System.Web.WebPages.HelperResult)'
has some invalid arguments
The Razor parser is confused. It doesn't know if that is HTML or C#.
Try:
#(Html.Test<ContestListItemViewModel>())
In the second example the statement is wrapped in a Razor code block, so it knows for sure.
Wrapping in parentheses can often solve code confusion problems like this.

ExpressionHelper.GetExpressionText() returning incorrect result when using an expression with casting

I have this example code within Razor:
#Html.TextBoxFor(x => ((VisitGozo.Modules.Data.Events.EventSpecificFieldsData)x.Event.DataObject.SpecificFields).LinkedTourismProduct)
As you can see, the expression includes casting. The TextBoxFor makes use of the ExpressionHelper.GetExpressionText() method which converts an expression into text for Model Binding.
In the above example, due to casting, the returned field name is just LinkedTourismProduct and not Event.DataObject.SpecificFields.LinkedTourismProduct. If there wasn't any casting, this would have returned the full name correctly.
Hence during model binding, it does not bind to the correct property because the property of the Model is Event.DataObject.SpecificFields.LinkedTourismProduct and not LinkedTourismProduct only.
Any idea why this is happening and is there any other solution/workaround?
That's normal behavior. Only simple expressions (property access and indexer access) are supported by the strongly typed helpers. Casting is not.
The correct way to do this is to have a property on your view model of the correct type:
public EventSpecificFieldsData EventSpecificFields { get; set; }
and then bind your textbox to it without any casts:
#Html.TextBoxFor(x => x.Event.DataObject.EventSpecificFields.LinkedTourismProduct)

RenderPartialForEach<T>(..) HtmlHelper for asp.net mvc?

This throws error:
public static void RenderPartialForEach<T>
(this HtmlHelper helper, string partialName, IList<T> list)
{
foreach (var item in list)
helper.RenderPartial(partialName, item);
}
=>
Error Message: CS1519: Invalid token '(' in class, struct, or interface member declaration
Line 283: #line default
Line 284: #line hidden
Line 285: #__w.Write("\r\n \r\n\r\n\r\n");
Line 286: }
Line 287:
Is it possible to create clean htmlhelper
which is able to render partial views for every item in list passing it as model?
Edit:
That was just a blunder from my side. I forgot to add '<% } %>'.
And got confused cause of error message. ^^
You need to declare the method in a class. It's not obvious that you are doing that, but it would certainly cause the type of error that you are seeing.
public static class CustomHtmlHelperExtensions
{
public static void RenderPartialForEach<T>(
this HtmlHelper helper,
...
}
EDIT: In retrospect, given the text of the error, I suspect that the error lies elsewhere in your markup. Perhaps, you're missing a parenthesis around an if statement or foreach clause.
Where are you declaring such a thing? Try writing that extension method in a separate static class in a code file, not inline in .aspx.
Just a guess... you are implementing a generic method (of T) but you are not actually replacing the generic parameter (T) with a type argument?

Resources