How to access selected item from dropdown in asp.net mvc3 - c#

i am using asp.net mvc3 and i populate a create view using following model
Model
public class CategoryModel
{
public int Id { get; set; }
public string Name { get; set; }
public string URL { get; set; }
public string Description { get; set; }
public string Logo { get; set; }
public bool IsActive { get; set; }
public bool isPopular { get; set; }
public IList<Category> Parentcategories { get; set; }
}
In my create View i populate like this
View
<div class="editor-field">
#Html.DropDownList("parentcategories", new SelectList(Model.Parentcategories.Select(c => c.Name), Model.Parentcategories.Select(c => c.Name)))
#Html.ValidationMessageFor(model => model.Parentcategories)
</div>
now how can i access the selected item in my controller method
Method
[HttpPost]
public ActionResult Create( CategoryModel model , HttpPostedFileBase file)
{
//
}
thanks,
Ahsan

Try this:
public ActionResult Create(string parentcategories, CategoryModel model , HttpPostedFileBase file)
parentcategories will contain selected option value.

As Smartboy already mentioned, you should use DropDownListFor:
1. append your model with public int ParentCategoryId { get; set; } field.
2. instead of using #Html.DropDownList use:
#Html.DropDownListFor(m => m.ParentCategoryId, new SelectList(...))
3. the server side can stay the same:
[HttpPost]
public ActionResult Create(CategoryModel model)
{
//
}
where model.ParentCategoryId will have selected item value.
Also note that you can first set selected item value for your view:
public ActionResult Index()
{
var model = CategoryModel();
...
model.ParentCategoryId = some_selected_value;
return View(model);
}

Details: You can access it directly from your model.
[HttpPost]
public ActionResult Create( CategoryModel model , HttpPostedFileBase file)
{
var selectedCategory = model.parentcategories; // something like that
}

Related

MVCCheckBoxList catering for a 1 to Many relationship

I have a class for which has a 1 to many relationship with another class. for this I will use class Car and class Gears. I need to create a form, which registers a car and the user needs to specify a choice of gears.
public class Car
{
public int id { get; set; }
public string desc { get; set; }
public List<Gear> Gears { get; set; }
}
public class Gear
{
public int gid { get; set; }
public int gname { get; set; }
}
using asp.net MVC 5, I have a create form, which I have scaffolded to the Car model, and within the form, I wish to have a checkboxlist of gears,
I also have a ViewModel that I have provided for my checkboxlist which is as below:
public class GearsViewModel
{
public Gear _gear {get; set; }
public bool _isChecked {get; set;}
}
Controller looks like:
Gears fetched from db context will be
"GearR","Gear1","Gear2","Gear3","Gear4","Gear5","Gear6","Gear7"
public action Create()
{
ViewBag.Gears = new SelectList(db.Gears, "gid","gname");
List<GearViewModel> _gears= new List<GearViewModel>();
foreach(Gear G in ViewBag.Gears)
{
_gears.Add(new GearViewModel(G, false));
}
ViewBag.GearsCheckList = _gears.ToList();
return View();
}
Now, this is the part I'm getting stuck at, is how to display and capture details in the CreateView.
I need assistance on how to design the Create form and how I will capture the info.
Firstly, view models should not contain data models when editing. You view models should be (add validation and display attributes as appropriate)
public class CarVM
{
public int? ID { get; set; }
public string Description { get; set; }
public List<GearVM> Gears { get; set; }
}
public class GearVM
{
public int ID { get; set; }
public string Name { get; set; }
public bool IsSelected { get; set; }
}
and the GET method will be
public ActionResult Create()
{
var gears = db.Gears;
CarVM model = new CarVM
{
Gears = gears.Select(x => new GearVM
{
ID = x.gid,
Name = x.gname
}).ToList()
};
return View(model);
}
and the view will then be
#model CarVM
....
#using (Html.BeginForm())
{
..... // elements for editing ID and Description properties of CarVM
#for (int i = ; i < Model.Gears.Count; i++)
{
<div>
#Html.HiddenFor(m => m.Gears[i].ID)
#Html.HiddenFor(m => m.Gears[i].Name) // include if your want to get this in the POST method as well
#Html.CheckboxFor(m => m.Gears[i].IsSelected)
#Html.LabelFor(m => m.Gears.IsSelected, Model.Gears[i].Name)
</div>
}
<input type="submit" .... />
}
Then in the POST method
public ActionResult Create(CarVM model)
{
if (!ModelState.IsValid)
{
return View(model);
}
// To get the ID's of the selected gears
IEnumerable<int> selected = model.Gears.Where(x => x.IsSelected).Select(x => x.ID);
// Initialize your data models, save and redirect
}

