Pre-select object properties using lambda expressions, then display in MVC? - asp.net-mvc

Currently in MVC, we have to define columns manually whenever we want to list out items:
<tr>
<td>
#Model.Name
</td>
<td>
#Model.Age
</td>
<td>
#Model.Gender
</td>
</tr>
What I want to do however, is to have a ViewModel where we specify which columns should be used, something like:
var model = new PersonViewModel(
// List of persons
CollectionOfPersons,
// List of columns that we want to display
new Expression<Func<Person, object>>[]
{
x => x.Name,
x => x.Age,
x => x.Gender
});
Then in our view, all I need to do is:
<tr>
// Model.Predicates is our ViewModel's selected columns
#foreach(var predicate in Model.Predicates)
{
<td>
// This would basically loop each pre-defined lambda expression in our ViewModel
#Html.DisplayFor(predicate)
</td>
}
</tr>
I have to admit I have a very weak understanding of expressions, and I have so far been unsuccessful in looking for information. Does anyone have any idea if this is possible to do?

Seems like the html helpers don't work like that (passing in the expression as a variable). The compiler explicitly needs an expression to be able to determine the TModel and TResult types to be able to work.

Related

Show different button based upon ViewBags passed to Views from Controller?

I have created two ViewBags which contain different values.
ViewBag.attendingEvents = attendingIds;
ViewBag.notAttendingEvent = notAttendingIds;
The attendingIds contain the ids of all the users which are attending the event whereas notAttendingIds contain the ids of all the users which are not attending the event.
It's showing me too many button since I am looping through. If there are 100 users attending the event then its showing me 100 Remove buttons with one record because the loop. What's the best approach to solve this problem?
#foreach (var user in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => user.Name)
</td>
<td>
#foreach(var record in ViewBag.attendingEvents as IList<User>) { Remove }
</td>
</tr>
}
Your Problem:
It's showing me too many button since I am looping through. If there are 100 users attending the event then its showing me 100 Remove buttons with one record because the loop. What's the best approach to solve this problem?
Best approach or solution:
You are getting the above problem, because there is a foreach to put the button, actually its not neccessary, you simply change your code like below
#foreach (var user in Model.User)
{
<tr>
<td>
#Html.DisplayFor(modelItem => user.Name)
</td>
<td>
string[] Users_attendingEvents = string[](ViewBag.attendingEvents);
#if(Users_attendingEvents.Contains(user.Id))
{
Remove
}
</td>
</tr>
}
you may also take at look at this link for additional info :how to use .contains in Viewbag in asp.net mvc
Hope it helps
Thanks
This is my solution if I have understood your question correct and if you want only those users who attend the meeting to be able to have the remove button. You need to loop through the result of users and check if they exist in your attendable id list.
#foreach (var user in Model.User)
{
<tr>
<td>
#Html.DisplayFor(modelItem => user.Name)
</td>
<td>
#if(ViewBag.attendingEvents.Contains(user.Id))
{ Remove }
</td>
</tr>
}

How to read DropDownList value with HTML Helper when using #Html.PagedListPager MVC

I have the following code in my view:
<table>
<tr>
<td>
Number of Records to Show:
</td>
<td>
#Html.DropDownListFor(m=>m.NumRecsToShow, Enumerable.Range(1, 100).Select(i => new SelectListItem {Text = i.ToString(), Value = i.ToString()}))
</td>
</tr>
<tr>
#Html.PagedListPager((IPagedList) Model.lp, page => Url.Action("List", new RouteValueDictionary()
{
{"Page",page},
{"Recs",?????}
}))
</tr>
</table>
What I need to do is read the new selected value and pass it back as a "Recs" parameter, so I can return the right number of Records. But I dont know how to read the selected value when the PagedListPager number is clicked on in the table. I would love to know of some #Html helper (Where the ???? is in the code above.) that can read the value when I need it, but I am starting to think that I might need to use JQuery. Of course the other route would be if I can use the DropdownList to change the model data then I can just pass back the model to the controller, but the dropdownlist doesnt seem to get read unless I do a form submit.
Any pointing me in the right direction is appreciated.

Performaing operations on model members

