I have a class called Entity
public class Entity
{
public string Name { get; set; }
public Location Place { get; set; }
}
and one class called Location
public class Location
{
public string Country { get; set; }
public string State { get; set; }
public string City { get; set; }
}
One Entity contains a Location, so I want to generate 3 dropdowns for Location.
Country
State
City
I could do it manually like
#Html.DropDownListFor(o => o.Country, new [] { new SelectListItem() { Text = "United States", Value="US" } })
<br />
#Html.DropDownListFor(o => o.State, new [] { new SelectListItem() { Text = "Some State", Value="SS" } })
<br />
#Html.DropDownListFor(o => o.City, new[] { new SelectListItem() { Text = "Some city", Value = "City" } })
But I have several places on my website that will need the exact same 3 dropdowns, like Restaurant, Hotel and other classes that also have a Location. I've tried to make a partial view that starts a new form, but I get an exception:
The model item passed into the dictionary is of type 'TestMVC3Razor.Controllers.Entity', but this dictionary requires a model item of type 'TestMVC3Razor.Controllers.Location', with this code:
#model TestMVC3Razor.Controllers.Entity
#using (Html.BeginForm())
{
#Html.Partial("LocationSelector", Model.Place)
<br />
<input type="submit" value="Submit" />
}
And the partial view is
#model TestMVC3Razor.Controllers.Location
#using (Html.BeginForm())
{
#Html.DropDownListFor(o => o.Country, new [] { new SelectListItem() { Text = "United States", Value="US" } })
<br />
#Html.DropDownListFor(o => o.State, new [] { new SelectListItem() { Text = "Some State", Value="SS" } })
<br />
#Html.DropDownListFor(o => o.City, new[] { new SelectListItem() { Text = "Some city", Value = "City" } })
}
This obviously shouldn't work, but I want to do something like it, a helper like this would be perfect
#Html.LocationSelectFor(o => o.Location)
But how do I do this? I need to generate 3 dropdowns and when I post to an action I need to get the object with bidden values.
public ActionResult(Location loc)
{
var x = String.Format("{0}, {1} - {2}", loc.City, loc.Country, loc.State);
}
How can I make this helper to create 3 dropdowns and bind values when I post?
Just create your own extension off of HtmlHelper:
public static HtmlHelperExtensions {
public static MvcString LocationSelectFor<TModel, TProperty>(this HtmlHelper<TModel> helper, System.Linq.Expressions.Expression<Func<TModel,TProperty>> expression) {
// examine expression and build html
}
}
The trick is looking at the expression. This blog post should get you started: http://geekswithblogs.net/Madman/archive/2008/06/27/faster-reflection-using-expression-trees.aspx
Alternately, you can create an EditorTemplate for your Location class. Just google for asp.net mvc editortemplate. http://www.codecapers.com/post/Display-and-Editor-Templates-in-ASPNET-MVC-2.aspx
Personally I would stick with EditorTemplates, as you can change the view without the need to recompile typically.
You can follow this example to use expression and expression body
Get Custom Attributes from Lambda Property Expression
Or just use string expression and manipulate that as here http://www.joelscode.com/post/Use-MVC-Templates-with-Dynamic-Members-with-custom-HtmlHelper-Extensions.aspx
Following xixonia little hints I got what I needed.
#Html.EditorFor(o => o.Place, "LocationSelector",
new CreateLocation{ Country = "US", State = "A", City = "Y" })
And I have a template under
Views
|- Shared
|- EditorTemplates
LocationSelector.cshtml
#model TestMVC3Razor.Controllers.CreateLocation
#using TestMVC3Razor.Controllers
#Html.DropDownListFor(o => o.Country, Model.CountryList)
<br />
#Html.DropDownListFor(o => o.State, Model.StateList)
<br />
#Html.DropDownListFor(o => o.City, Model.CityList)
And then I made
public class CreateEntity
{
[Required]
public string Name { get; set; }
public CreateLocation Place { get; set; }
}
public class CreateLocation
{
public CreateLocation(Location location = null)
{
if (location != null)
{
Country = location.Country;
State = location.State;
City = location.City;
}
}
public string Country { get; set; }
public string State { get; set; }
public string City { get; set; }
public IEnumerable<SelectListItem> CountryList
{
get
{
var list = new[]
{
new SelectListItem() { Text = "US", Value = "US" },
new SelectListItem() { Text = "BR", Value = "BR" },
new SelectListItem() { Text = "ES", Value = "ES" },
};
var selected = list.FirstOrDefault(o => o.Value == Country);
if (selected != null)
{
selected.Selected = true;
}
return list;
}
}
public IEnumerable<SelectListItem> StateList
{
get
{
var list = new[]
{
new SelectListItem() { Text = "A", Value = "A" },
new SelectListItem() { Text = "B", Value = "B" },
new SelectListItem() { Text = "C", Value = "C" },
};
var selected = list.FirstOrDefault(o => o.Value == State);
if (selected != null)
{
selected.Selected = true;
}
return list;
}
}
public IEnumerable<SelectListItem> CityList
{
get
{
var list = new[]
{
new SelectListItem() { Text = "X", Value = "X" },
new SelectListItem() { Text = "Y", Value = "Y" },
new SelectListItem() { Text = "Z", Value = "Z" },
};
var selected = list.FirstOrDefault(o => o.Value == City);
if (selected != null)
{
selected.Selected = true;
}
return list;
}
}
}
And my controller
public class HomeController : Controller
{
public ActionResult Index()
{
// can load data for edit
return View(new CreateEntity { Place = new CreateLocation(TempData["Location"] as Location) });
}
[HttpPost]
public ActionResult Index(Entity ent)
{
var loc = ent.Place;
var x = String.Format("{0} {1} {2}", loc.Country, loc.State, loc.City);
ViewBag.Result = x; // display selected values
TempData["Location"] = loc;
return Index();
}
}
I don't know if it is the best solution, but at least I can call
#Html.EditorFor(o => o.Place, "LocationSelector", obj)
from any place and have a default place selector on my website.
Related
I have the below code which pass 2 values (ProductId and ItemName) to SelectList (Dropdownlist) located on the View.
I need a way to pass 3 values (ProductId, Price, and ItemName).
public class Product
{
public int ProductId { get; set; }
public int Price { get; set; }
public string ItemName { get; set; }
public IList<ProductInvoice> ProductInvoices { get; set; }
}
public IActionResult Create()
{
IEnumerable<SelectListItem> items = _context.Products.Select(c => new SelectListItem
{
Value = c.ProductId.ToString() + "-" + c.Price,
Text = c.ItemName
});
ViewData["Products"] = items;
return View();
}
Code to generate drop down list:
#Html.DropDownList("SelectedProducts",new SelectList((System.Collections.IEnumerable)ViewData["Products"], "Value", "Text", new { #class = "form-control dropdown-list" }))
You can join every property you need by a delimiter and pass it to Value. Like this:
IEnumerable<SelectListItem> items = _context.Products.Select(c => new SelectListItem
{
Value = String.Join("$", new string[] { c.ProductId.ToString(), c.Price }),
Text = c.ItemName
});
and when fetching back, you can perform a split with the delimiter. Like this:
var myVal = value.Split("$");
string productId = myVal[0];
string price = myVal[1];
You made almost right thing, only if you want to see the price you have to concat it with Name, not with Id
public IActionResult Create()
{
var selectListItems= _context.Products.Select(c => new SelectListItem
{
Value = c.ProductId.ToString(),
Text = c.ItemName+ " - " + c.Price.ToString()
});
var items= new SelectList( selectListItems,"Value", "Text");
ViewData["Products"] = items;
var model=new Product();
return View( model);
}
and fix the view
#Html.DropDownListFor(m=> m.ProductId, (SelectList) ViewData["Products"], new { #class = "form-control dropdown-list" }))
How do you pass a value from razor into a C# class?
I want to pass the value from autoCreateSoldPart into the soldPartSetting, but I am not sure how to do that.
Razor
<p>
#Html.Label("Auto recreate sold part", new { #for= "autoCreateSoldPart" })
#{
var lstSelect = new List<SelectListItem>()
{
new SelectListItem() { Text = "Yes", Value = "true" },
new SelectListItem() { Text = "No", Value = "false" }
};
lstSelect.ForEach(o =>
{
if (o.Value == System.Configuration.ConfigurationManager.AppSettings["ReCreateSoldPart"])
{
o.Selected = true;
}
});
}
#Html.DropDownList("autoCreateSoldPart", lstSelect, new { #class = "form-control" })
</p>
Controller for razor
public class HomeController : Controller
{
public ActionResult Settings()
{
ViewBag.Message = return View();
}
}
C#
public class PartBLL
{
public static int AssignPartToShelf(string action, string userName, string itemNumber, string shelfNumber, string userToken)
{
if (soldPartSetting == true)
{
partService.ServiceInstance.UndoSoldItem(part.ID.Value, userToken);
return 0;
}
}
}
First of all, I'm a beginner in what concerns C#, Razor, ASP.NET... I want to make a 'DropDownListFor' for the genders with a 'List' but the problem is that I get the errormessage as described in the title. Here is my code:
Model
namespace Hoofdstuk5_MvcStudentForm1.Models.ViewModels
{
public class StudentViewModel
{
public string Geslacht { get; set; }
}
}
Controller
namespace Hoofdstuk5_MvcStudentForm1.Controllers
{
public class StudentController : Controller
{
// GET: Student/Index
public ActionResult Index()
{
return View();
}
// GET: Student/Index
[HttpPost]
public ActionResult Index(string groep)
{
ViewBag.Geslacht = new List<SelectListItem>
{
new SelectListItem { Text = "Female", Value = "1" },
new SelectListItem { Text = "Male", Value = "2" }
};
return View(groep);
}
}
}
View
#Html.DropDownListFor(model => model.Geslacht, ViewBag.Geslacht as IEnumerable<SelectListItem>, "--- Select ---", new { htmlAttributes = new { #class = "form-control" } })
#Html.DropDownListFor(model => model.Geslacht, new List<SelectListItem> {
new SelectListItem { Text = "Male", Value = "1" },
new SelectListItem { Text = "Female", Value = "2" }
}, "--- Select ---", new { htmlAttributes = new { #class = "form-control" } })
#Html.DropDownListFor(model => model.Geslacht, null, "--- Select ---", new { htmlAttributes = new { #class = "form-control" } })
The second 'DropDownListFor' works, but the first and third has to be from the controller, but it doesn't work and I don't know why? Can somebody help me please?
The error means that the 2nd parameter of DropDownListFor() (IEnumerable<SelectListItem>) is null and therefore the method expects that the first parameter is IEnumerable<SelectListItem> (which it's not)
In the first example, the value of ViewBag.Geslacht is null because you did not populate it in the GET method (although you do populate it in the POST method). Add ViewBag.Geslacht = new List<SelectListItem>(...) to the GET method.
In the 3rd example, you secifically set the 2nd parameter to null
You are sending a string to your view as the model:
return View(groep);
Therefore, you are getting that error. You have to pass a model to your view. Create a model like this:
// Give this a better name
public class SomeModel
{
public List<SelectListItem> Geslacht { get; set; }
}
And then change your controller code to this:
// GET: Student/Index
[HttpPost]
public ActionResult Index(string groep)
{
var model = new SomeModel();
model.Geslacht = new List<SelectListItem>
{
new SelectListItem { Text = "Female", Value = "1" },
new SelectListItem { Text = "Male", Value = "2" }
};
return View(model);
}
If you need more things then just add more properties to your model.
I would suggest you change a bit your model:
public class StudentViewModel
{
public string Geslacht { get; set; }
public IEnumerable<SelectListItem> Geslachts { get; set; }
public StudentViewModel()
{
Geslachts = GetGeslachts();
}
public StudentViewModel(string geslacht)
{
Geslacht = geslacht;
Geslachts = GetGeslachts();
}
private IEnumerable<SelectListItem> GetGeslachts()
{
return new List<SelectListItem>
{
new SelectListItem
{
Text = "Select ---",
Value = string.Empty
},
new SelectListItem
{
Text = "Male",
Value = "1"
},
new SelectListItem
{
Text = "Female",
Value = "2"
}
}
}
}
Then you could change your controller like below:
public class StudentController : Controller
{
// GET: Student/Index
public ActionResult Index()
{
return View(new StudentViewModel());
}
// GET: Student/Index
[HttpPost]
public ActionResult Index(string groep)
{
return View(new studentViewModel(groep));
}
}
Doing this changes entails that you should also change your View, to accept a StudentViewModel. Now at your view you could have the following:
#model oofdstuk5_MvcStudentForm1.Models.ViewModels.StudentViewModel
#Html.DropDownListFor(model=>model.Geslacht, Model.Geslachts, new { htmlAttributes = new { #class = "form-control" } )
I am trying to create a drop down list but it gives me an error saying 'Cannot implicitly convert type 'string' to 'System.Web.Mvc.SelectList'. My code is below:
Application Database Model:
public string dropdown{ get; set; }
Application View Model:
public SelectList dropdown{ get; set; }
ApplicationService.cs:
public static SelectList GetDropdownList(string currSelection)
{
List<SelectListItem> list = new List<SelectListItem>();
list.Add(new SelectListItem { Value = "1", Text = "firstvalue" });
list.Add(new SelectListItem { Value = "2", Text = "secondvalure" });
list.Add(new SelectListItem { Value = "3", Text = "All of the Above" });
return new SelectList(list, "Value", "Text", currSelection);
}
in my controller i am calling:
applicationviewmodel.dropdown= ApplicationService.GetDropdownList(null);
and then trying to save it in database as:
ApplicationDatabaseModel.dropdown= applicationviewmodel.dropdown;
This is where i get this error.
In my view i have:
#Html.DropDownListFor(x => x.dropdown, applicationviewmodel.dropdown)
I am not sure how to make this work.
I find it's easier to just have a List as part of your model and use a simple linq statement. Simple example below for a countries drop down:
assuming you have a model like
public class MyModel()
{
public int CountryId { get; set; }
public List<Country> Countries { get; set; }
}
and a Country class of
public class Country()
{
public int Id { get; set; }
public string Name { get; set; }
}
in your view you can then do the following:
#Html.DropDownListFor(m => m.CountryId,
Model.Countries.Select(x =>
new SelectListItem { Text = x.Name, Value = x.Id.ToString(), Selected = Model.CountryId == x.Id }, "Please Select...", null)
This:
#Html.DropDownListFor(x => x.dropdown, applicationviewmodel.dropdown)
..is incorrect. It is trying to store the selected item into the SelectList instance.
What you want is a string variable on the view model that this value is selected into:
public class ApplicationViewModel {
public SelectList DropDown { get; set; }
public string SelectedDropDownValue { get; set; }
// .. the rest of the properties here
}
Then your view becomes this:
#Html.DropDownListFor(x => x.SelectedDropDownValue, Model.DropDown)
This says "store the selected value into SelectedDropDownValue".
Then, you need to change how you build your SelectList. Value is what gets posted to your property.. Text is what is displayed in the browser.
So this:
list.Add(new SelectListItem { Value = "1", Text = "firstvalue" });
list.Add(new SelectListItem { Value = "2", Text = "secondvalure" });
list.Add(new SelectListItem { Value = "3", Text = "All of the Above" });
..has to be this:
list.Add(new SelectListItem { Value = "firstvalue", Text = "firstvalue" });
list.Add(new SelectListItem { Value = "secondvalue", Text = "secondvalure" });
list.Add(new SelectListItem { Value = "all of the above", Text = "All of the Above" });
..because they are strings (unless of course, you want the numbers to be posted back).
Then, finally, your controller code becomes this:
// assign the string value to the string property
ApplicationDatabaseModel.dropdown = applicationviewmodel.SelectedDropDownValue;
Need some advice I am trying to write a validator which only fires when a specific value in a dropdownlist is selected.
I have two drop down lists on this form one for Countries and the other for US States, the States dropdownlist only shows when the United States is selected from the Countries dropdownlist.
I need a validator that makes the State dropdownlist list a required field only if the United States is selected as a country.
As background information this a MVC3 Web Application and the show/hide code for the States dropdownlist is JQuery.
Another alternative is to dynamically add the rule to jQuery for validation.
You would need to check on the server side this custom logic as well however.
You can do this in your controller, or ideally your VieWModel would implement IValidateableObject to check if country="usa" then county is required.
Use jQuery's .rules.add and .remove:
http://docs.jquery.com/Plugins/Validation/rules#.22remove.22rules
So you could do something along the lines of:
$(document).ready(function() {
$("#country").change(function(){
if($(this).val()=="usa")
{
$("#yourCountyDropDown").rules("add", {
required: true,
messages: {
required: "County is required"
}
});
}
else
{
$("#yourCountyDropDown").rules("remove");
}
});
});
and for your ViewModel
public class WhateverYourObjectNameCreateViewModel : IValidatableObject
{
#region Validation
public IEnumerable Validate(ValidationContext validationContext)
{
if (this.Country=="USA" && string.IsNullOrEmpty(this.County))
{
yield return new ValidationResult("County is required");
}
}
#endregion
}
You could write a custom validation attribute:
public class RequiredIfAttribute : ValidationAttribute
{
public RequiredIfAttribute(string otherProperty, object otherPropertyValue)
{
OtherProperty = otherProperty;
OtherPropertyValue = otherPropertyValue;
}
public string OtherProperty { get; private set; }
public object OtherPropertyValue { get; private set; }
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var property = validationContext.ObjectType.GetProperty(OtherProperty);
if (property == null)
{
return new ValidationResult(string.Format("Unknown property: {0}", OtherProperty));
}
object otherPropertyValue = property.GetValue(validationContext.ObjectInstance, null);
if (!object.Equals(OtherPropertyValue, otherPropertyValue))
{
return null;
}
if (value != null)
{
return null;
}
return new ValidationResult(this.FormatErrorMessage(validationContext.DisplayName));
}
}
Now you could have a view model:
public class MyViewModel
{
public string Country { get; set; }
[RequiredIf("Country", "usa", ErrorMessage = "Please select a state")]
public string State { get; set; }
public IEnumerable<SelectListItem> Countries
{
get
{
return new[]
{
new SelectListItem { Value = "fr", Text = "France" },
new SelectListItem { Value = "usa", Text = "United States" },
new SelectListItem { Value = "spa", Text = "Spain" },
};
}
}
public IEnumerable<SelectListItem> States
{
get
{
return new[]
{
new SelectListItem { Value = "al", Text = "Alabama" },
new SelectListItem { Value = "ak", Text = "Alaska" },
new SelectListItem { Value = "az", Text = "Arizona" },
};
}
}
}
a controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new MyViewModel();
return View(model);
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return View(model);
}
}
and a view:
#model MyViewModel
#using (Html.BeginForm())
{
<div>
#Html.DropDownListFor(x => x.Country, Model.Countries, "-- Country --")
#Html.ValidationMessageFor(x => x.Country)
</div>
<div>
#Html.DropDownListFor(x => x.State, Model.States, "-- State --")
#Html.ValidationMessageFor(x => x.State)
</div>
<button type="submit">OK</button>
}
You might also find the Foolproof package of validation attributes useful.