Lambda expression in #Html.DisplayNameFor() - c#

Let's say I am passing a List of Person as the model to a view page:
#model IEnumerable<Foo.Models.Person>
#foreach (var item in Model)
{
<p>
#Html.DisplayFor(ListOfPersons => item.Id) : #Html.DisplayFor(ListOfPersons =>item.Name)
</p>
}
I just don't get how this Lambda expression works. Why we are passing a IEnumerable and get a single value of on its objects?

As ListOfPersons is not a declared variable, and is just the parameter name for the expression, the expressions are valid.
To briefly touch on expressions, they are composed of both a parameter set and a body.
(parameter set) => (body)
The parameter set can be empty () or include 1 or many parameters (x) or (x,y) for example. The body then can use those parameters similar to how a method body would use parameters passed in.
#Html.DisplayFor(ListOfPersons => item.Id) when used in the scope shown is ignoring the parameter. It doesn't use it, and is similar to something like this
public int Id = 5;
public string DisplayFor(Person ListOfPersons)
{
return Id;
}
So you can see from this aspect that the parameter is not used and the value returned is actually a value from a different scope.
DisplayFor is scoped to use the page's model to bind to. So regardless of the parameter name, what is passed in to the parameter is going to be the model. As such, since the parameter is being completely ignored here, it doesn't particularly matter what it was named and could simply be () or _.
The returned value is then the value from the body, in this case item.Id and item.Name. However, as a result of there being no use of the parameter, the html rendered will be incorrect even though the value shown will be what looks to be accurate.
In order to remedy this, the model must be properly accessed or the rendered html will not be bound on post. This is typically done by iterating and using an index reference, as is shown in #Jonespolis' answer.

use a for loop in razor, so you maintain a direct reference to your model:
#for(int i =0; i < Model.Count(); i++)
{
<p>
#Html.DisplayFor(model => model[i].Id) : #Html.DisplayFor(model => model[i].Name)
</p>
}

#Html.DisplayFor(ListOfPersons => item.Id) : #Html.DisplayFor(ListOfPersons =>item.Name)
The view engine will examine the expression and determine that you want "display" controls for the Id and Name properties. It ignores the "input" variable name (ListOfPersons* in this case) and the item variable and just parses the expression on the right. You could just as well have done this:
#Html.DisplayFor(_ => item.Id) : #Html.DisplayFor(_ => item.Name)
*Note that ListOfPersons in your lambda does NOT reference a local variable - it just creates a new "variable" that you could reference in your lambda. The fact that you seem to have a property or local variable named ListOfPersons is irrelevant.

Related

.Where in list gives error for using dynamic types in 2sxc

I have this working code:
#{
var ekgList = AsList(App.Data["Ekgs"]);
foreach(var ekg in ekgList) {
<div>
#foreach (var entitiesFromSinusDataType in ekg.Sinus) {
if(entitiesFromSinusDataType.EntityId == Content.EntityId) {
<a>#ekg.ShortName</a>
}
}
</div>
}
}
For what I can understand:
ekgList is a list (standard 2sxc code for accessing data)
ekg.Sinus is a field in Ekgs content type (Entity data type, multiple, which creates a list of entities from another content type,
Sinus)
Content.EntityId is an int, the same as entitiesFromSinusDataType.EntityId
There should be an easy way to remove the second loop and the "if", by placing a where clause in the first loop.
I'm trying this step by step, but as soon as I try this:
#{
var ekgList = AsList(App.Data["Ekgs"]);
foreach(var ekg in ekgList) {
<div>
#foreach (var entitiesFromSinusDataType in ekg.Sinus.Where(i => i.EntityId == Content.EntityId)) {
<a>#ekg.ShortName</a>
}
</div>
}
}
I get this error:
CS1977: Cannot use a lambda expression as an argument to a dynamically dispatched operation without first casting it to a delegate or expression tree type
This seems to be caused by using dynamic types.
Is there some way to cast the lists into a non dynamic version of them?
Try to cast the ekg.Sinus to ((IEnumerable<dynamic>)ekg.Sinus) :
((IEnumerable<dynamic>)ekg.Sinus).Where(i => i.EntityId == Content.EntityId)
I hope this will help you out.
This is because the compiler cannot guess that ekg.Sinus is a list - so LINQ extensions are not automatically resolved. Note that you can also do this since 2sxc 10.25:
AsList(ekg.Sinus).Where(...)
or in case that doesn't work (again sometimes the compiler can't guess everything) do:
AsList(ekg.Sinus as object).Where(...)
I also suggest you check out the LINQ tutorials https://2sxc.org/dnn-tutorials/en/razor/linq/home

