A common way to fill a table is:
#For Each item In Model
Dim currentItem = item
#<tr>
<td>currentitem.Data1</td>
<td>currentitem.Data2</td>
<td>currentitem.Data3</td>
</tr>
Next
But for some reasons (to control the order of columns), I would like to refer to the fields by their string names, something like this:
#For Each item In Model
Dim currentItem = item
#<tr>
<td>currentitem("Data1").Value</td>
<td>currentitem("Data2").Value</td>
<td>currentitem("Data3").Value</td>
</tr>
Next
Is it somehow possible?
Ok - now that we have established that you need to change the order of your view model, let's do a little bit of work to your view model to make it easier to change the order. Given that we have to change the order of how we display things, we have to have a way to track what order to display them in. To change on the fly in the view without having different views with different orders (could get messy fast) - so let's just update our model a little bit.
Let assume this is your view model
public class OriginalViewModel{
public string Data1 {get;set;}
public string Data2 {get;set;}
public string Data3 {get;set;|
}
Let's create a new class called DataProperty. This will hold the data objects as well as allow us to set a display order that we can sort on later.
public class DataProperty{
public int DisplayOrder {get;set;}
public string Data {get;set;}
}
Now, let's update your view model to be a set of these DataProperties. We are using something we can enumerate and order by the display order.
public class UpdatedViewModel{
public IEnumerable<DataProperty> DataSet {get;set;}
}
Now, you can set the display order on each one of your property elements.
var listOfItems = new List<DataProperty>();
listOfItems.Add(new DataProperty{Data = "data1", DisplayOrder = 2});
listOfItems.Add(new DataProperty{Data = "data2", DisplayOrder = 3});
listOfItems.Add(new DataProperty{Data = "data3", DisplayOrder = 1});
var model = new UpdatedViewModel();
model.Data = listOfItems;
In your view, order by the display order property. Note we can now use Linq to order our data appropriately based on the requirements for this view.
#{foreach(var item in Model.OrderBy(x=>x.Data.DisplayOrder){
<tr>
<td>#item.Data</td>
<td>#item.Data</td>
<td>#item.Data</td>
</tr>
}}
which should render in HTML
<tr>
<td>data3</td>
<td>data1</td>
<td>data2</td>
</tr>
Thank you Tommy,
I tried to solve it in simple way and did this:
For example, I have a Viewdata that contains the order of the columns:
ViewData("columnsorder")="|3|1|2|"
and then, I just loop through ViewData and set the columns.
#For Each item In Model
Dim currentItem = item
#<tr>
#For i As Integer = 1 To ViewData("columnsorder").ToString.Split("|").Count - 2
Dim ind As Integer = ViewData("columnsorder").ToString.Split("|")(i)
Select Case ind
Case 1#<td>#currentItem.Data1</td>
Case 2#<td>#currentItem.Data2</td>
Case 3#<td>#currentItem.Data3</td>
End Select
Next
</tr>
Next
Related
(NUBE) I am trying to loop thru a view model and show rows when a field has a particular value. (IE In a Master/Detail model I have pulled all my orders in one list and all the detail in another. When the user picks the particular order from the first list I just want to iterate thru the detail list and show those line items for edit/delete purposes.
This code works from testing if I hardcode the value (2953) but I wish to pass a value and am not sure if Viewbag or razor is the way to go. I would like to maintain a consistent framework throughout.
#foreach (var row in Model.SplitTankAssignmentItems.Where(x => x.MeterValueID == 2953) ) // row.splitTicketID) )
{
<tr id="#row.SplitTankID>
:
:
:
</tr>
}
The ViewBag is exactly for this purpose.
On your control-action method you can set up the ViewBag and in your view you can read it.
Controller
public IActionResult Index()
{
:
ViewBag.ValueToUse = 2953
Return View(model);
}
View
#foreach (var row in Model.SplitTankAssignmentItems.Where(x => x.MeterValueID == (int)ViewBag.ValueToUse) ) // row.splitTicketID) )
{
<tr id="#row.SplitTankID>
:
:
:
</tr>
}
Notes: Remeber to cast your ViewBag property to the type you need, and keep in mind that ViewBag is only for one call, usually to take information from controller to view. It won't stay from view to controller neither from an action method to action method (RedirectTo)
I have a class, Farm, which contains a list of classes of type Animal.
public class Farm {
public list<Animal> Animals;
// other members..
}
public class Animal {
public string Name;
public string Family;
}
I would like to make a createOrEdit view for my Farm object, and I'd like to use DropDownLists for the Animal's Name and Family. The choices are coming from a database.
When I pass the Animal model to the view for editing I'd like to have the DropDownLists somehow match the properties for each animal and set the selected values of the lists.
I've tried lots of things like this:
#for(int i = 0; i < Model.Animals.Count; i++)
{
#Html.DropDownListFor(model => model.Animals[i].Name, Model.AnimalNames)
// where Model.AnimalNames is a SelectList
#Model.Animals[i].Name // (for testing) this properly displays the name I want to be selected in the list
}
I've seen a bunch of suggestions on this site for creating SelectLists in the controller, iterating through each item and setting the selected property where appropriate. But there's gotta be a cleaner way.. what if I have 100 Animals on my farm. It doesn't seem reasonable to create 200 SelectLists in the controller, iterate through each of them to match up the selected values, and then pass that to the view.
So, is there a simple way for me to take that Animal.Name[i] value and find its matching listitem in the DDL?
Thanks!!
Inside your foreach loop you can do:
#for(int i = 0; i < Model.Animals.Count; i++)
{
var original = Model.AnimalNames;
var animalSelected = new SelectList(original.Items, original.DataValueField, original.DataTextField, model.Animals[i].Name);
#Html.DropDownListFor(model => model.Animals[i].Name, animalSelected)
}
This way, in your controller you create just one SelectList with the AnimalNames... and in your view, you create SelectList with the selected value for each one.
We use this approach when using list of editable items with the need of display the current value of each item.
I have a table that contains a list of EquipmentIDs and another table that has maintenance records.
When the user edits a maintenance record I want there to be a drop down list of all of the equipment IDs from the table.
The dropdown list populates, and it populates with the correct amount of entries, however they all say System.Web.MVC.SelectListItem instead of the value of the ID.
Here is the code that generates the list:
public ActionResult Edit(int id)
{
MaintPerformed maintPerformed = maintPerformedRepository.GetMaintPerformed(id);
IList<EquipmentID> IDs = equipmentIDRepository.GetEquipmentIDAsList();
IEnumerable<SelectListItem> selectEquipList =
from c in IDs
select new SelectListItem
{
//Selected = (c.EquipID == maintPerformed.EquipID),
Text = c.EquipID,
Value = c.Sort.ToString()
};
ViewData["EquipIDs"] = new SelectList(selectEquipList, maintPerformed.ID);
return View(maintPerformed);
}
Here is the entry in the .aspx page for the Dropdown list:
%: Html.DropDownList("EquipIDs") %>
Here is how I am generating the list from the table:
public List<EquipmentID> GetEquipmentIDAsList()
{
return db.EquipmentIDs.ToList();
}
It appears that everything is working correctly with the exception of assigning the text to be displayed in the drop down box.
What am I missing or not thinking correctly about?
SelectList and SelectListItem are actually mutually exclusive. You should be using one or the other. Etiher pass the constructor of SelectList your raw data (IDs) or don't use SelectList at all and just make ViewData["EquipIDs"] your enumerable of SelectListItem. If you go with the latter approach, you will have to tweak your code so that you are setting the selected item in the constructor of SelectListItem (as you had done, but commented out).
Either:
ViewData["EquipIDs"] = new SelectList(IDs, maintPerformed.ID, "EquipID", "Sort");
Or:
IEnumerable<SelectListItem> selectEquipList =
from c in IDs
select new SelectListItem
{
Selected = c.EquipID == maintPerformed.EquipID,
Text = c.EquipID,
Value = c.Sort.ToString()
};
ViewData["EquipIDs"] = selectEquipList;
All,
I've read through a lot of posts about Checkboxes and ASP.MVC but I'm not that much wiser.
My scenario:
I have a strongly typed View where I pass a collection of summary objects to the view for rendering in a for-each. This summary object contains label data based on a unique id. I also add a checkbox to the row so do so via:
<td>
<%= Html.CheckBox("markedItem", Model.MarkedItem, new { TrackedItemId = Model.Id })%>
</td>
When I perform a POST to get the submitted results my action method takes the strongly typed ViewModel back but the original summary object that I used to create the list is not populated.
Ok, this is annoying, but I can understand why so I'll live with it.
What I then do is to add a new property to my ViewModel called "MarkedItem" which is a string collection.
On postback this marked item is filled with the before and after states if the checkbox has changed but nothing to tell me which key they were for. Just to clarify, if I send this
TrackedItemId = A, Value = false
TrackedItemId = B, Value = true
TrackedItemId = C, Value = false
and set the page to this:
TrackedItemId = A, Value = true
TrackedItemId = B, Value = true
TrackedItemId = C, Value = false
I will get back this:
MarkedItem[0] = true
MarkedItem[1] = false
MarkedItem[2] = true
MarkedItem[3] = false
in other words [0] is the new value and [1] is the old value, [2] and [3] represent values that haven't changed.
My questions are:
Is this right - that I get before and after in this way? Is there any way to only send the latest values?
How can I get hold of the custom attribute (TrackedItemId) that I've added so that I can add meaning to the string array that is returned?
So far I like MVC but it not handling simple stuff like this is really confusing. I'm also a javascript noob so I really hope that isn't the answer as I'd like to return the data in my custom viewmodel.
Please make any explanations/advice simple :)
<p>
<label>
Select project members:</label>
<ul>
<% foreach (var user in this.Model.Users)
{ %>
<li>
<%= this.Html.CheckBox("Member" + user.UserId, this.Model.Project.IsUserInMembers(user.UserId)) %><label
for="Member<%= user.UserId %>" class="inline"><%= user.Name%></label></li>
<% } %></ul>
and in the controller:
// update project members
foreach (var key in collection.Keys)
{
if (key.ToString().StartsWith("Member"))
{
int userId = int.Parse(key.ToString().Replace("Member", ""));
if (collection[key.ToString()].Contains("true"))
this.ProjectRepository.AddMemberToProject(id, userId);
else
this.ProjectRepository.DeleteMemberFromProject(id, userId);
}
}
With thanks to Pino :)
ok, one hack I've come up with - I really hate that I have to do this but I don't see another way round it and I'm sure it will break at some point.
I've already implemented by own ModelBinder to get round some other issues (classes as properties for example) so have extended it to incorporate this code. We use Guid's for all our keys.
If there are any alternatives to the below then please let me know.
Html
<%= Html.CheckBox("markedItem" + Model.Id, false)%>
C#
(GuidLength is a const int = 36, Left and Right are our own string extensions)
//Correct checkbox values - pull all the values back from the context that might be from a checkbox. If we can parse a Guid then we assume
//its a checkbox value and attempt to match up the model. This assumes the model will be expecting a dictionary to receive the key and
//boolean value and deals with several sets of checkboxes in the same page
//TODO: Model Validation - I don't think validation will be fired by this. Need to reapply model validation after properties have been set?
Dictionary<string, Dictionary<Guid, bool>> checkBoxItems = new Dictionary<string, Dictionary<Guid, bool>>();
foreach (var item in bindingContext.ValueProvider.Where(k => k.Key.Length > GuidLength))
{
Regex guidRegEx = new Regex(#"^(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$");
if (guidRegEx.IsMatch(item.Key.Right(GuidLength)))
{
Guid entityKey = new Guid(item.Key.Right(GuidLength));
string modelKey = item.Key.Left(item.Key.Length - GuidLength);
Dictionary<Guid, bool> checkedValues = null;
if (!checkBoxItems.TryGetValue(modelKey, out checkedValues))
{
checkedValues = new Dictionary<Guid, bool>();
checkBoxItems.Add(modelKey, checkedValues);
}
//The assumption is that we will always get 1 or 2 values. 1 means the contents have not changed, 2 means the contents have changed
//and, so far, the first position has always contained the latest value
checkedValues.Add(entityKey, Convert.ToBoolean(((string[])item.Value.RawValue).First()));
}
}
foreach (var item in checkBoxItems)
{
PropertyInfo info = model.GetType().GetProperty(item.Key,
BindingFlags.IgnoreCase |
BindingFlags.Public |
BindingFlags.Instance);
info.SetValue(model, item.Value, null);
}
I'm trying to use a SelectList one of my views, and its just not populating correctly. It gets the proper number of entries (4), but they all read System.Web.Mvc.SelectListItem. I fired up the debugger on the code, and saw some strangeness going on. I must be doing something wrong, but I don't quite see what.
Code from the ViewModel:
public SelectList DeviceTypes {get; private set;}
....
var device_types = DataTableHelpers.DeviceTypes();
IEnumerable<SelectListItem> sl = device_types.Select(
dt => new SelectListItem { Selected = (dt.DeviceType == 1),
Text = dt.Description,
Value = dt.DeviceType.ToString() }).ToList();
DeviceTypes = new SelectList(sl);
And code from the View:
<%= Html.DropDownList("Type",Model.DeviceTypes) %>
So, when I look at this in the debugger, the sl IEnumerable is getting built correctly. I can see all 4 elements in there, with the proper Text and Value property values. Once I call the SelectList constructor however, if I expand the IEnumerable that it contains, I see that it has 4 entries, but all the data in them has been lost. The Text is set to System.Web.Mvc.SelectListItem, and the value is null.
Ive tried changing the ToList() call to a ToArray(), as well as removing it entirely. That didn't change the behaviour.
What am I doing wrong here?
EDIT: Scratch my first answer.
You should be passing the IEnumerable list if items to the View, not trying to construct a Html item in the controller.
Code for controller:
public IEnumberable<YourModel> DeviceTypes {get; internal set;}
....
DeviceTypes = DataTableHelpers.DeviceTypes();
Code for View:
<%= Html.DropDownList("Type", from dt in Model.DeviceTypes
select new SelectListItem
{
Text = dt.Description,
Value = dt.DeviceType.ToString(),
Selected = dt.DeviceType == 1
}) %>