I have a view that is using a model. I just need to do a simple subtraction on the model variables using razor.
Here is what the code looks like:
#model Inquiry.Inq_Weights
<table id="mainWeights">
<tr>
<td>#Html.DisplayFor(model => Model.HotScaleHalfId)</td>
<td>#Html.DisplayFor(model => Model.HotScaleGrossWeight)</td>
<td>#Html.DisplayFor(model => Model.HotScaleTareWeight)</td>
</tr>
<tr>
<td><strong>Total Hot Scale Weight</strong></td>
</tr>
<tr>
<td align="right">(#Model.HotScaleGrossWeight - #Model.HotScaleTareWeight;)</td>
</tr>
</table>
I am trying the (#Model.HotScaleGrossWeight - #Model.HotScaleTareWeight;)
but it is just displaying "0 - 0". The zeros are correct at this point. but i don't want it to display the expression, just the result of that operation.
I have also tried using a variable then listing that, as in
#{
var netWeight = #Model.HotScaleGrossWeight - #Model.HotScaleTareWeight;
netWeight;
}
But that doesn't work either. How can I do simple math on model members?
You should do this:
#(Model.HotScaleGrossWeight - Model.HotScaleTareWeight)
With your other example:
#{
var netWeight = Model.HotScaleGrossWeight - Model.HotScaleTareWeight;
}
Then use it with #netWeight. Please note that the # symbol is razor specific, you don't have prefix your variables with it inside a c# expression. When you write # you are actually saying that you are going to write a c# expression. Writing # before Model is just for the razor engine to know that this is going to be a c# expression (your variable actually) and not a simple text 'Model'.
But I recommend to do this kind of stuff within your controller.
update
as Alexei wrote in comment # is not just for razor: What does the # symbol before a variable name mean in C#?

can i fill model's fields by tds without javascript

is it possible i fill model's fields by content of table's tds. of course without using javascript .
i wanna pass a model to view and get content of tds .
some thing like text box : #Html.TextBoxFor(m => m.username)
is it possible i have some thing like this for tds? what can i put in place of tds?
<tr class="darckTr">
<td>code :</td>
<td id="tdPobox" colspan="3">12345</td>
<td>Email :</td>
<td id="tdEmail">example#yahoo.com</td>
</tr>
It sounds like what you want is a display template. Create a .cshtml view, for example "ContactDetails.cshtml":
#model ContactDetails
<tr class="darckTr">
<td>code :</td>
<td id="tdPobox" colspan="3">#Model.PoBox</td>
<td>Email :</td>
<td id="tdEmail">#Model.Email</td>
</tr>
If the filename of your partial view matches the type in your model, it will be used automatically. Otherwise, you have a couple of options. Either specify the partial on your model, e.g.:
public class MyWrappingClass
{
[UIHint("_ContactDetails")]
public ContactDetails Details { get; set; }
}
And then do this in your view:
#model MyWrappingClass
#Html.DisplayFor(m => m.Details)
Or, just specify the template directly in your view:
#model MyWrappingClass
#Html.DisplayFor(m => m.Details, "_ContactDetails")
Brad Wilson wrote a good blog post on templates, including how they are resolved, here:
http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-3-default-templates.html
Edit
If you want to persist these values back on POST, you need to have an input element containing the values. In your case, the best way to do this would be to use a hidden field, for example:
#Html.HiddenFor(m => m.Username)

Why does this lambda expression work?

I found this Related Topic, but it failed to answer my question.
When automatically creating a strongly typed view, lets say with a List scaffolding template, I will get something roughly like this:
#model IEnumerable<Test.Models.abc>
<table>
<tr>
<th>
#Html.DisplayNameFor(model => model.ID)
</th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.ID)
</td>
</tr>
}
</table>
I understand #Html.DisplayNameFor(model => model.ID) completely, but not #Html.DisplayFor(modelItem => item.ID).
What is the purpose of modelItem? Replacing it with any arbitrary text will result in a functional web page. It seems that modelItem is just a throwaway word. I guess my real question is why doesn't this work?
#Html.DisplayFor(item => item.ID)
Edit
A good point was brought up in the comments. It seems you are also able to change model to anything so long as you change it on both sides of the lambda expression:
#Html.DisplayNameFor(abc => abc.ID)
A side question would be: How does the #model statement at the top affect the functions below? I had previously thought model referenced the #model expression in order to figure out the display name via the class, but the page still works after the mentioned changes.
It does not work because item is already declared in outer scope in foreach.
#foreach (var item in Model).
The reason why modelItem lambda is not used is because of that iteration of IEnumerable. If there would be Test.Models.abc as model instead of IEnumerable, then would that lambda does matter and the code of DisplayFor would change to #Html.DisplayFor(m => m.ItemId).
Update
#model IEnumerable<Test.Models.abc> just declares that this view is strongly typed, with type of IEnumerable<Test.Models.abc> - in your case. That means that view property this.Model (not model) is of type IEnumerable<Test.Models.abc> - and also that model of that type should passed into this view. model in example above is just expression variable - it has scope just for that expression. You can change it's name to any unprotected legal variable name that was not already used in outer scope (that's why it should not be named Model, because it would hide the Model property already declared in view).
item is already a variable defined in an outer scope. You are capturing it as a closure, which is not ideal here. What your code should read is:
#Html.DisplayFor(modelItem => modelItem.ID)

Resources