How do I use an IEnumerable string parameter in my code?

As part of my search page I am allowing the user to make a multi-select on specific search terms using the KendoUI Multiselect widget. These items in the collection are passed to my controller as a parameter. My question is, after I have passed them to my controller how do I use them? more specifically how do I use them in my Where statement which uses the Contains method.
Here is my code for the multi select widget
#(Html.Kendo().MultiSelect()
.Name("vessel_type")
.Placeholder("Select Type")
.BindTo(new List<string>() {
"AHTS",
"PSV",
"Special"
}))
Here is my controller code which uses the vessel_type as a parameter
public ActionResult Search(IEnumerable<string> vessel_type)
{
var vessels = (from o in db.vessels
select o);
vessels = vessels.Where(s => s.vessel_type.Contains(vessel_type));
return PartialView("_results", vessels);
}
This line isn't correct because it's expecting a string but I have a collection of mroe than one:
vessels = vessels.Where(s => s.vessel_type.Contains(vessel_type));
Thanks
If I understand the question correctly, I believe you need to perform the check the other way around, that is check if the vessel_type collection contains the vessel's type:
vessels = vessels.Where(s => vessel_type.Contains(s.vessel_type));
Here Contains is an extension method on IEnumerable<T>.
On a side note, since the parameter represents a collection, I think a plural name is more appropriate, for example vessel_types.

Is it possible to know a variable's name [duplicate]

This question already has answers here:
How can I get the name of a variable passed into a function?
(23 answers)
Closed 8 years ago.
One thing to note is: I need to get the variable's name, not the property's name.
I have a scenario where I need to pass in either List<int> or List<double> to a view. So in the partial view I bind the model to dynamic:
#model dynamic
var nameOfParameter = Web.Controllers.MemberInfoGetting.GetMemberName(() => Model);
#foreach (var value in list)
{
<td>#Html.Editor(value, "DoubleTemplate", new { Column = count, Switcher = (YearOfProgram >= count)})</td>
sum += (double)value;
++count;
}
And this is how I call the partial view from the main view:
#Html.Partial("_myPartial", Model.CategoryList)
Then I found that I have to know the name(CategoryList) of the list which is passed into the partial view.
Here I found many posts talking about using something like this:
public static class MemberInfoGetting
{
public static string GetMemberName<T>(Expression<Func<T>> memberExpression)
{
MemberExpression expressionBody = (MemberExpression)memberExpression.Body;
return expressionBody.Member.Name;
}
}
}
This code doesn't work for me as the result of (nameOfParameter )calling it will always be "Model", rather than "CategoryList".
So, is it possible to achieve what I want?
Update1
The reason why I need to pass in the name of the list is because I need the name of the list to form the name on the html element into something like: CategoryList_1. So Razor knows I am trying to bind the value of the textbox into the 1st element of a property in my view model.
class MyViewModel
{
................
public List<double> CategoryList {get; set;}
................
}
Update2
#Html.RenderPartial("_VerificationSummarySection",new { ListName = "PracticeEvaluationCreditsVerifiedList", List = Model.PracticeEvaluationCreditsVerifiedList})
I'm now trying to pass in the name of the list by using RenderPartial. But, I cannot find the right way to use it.
Names are relative. The list itself doesn't have a name.
One thing to notice is : I need to get the variable's name, not the property's name.
In your use of the method:
var nameOfParameter = Web.Controllers.MemberInfoGetting.GetMemberName(() => Model);
The name here is Model, and Model is the property; it isn't a variable. You have obtained the name of the property: "Model". You cannot, however, obtain the name as it would have been in a calling context (even in regular C# this is hard; between views, however, it is essentially impossible).
As an aside, with that GetMemberName method, if you did use it to obtain the name of a variable, in IL terms it would actually cease being a variable, and would instead become a field - because that is how "captured variables" are implemented by the compiler.
You should instead make it possible to pass down the name you want; either as additional context on the view, or as a view-model that encapsulates a list and a name, or by creating a "named list" concept (perhaps by subclassing List<T> or similar, or by adding an INamedList interface).

