I have a Model like so:
class Model
{
public String Text { get; set; }
public AnotherType[] Values { get; set; }
}
And a View that takes an IEnumerable<Model> that I am displaying in a WebGrid using Razor and asp.net MVC 4.
So far I have:
#{ var grid = new WebGrid(Model); }
<div id="grid">
#grid.GetHtml(
columns:
grid.Columns(
grid.Column("Text")))
</div>
What I would like is to create a list of comma separated list of links based on the value of Values in my model in the second column.
I have tried just creating a Linq query to project them into ActionLinks, (or even a String Array and use String.Join() to test it) but that doesn't compile at Runtime.
For example:
grid.Column("Values",
format: (item) => String.Join(item.Select(v => v.Property).ToArray()))))
The error I get is (I think):
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
I'd like my output to look like:
|--------------------------------|
|**Text** | **Values** |
|--------------------------------|
|Text A | Val 1, Val 2, Val 3 |
|--------------------------------|
Where Val n is a link that can navigate you away to view more detail.
Does anyone know of a way to do this?
As a workaround, I have combined the values in my Model and expose a String property with the result, but this doesn't allow me to create ActionLinks.
You have to cast the item (dynamic) as AnotherType
(item).Cast<AnotherType>()
Related
I'm just inquisitive if this is possible. If so, please help me.
Everything is already set and valid like the:
-Model.info:
public class info{
public string information{get;set;}
}
-ViewBag.Infos:
ViewBag.Infos = new SelectList(new []{"Option1","Option2"});
-I am not finding or encountering an error.
So it goes like this:
#foreach(var nom in Model.info)
{
#Html.DropDownList("Infos",new{#Value = "nom.information"})
}
Is it possible to put the foreach value of nom into each dropdownlist's value?
First error is that you are iterating over Model.info that is an string (is an iteration over every char, the type of nom would be char).
By the other hand, you must define a DropDownList with the ViewBag elements, something like that:
#Html.DropDownList("info", ViewBag.info, null, Model.info)
I have converted my MVC3 application to MVC5, I had to change all views to razor. Having a challenge with a select list:
In ASPX view that works I am using the following:
<select id="Profession" name="Profession" style="width: 235px; background-color: #FFFFCC;">
<% List<string> allProfessions = ViewBag.AllProfessions;
string selectedProfession;
if (Model != null && !String.IsNullOrEmpty(Model.Profession))
selectedProfession = Model.Profession;
else
selectedProfession = allProfessions[0];
foreach (var aProfession in allProfessions)
{
string selectedTextMark = aProfession == selectedProfession ? " selected=\"selected\"" : String.Empty;
Response.Write(string.Format("<option value=\"{0}\" {1}>{2}</option>", aProfession, selectedTextMark, aProfession));
}%>
</select>
In Razor I am using:
<select id="Profession" name="Profession" style="width: 235px; background-color: #FFFFCC;">
#{List<string> allProfessions = ViewBag.AllProfessions;
string selectedProfession;}
#{if (Model != null && !String.IsNullOrEmpty(Model.Profession))
{selectedProfession = Model.Profession;}
else {selectedProfession = allProfessions[0];}
}
#foreach (var aProfession in allProfessions)
{
string selectedTextMark = aProfession == selectedProfession ?
"selected=\"selected\"" : String.Empty;
Response.Write(string.Format("<option value=\"{0}\" {1}>{2}</option>",
aProfession, selectedTextMark, aProfession));
}
</select>
The list shows up at the top of the page, I can't figure out where is the problem. Would appreciate your assistance.
Don't create your dropdown manually like that. Just use:
#Html.DropDownListFor(m => m.Profession, ViewBag.AllProfessions, new { style = "..." })
UPDATE
I tried your solution but got this error: Extension method cannot by dynamically dispatched
And, that's why I despise ViewBag. I apologize, as my answer was a little generic. Html.DropDownList requires the list of options parameter to be an IEnumerable<SelectListItem>. Since ViewBag is a dynamic, the types of its members cannot be ascertained, so you must cast explicitly:
(IEnumerable<SelectListItem>)ViewBag.AllProfessions
However, your AllProfessions is a simple array, so that cast won't work when the value gets inserted at run-time, but that can be easily fixed by casting it to a List<string> and then converting the items with a Select:
((List<string>)ViewBag.AllProfessions).Select(m => new SelectListItem { Value = m, Text = m })
There again, you see why dynamics are not that great, as that syntax is rather awful. The way you should be handling this type of stuff is to use your model or, preferably, view model to do what it should do: hold domain logic. Add a property to hold your list of profession choices:
public IEnumerable<SelectListItem> ProfessionChoices { get; set; }
And then, in your controller action, populate this list before rendering the view:
var model = new YourViewModel();
...
model.ProfessionChoices = repository.GetAllProfessions().Select(m => new SelectListItem { Value = m.Name, Text = m.Name });
return View(model);
repository.GetAllProfessions() is shorthand for whatever you're using as the source of your list of professions, and the Name property is shorthand for how you get at the text value of the profession: you'll need to change that appropriately to match your scenario.
Then in your view, you just need to do:
#Html.DropDownListFor(m => m.Profession, Model.ProfessionChoices)
Given that you don't have this infrastructure already set up, it may seem like a lot to do just for a drop down list, and that's a reasonable thing to think. However, working in this way will keep your view lean, make maintenance tons easier, and best of all, keep everything strongly-typed so that if there's an issue, you find out at compile-time instead of run-time.
I believe it's happening because of the Response.Write. Try this:
#Html.Raw(string.Format("<option value=\"{0}\" {1}>{2}</option>", aProfession,
selectedTextMark, aProfession))
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.
have looked at Phil Haacks project on books at
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
which has been useful, but I have a mix of data types.
I use a modelview so that i can have a mix of objects, in this case: Order (ie order.id, order.date etc), Customer, SoilSamplingOrder and a list of SoilSamplingSubJobs which is like this [0].id, [0].field, [1].id, [1].field etc
Perhaps I should be using ICollection instead of List? I had problems getting UpdateModel to work so I used an extract from collection method. the first 4 method calls : orderRepository.FindOrder(id); etc give the model the original to be edited. but after this point i'm a little lost in how to update the subjobs. I hope i have delineated enough to make sense of the problem.
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
Order order = orderRepository.FindOrder(id);
Customer cust = orderRepository.FindCustomer(order.customer_id);
IList<SoilSamplingSubJob> sssj = orderRepository.FindSubOrders(id);
SoilSamplingOrder sso = orderRepository.FindSoilSampleOrder(id);
try
{
UpdateModel(order, collection.ToValueProvider());
UpdateModel(cust, collection.ToValueProvider());
UpdateModel(sso, collection.ToValueProvider());
IList<SoilSamplingSubJob> sssjs = orderRepository.extractSSSJ(collection);
foreach (var sj in sssjs)
UpdateModel(sso, collection.ToValueProvider());
orderRepository.Save();
return RedirectToAction("Details", new { id=order.order_id});
}
catch
{
return View();
}
}
I think you should work on developing a view model that reflects the data that you need to get back and create display/edit templates for that model that renders the view model using Phil Haack's methods for your lists of objects -- in this case, arrays of submodel classes. Let the model binding framework build the returned model (as a parameter) to your action, then reconstitute your domain models from the view model data. Brad Wilson has an excellent series of articles on templating that should be helpful.
I use IModelBinder on my complex classes. You don't need IModelBinder, but it will make your controller post codeblock look much cleaner. I'm using VB at the moment, but my class looks something like this for example:
Public Class CombinedRulesAndXmlRules : Implements IModelBinder
Public Rules As New Rules()
Public XmlRules As New XmlRules()
Public RequiredTemplates As New List(Of RequiredTemplates)
Public SearchCriteria As New List(Of SearchCriteriaList)
Public OptionalTemplates As New List(Of OptionalTemplates)
Public Questions As New List(Of Questions)
Public QATemplates As New List(Of QATemplates)
**Public Answers As New List(Of Answers)**
Now I don't use editor templates in my views, so to have your lists appear in the formcollection you have to add something like this in your view:
#For x As Integer = 0 To Model.Answers.Count - 1
Dim incr As Integer = x
#Html.HiddenFor(Function(model) model.Answers(incr).Answer)
#Html.HiddenFor(Function(model) model.Answers(incr).AnswerId)
#Html.HiddenFor(Function(model) model.Answers(incr).AnswerTemplateTag)
#Html.HiddenFor(Function(model) model.Answers(incr).Tag)
Next
When the view is submitted/posted, the model binder takes over before hitting the first line of code in your mvc post controller method. I then iterate through the actual formcollection and strip out the [#] using regex, because your formcollection will show your list items like this: Answers[0].Answer, Answers[0]AnswerId ,etc.:
For x As Integer = 1 To request.Form.Count - 1
keyname = request.Form.Keys(x)
Debug.Write(keyname)
val = request.Form(x).ToString()
'If keyname contains [#] strip it. it's a list item.
Dim pattern As String = "\[(\d+)\]"
Dim iterpattern As String = "\d+"
Dim rgx As New Regex(pattern)
Dim rgxiter As New Regex(iterpattern)
If Regex.IsMatch(keyname, pattern) Then
Dim match As Match = rgxiter.Match(keyname)
ListIteration = CInt(match.Value)
Dim result As String = rgx.Replace(keyname, "")
keyname = result
End If
The Select Case codeblock is next. So you already know you have a strong typed class in your model, so your select can look like this:
Select Case keyname
Case "Answers.Answer"
'add code here to add to your return list. What you
'get in the post controller is a fully populated class.
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
}) %>