ASP MVC Model issue

I have a model:
public class MenuItem
{
public int Id { get; set; }
....
[StringLength(50)]
public string PageTemplate { get; set; }
public int MenuId { get; set; }
public Menu Menu { get; set; }
}
Notice the PageTemplate. I need to somehow create a property that list out files from a folder. This property should then be a selectlist in the view, and then saved in the PageTemplate.
What would the proper way of doing this be?
In the model add:
public IEnumerable<SelectListItem> Templates { get; set; }
In the action method populate your data to the property:
public ActionResult Create()
{
return View(new MenuItem
{
Templates = _yourFilesList.
.Select(f => new SelectListItem { Text = f.Title })
});
}
[HttpPost]
public ActionResult Create(MenuItem item)
{
var userTemplate=item.PageTemplate; // user's selected item is there
}
Finally in the view add this
#Html.DropDownListFor(x => x.PageTemplate , Model.Templates)

Entity Framework & ASP.NET MVC more complex models

I have Entity Framework model which contains reference to other Entity like
public class Product
{
[Key]
public int ProductID { get; set; }
[Required]
public string Name { get; set; }
[Required]
public virtual Shop Shop { get; set; }
[Required]
public double Price { get; set; }
}
I would like to create Edit View which contain Shop selector (DropDown).
By default I have created basic MVC Controller with Entity model connected, which created Edit like:
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Product product = db.Products.Find(id);
if (product == null)
{
return HttpNotFound();
}
return View(product);
}
and View does not contain Shop selector.
I have tried to add DropDown like:
#Html.DropDownListFor(product => product.Shop, (SelectList)ViewBag.Shops)
But in POST method, Shop entity is null.
How to handle that?
Create a view model to represent what you want to display
public class ProductVM
{
public int ProductID { get; set; }
[Required]
public string Name { get; set; }
[Required]
public int? ShopID { get; set; }
[Required]
public double Price { get; set; }
public SelectList ShopList { get; set; }
}
and in your controller, map your model to the view model
public ActionResult Edit(int? ID)
{
....
Product product = db.Products.Find(id);
ProductVM model = new ProductVM();
// map properties
....
// populate select list (assumes Shop has properties ID and Name)
model.ShopList = new SelectList(db.Shops, "ID", "Name");
return View(product);
}
and in your view
#model ProductVM
....
#Html.DropDownListFor(m => m.ShopID, Model.ShopList, "--Select shop--")
#Html.ValidationMessageFor(m -> m.ShopID)
this will post back the model with the selected ID of the Shop. Select controls post back single values so you cannot post back a complex object such as Shop. The POST method would be
[HttpPost]
public ActionResult Edit(ProductVM model)
{
....
}
Note you can use tools such as automapper to make mapping easier
I hope this helps.
Model for Product:
public class Product
{
public int ProductID { get; set; }
public string Name { get; set; }
public int ShopID { get; set; }
public double Price { get; set; }
}
Then a ViewModel for Product:
public class ProductViewModel
{
public Product Model { get; set; }
public IEnumerable<SelectListItem> Shops{ get; set; }
public ProductViewModel()
{
GetShops();
}
public void GetShops()
{
Shops = new List<SelectListItem>();
var collectionShops = GetShopsFromDatabase();
Shops.AddRange(
collectionShops.Select(
contract =>
new SelectListItem
{
Text = contract.ShopDescription,
Value = contract.ShopID.ToString()
}));
}
}
In your View:
#model ProductViewModel
....
#Html.DropDownListFor(x => x.Model.ShopID, Model.Shops, new { #title = "Please select a shop" })

Scaffolding ModelView creates underlying database tables

