I have read many times experts of MVC saying that if I were to use a SelectList, it's best to have a IEnumerable<SelectList> defined in my model.
For example, in this question.
Consider this simple example:
public class Car()
{
public string MyBrand { get; set; }
public IEnumerable<SelectListItem> CarBrands { get; set; } // Sorry, mistyped, it shoudl be SelectListItem rather than CarBrand
}
In Controller, people would do:
public ActionResult Index()
{
var c = new Car
{
CarBrands = new List<CarBrand>
{
// And here goes all the options..
}
}
return View(c);
}
However, from Pro ASP.NET MVC, I learned this way of Creating a new instance.
public ActionResult Create() // Get
{
return View()
}
[HttpPost]
public ActionResult Create(Car c)
{
if(ModelState.IsValid) // Then add it to database
}
My question is: How should I pass the SelectList to View? Since in the Get method there is no model existing, there seems to be no way that I could do this.
I could certainly do it using ViewBag, but I was told to avoid using ViewBag as it causes problems. I'm wondering what are my options.
You could create a ViewModel that has all the properties of Car which you want on your form then make your SelectList a property of that ViewModel class
public class AddCarViewModel
{
public int CarName { get; set; }
public string CarModel { get; set; }
... etc
public SelectList MyList
{
get;
set;
}
}
Your controller will look like
public ActionResult Create() // Get
{
AddCarViewModel model = new AddCarViewModel();
return View(model)
}
[HttpPost]
public ActionResult Create(AddCarViewModel c)
{
if(ModelState.IsValid) // Then add it to database
}
MarkUp
#Html.DropDownListFor(#model => model.ListProperty, Model.MyList, ....)
Easy way, this is a copy of my code
without model
In the controller
ViewBag.poste_id = new SelectList(db.Postes, "Id", "designation");
In the view
#Html.DropDownList("poste_id", null," -- ", htmlAttributes: new { #class = "form-control" })
Related
I have an MVC 5 site, I would like to use a strongly typed DropDownListFor with a ViewModel - not with ViewBag.
I have found various articles on this - but they all seem to have huge holes - for example this one doesnt cover editing, and I do not understand how or when "SelectedFlavourId" should be used.
http://odetocode.com/blogs/scott/archive/2013/03/11/dropdownlistfor-with-asp-net-mvc.aspx
I have several requirements.
When editing the story I would like a drop down list of all places to
be displayed - with the associated place (if any) - selected.
I want to use the strongly typed DropDownListFOR (as opposed to
DropDownList).
I would like to use a ViewModel not the ViewBag.
I want to add a "No Associated Place" which will be
selected if PlaceId is null.
I want to add a css class = "form-control" to the DropDownListFor.
The below is as far as I have got after a day of frustration.
A story can be optionally associated with a PlaceId. A blank placeId is also valid. A place can also be associated with more than one story.
Models
public class Place
{
public Guid Id { get; set; }
public string PlaceName { get; set; }
}
public class Story
{
public Guid Id { get; set; }
public Guid? PlaceId { get; set; }
public string StoryName { get; set; }
}
public class StoryPlaceDropdown
{
public Story story { get; set; }
public Guid SelectedStoryId;
public IEnumerable<Place> places;
public IEnumerable<SelectListItem> placeItems
{
get
{
return new SelectList(places, "Id", "PlaceName");
}
}
}
Controller
public ActionResult Edit(Guid Id)
{
var spd = new StoryPlaceDropdown();
spd.places = PlaceRepo.SelectAll();
spd.story = StoryRepo.SelectStory(Id);
spd.selectedStoryID = apd.story.Id;
// Return view
return View(spd);
}
[HttpPost]
public ActionResult Edit(StoryPlaceDropdown spd)
{
// Never gets this far
spd.Places = PlaceRepo.SelectAll();
return View();
}
In View
#Html.DropDownListFor(m => m.SelectedStoryId, Model.PlaceItems)
This populates the DropDownList fine. However it does not select the correct item in edit view. Also when I submit the form I get this error:
Object reference not set to an instance of an object. on this line in the view #Html.DropDownListFor(m => m.SelectedStoryId, Model.PlaceItems)
How can I get this all working? Thanks.
I solved this - I had stupidly forgotten the { get; set; } accessors on the ViewModel, doh!
You can resolved this by these three steps:
Step 1:Create viewmodel
public class StoryPlaceDropdown
{
Required]
[Display(Name = "SelectedStory")]
public int SelectedStoryId { get; set; }
}
Step 2:After this on controller you can write:
public ActionResult Edit(Guid Id)
{
var spd = new StoryPlaceDropdown();
ViewBag.PlaceItems= PlaceRepo.SelectAll();
spd.story = StoryRepo.SelectStory(Id);
spd.selectedStoryID = apd.story.Id;
return View(spd);
}
Step 3: And on view you can write
<div class="col-sm-6">
#Html.DropDownListFor(m => m.SelectedStoryId, new SelectList(#ViewBag.PlaceItems, "Id", "PlaceName"), "---Select---", new { #class = "form-control select-sm" })
</div>
I don't have much experience with programming and I'm new to MVC.
I want to fetch some data from database with entity framework and print it in the view.
This is my model:
public class Grad
{
public int ID { get; set; }
public string Naziv { get; set; }
public char KoordinataX { get; set; }
public char KoordinataY { get; set; }
public int BrojStanovnika { get; set; }
}
public class GradDBContext : DbContext
{
public DbSet<Grad> Gradovi { get; set; }
}
this is a controller:
private GradDBContext db = new GradDBContext();
public ActionResult Index()
{
List<int> gradoviList = new List<int>();
foreach (sea.Models.Grad g in db.Gradovi)
{
gradoviList.Add(g.ID);
}
ViewData["Gradovi"] = new SelectList(gradoviList);
return View();
}
and this is a view:
#foreach (var item in ViewData["Gradovi"] as IEnumerable<int>) ---> error appears here as null reference exception
{
<p>item</p>
}
I know that I have to parse data but don't have idea what did I do wrong
The ViewData item with the key "Gradovi" is typeof SelectList, so it would need to be
#foreach (var item in ViewData["Gradovi"] as SelectList)
{
<p>#item.Value</p> // or #item.Text
However there is no point generating IEnumerable<SelectListItem> (which is what SelectList is) when you do not need it, and you should be passing your model to the view. Your code in the controller should be
public ActionResult Index()
{
IEnumerable<int> model = db.Gradovi.Select(x => x.ID);
return View(model);
}
and in the view
#model IEnumerable<int>
#foreach(var item in Model)
{
<p>#item</p>
}
Your code can work like you have it, but I am going to modify it a bit and give you some pointers. I am supplying an answer based on what I see in your post, not what I think you want to achieve at a later stage. There are many ways to accomplish a goal, I will select the simplest way that I will normally use:
public ActionResult Index()
{
// You will have a repository layer for this part
GradDBContext db = new GradDBContext();
// Get a list of your items
List<Grad> gradovis = db.Gradovi.ToList();
// I never work with view data, I just pass my view model to the view
// This way you now have more data to display on the screen (if you need more)
return View(gradovis);
}
And then your view could look like this:
#model List<Project.Model.Grad>
#foreach (var grad in Model)
{
<p>#grad.ID</p>
}
I'm trying to list the items from my database into my view but I'm getting null back.
I know the connection must be working to a certain extent because in my database the tables didn't exist but once I ran my program it did create the tables. However when I add content into my table my view still returns NULL.
Also, haven't touched the Review table yet, just worried about getting Restaurants working.
Restaurant.cs
namespace OdeToFood.Models
{
public class Restaurant
{
public int Id { get; set; }
public string Name { get; set; }
public string City { get; set; }
public string Country { get; set; }
public ICollection<RestaurantReview> Reviews { get; set; }
}
}
OdeToFood.cs
namespace OdeToFood.Models
{
public class OdeToFoodDb : DbContext
{
public DbSet<Restaurant> Restaurants { get; set; }
public DbSet<RestaurantReview> Reviews { get; set; }
}
}
Controller
OdeToFoodDb _db = new OdeToFoodDb();
public ActionResult Index()
{
var model = _db.Restaurants.ToList();
return View();
}
Index.cshtml
#model IEnumerable<OdeToFood.Models.Restaurant>
#{
ViewBag.Title = "Home Page";
}
#{
if (Model != null)
{
foreach (var item in Model)
{
<div>
<h4>#item.Name</h4>
<div>#item.City, #item.Country</div>
<hr />
</div>
}
}
else
{
<h1>Null</h1>
}
}
You need to pass to model back to the view.
OdeToFoodDb _db = new OdeToFoodDb();
public ActionResult Index()
{
var model = _db.Restaurants.ToList();
return View(model);
}
You never actually send the model to the view. Pass it as an argument:
OdeToFoodDb _db = new OdeToFoodDb();
public ActionResult Index()
{
var model = _db.Restaurants.ToList();
return View(model);
}
Additionally, it's generally a good idea not to create database contexts in a shared scope. Keep the context as close to where it's used as possible and only expand its scope when you really need to. Something like this:
public ActionResult Index()
{
using (var _db = new OdeToFoodDb())
{
var model = _db.Restaurants.ToList();
return View(model);
}
}
Database contexts/connections in a shared scope is just asking for problems unless you pay close attention to what you're doing. As the code gets more complex, it becomes more likely that other methods will try to use it and it may be in an unknown state at that time.
I'm new to MVC, so bear with me...
I've got my new form\view working (Creating and Adding a client)
But now I want to get the user so specifiy the Country where the new client is from A drop downlist. But im to sure how I to do this?
ViewModel
public class ClientNew
{
public string Company { get; set; }
public string Address { get; set; }
//New
public IEnumerable<CountryList> Country{ get; set; }
}
public class CountryList
{
public string Id { get; set; }
public string Name { get; set; }
}
Controller
(This is where is may be wrong, and is this the best way to do it?)
public ActionResult New()
{
var cl= new List<CountryList>();
cl.Add(new CountryList(){Id = "abcd",Name = "UK"});
cl.Add(new CountryList() { Id = "abce", Name = "USA" });
var model = new ViewModels.ClientNew();
model.Country= cl;
return View("New", model);
}
View (not sure how to plumb this in)
Html.DropDownList("Id" ??????)
In your view you will set up your dropdown on the property Id. This will be the current value selected in the dropdown when you POST to your form. The data that will be used for the dropdown is a SelectList called Countries that exists in your model.
#Html.DropDownListFor(m => m.Id, Model.Countries)
Your view model will have your Id, Name and Countries properties plus whatever else you need.
public class ClientNewViewModel {
public string Id { get; set; }
public string Name { get; set; }
public SelectList Countries { get; set; }
}
In your controller you need to pass the model to the view. You will need to populate the Countries SelectList. Keep in mind you will need to populate this value when you POST and fail validation as well.
public ActionResult New()
{
var model = new ClientNewViewModel();
model.Countries = new SelectList(service.GetCountries(),
"Id", "Name"); // set up what properties are used for id/name of dropdown
return View(model);
}
[HttpPost]
public ActionResult New(ClientNewViewModel model)
{
if( !ModelState.IsValid )
{
model.Countries = new SelectList(service.GetCountries(),
"Id", "Name");
return View(model);
}
// redirect on success
return RedirectToAction("Index");
}
Html.DropDownList("Id",
Country.Select(x => new SelectListItem
{
Text = x.Name,
Value = x.Id
}));
There's a good blog post on the in's and out's of how to do this here -> http://277hz.co.uk/Blog/Show/10/drop-down-lists-in-mvc--asp-net
EF4.1-Code-First-Gurus!
I wonder if there is a more elegant way to handle the following ASP.NET MVC 3 EF 4.1 Code First scenario: Lets say we have the following POCOs:
public class Entity
{
[Key]
public int Id { get; set; }
[ScaffoldColumn(false)]
public DateTime CreatedOn { get; set; }
[ScaffoldColumn(false)]
public DateTime ModifiedOn { get; set; }
}
and
public class Person : Entity
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime Birthday { get; set; }
}
Lets assume we have created some standard editing views, which are not including CreatedOn/ModifiedOn fields, because, they will be set in the repository and not by the user.
In my repository I have the following Update method. The methods excepts a list of fields, which should be updated (leaving CreatedOn/ModifiedOn fields out):
public void Update(Person person, List<string> properties)
{
Person tmpPerson = context.People.Single(x => x.Id == person.Id);
context.People.Attach(tmpPerson);
foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(person))
{
if (properties.Contains(descriptor.Name))
descriptor.SetValue(tmpPerson, descriptor.GetValue(person));
}
tmpPerson.ModifiedOn = DateTime.Now;
}
Now the controller is calling this method like this:
[HttpPost]
public ActionResult Edit(Person person)
{
if (ModelState.IsValid) {
personRepository.Update(person, new List<string> { "FirstName", "LastName", "Birthday"});
personRepository.Save();
return RedirectToAction("Index");
} else {
return View();
}
}
This all works like a charm. However, I really dislike, that I have to specify the fields manually. How would you handle this requirement? Of course I could add CreatedOn/ModifiedOn fields as hidden fields to the view, but I dont want to bload the form to much (There are much more fields).
Maybe this is a similar question:
How To Update EF 4 Entity In ASP.NET MVC 3?
I highly appreciate your help!
Joris
Yes there is more elegant version:
public void Update(Person person, params Expression<Func<Person,object>>[] properties)
{
context.People.Attach(person);
DbEntityEntry<Person> entry = context.Entry(person);
foreach (var property in properties)
{
entry.Property(property).IsModified = true;
}
person.ModifiedOn = DateTime.Now;
}
You will call the method this way:
[HttpPost]
public ActionResult Edit(Person person)
{
if (ModelState.IsValid)
{
personRepository.Update(person, p => p.FirstName,
p => p.LastName, p => p.Birthday);
personRepository.Save();
return RedirectToAction("Index");
}
else
{
return View(person);
}
}