How do I pass a property expression back to the controller?

I have a view that displays a table of data that I want to be able to sort by specifying a property on the row type.
My model (so far) is:
class Model
{
List<DataType> data;
Expression<Func<DataType, object>> SortProperty;
}
I've tried creating that in the view to be passed into my controller as follows:
<a href='<%= Url.Action("Index", "Approvals", new IndexModel() { Page = Model.Page, SortProperty = ((ApprovalModel m) => m.Id)}) %>'>Id</a>
which renders as:
<a href='/PartsLegislation/Approvals.aspx/Approvals?SortProperty=m%20%3D%3E%20Convert(m.Id)'>Id</a>
so it sort of looks like it'll work (all-be-it with a Convert expression in there), however in the controller SortProperty is always null.
TL;DR: How can I pass an expression pointing to a property from my view to the controller?
Edit: My controller action is as below:
[HttpGet]
public ActionResult Index(Model viewModel)
{
....
viewModel.Approvals = PartsDC.Repository<Approval>()
.Where(a => !a.Deleted)
.OrderBy(viewModel.SortExpression)
.Skip((viewModel.Page ?? 0) * RowsPerPage)
.Take(RowsPerPage)
.Select(a => Mapper.Map<Approval, ApprovalHeaderModel>(a))
.ToList();
...
}
As far as I know you can't pass complex objects via URL. You can pass for example string with name of property.
However if you want to just sort table maybe you should think about another approach to this problem. Check this http://www.kryogenix.org/code/browser/sorttable/ .
It allows you to simply sort your table by clicking on headers.

c# razor actionresult parameter

I'm totally new at mvc razor, so please excuse my unfamilarity with this subject. If I understood well, it is the controller which gets the url parameter, like the following:
// Get: /User/23
public ActionResult(int? id) {
return View("User", id);
}
So basically, this ActionResult gets the parameter in the url and sends it somewhere. But where?
Also, say I have a repository in which I return all the users in an IQueryable<> class. If the id is not null it gets only the relevant result, if not, it gets the whole list. Therefore, I'd like to return a list to the view and show the results. How can I do that? To ease the understanding I give the following example:
// I want to populate this list based on the id in the actionresult
#foreach(var u in MyList) {
name: #u.Name <br>
lastname: #u.LastName <hr>
}
return View invokes a helper method in the base class, Controller.
It will return a ViewResult, with the parameter being the model passed to the view.
So if you want to pass through a list of User's, you would do this:
public ActionResult Users()
{
var users = _repository.FindAllUsers();
return View(users);
}
Note how i left out the View name, so the default convention will be to look for a View based on the action name, in this case Users.cshtml. Always try and use convention over configuration - this is what makes MVC great.
#model IEnumerable<YourApp.Models.User>
#Html.DisplayForModel()
Shared\DisplayTemplates\User.cshtml
#Html.LabelFor(model => model.Name)
#Html.DisplayFor(model => model.Name)
#Html.LabelFor(model => model.LastName)
#Html.DisplayFor(model => model.LastName)
Note how i didn't use a foreach loop - i use i custom display template, again - convention over configuration.
Whatever you return as the second parameter is the model passed into the view. You could then inherit the razor page in WebViewPage, or better, use the #model keyword. The data passed in is available within the page via the "Model" variable.
Good example of usage after that is at http://weblogs.asp.net/scottgu/archive/2010/10/19/asp-net-mvc-3-new-model-directive-support-in-razor.aspx

Categories