I'm trying to use ViewModels for the first time using AutoMapper. I have two models:
public class Item
{
public int ItemId { get; set; }
public bool Active { get; set; }
public string ItemCode { get; set; }
public string Name { get; set; }
public List<ItemOption> ItemOptions { get; set; }
//...
}
public class ItemOption
{
public int ItemOptionId { get; set; }
public string Name { get; set; }
public string Barcode { get; set; }
//...
}
Which I have turned into two ViewModels:
public class ItemDetailViewModel
{
public int ItemDetailViewModelId { get; set; }
public int ItemId { get; set; }
public bool Active { get; set; }
public string ItemCode { get; set; }
public string Name { get; set; }
public List<ItemDetailItemOptionViewModel> ItemOptions { get; set; }
}
public class ItemDetailItemOptionViewModel
{
public int ItemDetailItemOptionViewModelId { get; set; }
public int ItemOptionId { get; set; }
public string Name { get; set; }
public string Barcode { get; set; }
}
I then set the following in my application start-up:
Mapper.CreateMap<Item, ItemDetailViewModel>();
Mapper.CreateMap<ItemOption, ItemDetailItemOptionViewModel>();
Finally I scaffolded my ItemDetailViewModel:
I then built my project and added a new Item through /Item/Create
I had a look in the database expecting to see that I would have an entry in the Item table, but instead I have ItemDetailViewModel and ItemDetailItemOptionViewModel tables, which I wasn't expecting and the data is is ItemDetailViewModel.
I assume I have done something wrong with my scaffolding? How do I scaffold off the ViewModel without making it part of the main business models?
Further Details
If it isn't possible to scaffold the controller with a ViewModel, then how do I reference the ViewModel in the controller and save changes back to the database?
For example what would the following change to once I remove ItemDetailViewModel from the db context?
//
// POST: /Item/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ItemDetailViewModel itemdetailviewmodel)
{
if (ModelState.IsValid)
{
db.ItemDetailViewModels.Add(itemdetailviewmodel);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(itemdetailviewmodel);
}
Further Details [2]
So am I correct that my Index/Details should work as so or is there a better way of doing it?
//
// GET: /Item/
public ActionResult Index()
{
var items = db.Items.ToList();
var itemdetailviewmodel = AutoMapper.Mapper.Map<ItemDetailViewModel>(items);
return View(itemdetailviewmodel);
}
//
// GET: /Item/Details/5
public ActionResult Details(int id = 0)
{
ItemDetailViewModel itemdetailviewmodel = AutoMapper.Mapper.Map<ItemDetailViewModel>(db.Items.Find(id));
if (itemdetailviewmodel == null)
{
return HttpNotFound();
}
return View(itemdetailviewmodel);
}
Scaffolding is not that intelligent. The standard controller scaffolding template is creating a DbContext with the controller model and presumes you are working with the DB models, not view models and it does not use Automapper. So you'll need to either not use scaffolding, or check what it has done before using it.
And nothing is wrong with the way you use scaffolding, it is just not supposed to do what you expect.
Update this is how you do this without scaffolding
// Without Automapper
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ItemDetailViewModel model)
{
if (ModelState.IsValid)
{
var item = new Item()
{
Active = model.Active,
ItemCode = model.ItemCode,
Name = model.Name,
ItemOptions = // code to convert from List<ItemDetailItemOptionViewModel> to List<ItemOption>
}
db.Items.Add(item);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(model);
}
// with Automapper - not recommended by author of Automapper
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ItemDetailViewModel model)
{
if (ModelState.IsValid)
{
var item = Automapper.Mapper.Map<Item>(model);
db.Items.Add(item);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(model);
}
You'll need to modify your DbContext to have IDbSet<Item> Items instead of IDbSet<ItemDetailViewModels> ItemDetailViewModels.
Automapper was created to map from Domain Models to View Models and not the other way. I have done that for a while, but this is troublesome and causes subtle bugs and other maintenance problems. Even Jimmy Bogard himself says you should not map from view models to domain models.

Posting separate lines from TextAreaFor into separate DB rows

How can I get every line from TextAreaFor in Razor (or other form of HTML input) to be used by Create method (or how to modify that method) as separate value and inserted into separate row in DB.
Controller:
[HttpPost]
public ActionResult Create(Names name)
{
unitofwork.Names.Insert(name);
unitofwork.save();
return RedirectToAction("Index");
}
View
#Html.TextAreaFor(model => model.name)
Names Model
[Key]
public int NameID { get; set; }
[Required]
public string Opinion { get; set; }
[Required]
public int ID { get; set; }
public virtual NickName NickName { get; set; }
Try something like this:
[HttpPost]
public ActionResult Create(Names name)
{
var names = name.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)
foreach(var n in names)
{
unitofwork.Names.Insert(n);
}
unitofwork.save();
return RedirectToAction("Index");
}

Categories