mvc 3 dynamic Category Dropdown - asp.net-mvc-3

Ok, I have read a bunch of articles, and I am still lost, so I figure I will put the question out here.
I am trying to create a dynamic dropdown in my "posts" create view. I would like to pull the selectList items from my Categories.sdf, which has a table called categories and two columns, "CategoryID" and "CategoryTitle".
I know I need to pull the items into the viewbag within by "postscontroller" so they can be passed to the view. But I am not sure what this would look like. Again, i'm new to MVC so if i sound like a dope, i apologize.

I know I need to pull the items into the viewbag within by "postscontroller"
Oh no, you don't need to do anything like that.
You could start by defining a view model:
public class PostViewModel
{
[DisplayName("Select a category")]
[Required]
public string SelectedCategoryId { get; set; }
public IEnumerable<SelectListItem> Categories { get; set; }
}
that you will populate in your controller:
public class PostsController: Controller
{
public ActionResult Index()
{
var model = new PostViewModel();
model.Categories = db.Categories.ToList().Select(c => new SelectListItem
{
Value = c.CategoryId,
Text = c.CategoryName
});
return View(model);
}
}
and then have a corresponding strongly typed view (~/views/posts/index.cshtml):
#model PostViewModel
#using (Html.BeginForm())
{
#Html.LabelFor(x => x.SelectedCategoryId)
#Html.DropDownListFor(x => x.SelectedCategoryId, Model.Categories, "-- select --")
#Html.ValidationMessageFor(x => x.SelectedCategoryId)
<button type="submit">OK</button>
}

Related

MVC dropdownlist , data not displaying when model is not Ienumerable

I am having a problem passing to the View a Model that contains the dropdown data and the model.
With this code my page loads, but the dropdownlist contains "System.Web.MVC.SelectList" when selected.
Here's my controller code.
public ActionResult Index(string productNameFilter, string productCategoryFilter, String productTypeFilter )
{
var ddl = new Items();
ddl.CategoryddList = itemsRepository.GetItemDdl("Item Categories").Select(c => new SelectListItem
{
Value = c.DropdownID.ToString(),
Text = c.DropdownText
});
ViewBag.CategoryDD = new SelectList(ddl.CategoryddList, "Value", "Text");
var model = itemsRepository.GetItemByName(productNameFilter);
return View(model);
}
Here's my view
#model Ienumerable<Models.items.items>
#Html.DropDownList("productCategoryFilter",
new SelectList(ViewBag.CategoryDD),
"---Select Category---")
Side note - if you use a ViewModel between the View and the Model instead of binding directly to the model, you can put your SelectList on the ViewModel and use #Html.DropdownFor() instead of #Html.Dropdown(). The ViewBag should really be used sparingly.
However back to your original question:
What is "Items()"? in your line
var ddl = new Items();
I'm not sure what good reason you would have NOT to make it enumerable.
I suspect it is not working because you are making a selectlist from a select list twice --
in your code behind you are defining ViewBag.CategoryDD as a SelectList(), and then in your Razor code you are creating a new SelectList() from the existing selectlist. You shouldn't have to do this.
The way I would do this is create a ProductViewModel class that contains your product category list AND your list of products (your current model), and a property for the selected filter.
public class ProductViewModel
{
public IEnumerable<Model.items.items> ProductList {get;set;}
public IEnumerable<SelectListItem> ProductCategoryList {get;set;} //SelectList is an IEnumerable<SelectListItem>
public string SelectedCategory {get;set;}
}
Then on your view the model would be
#model ProductViewModel
#Html.DisplayFor(model => model.SelectedCategory, "---Select Category---")
#Html.DropdownListFor(model => model.SelectedCategory, Model.ProductCatgoryList)

Read The Data From DropDown in ASP.Net MVC3

I am doing the ASP.net MVC 3 (Empty type and not the internet type) with the Database First approach...
What i need is
Step 1:
I just used the dropdown to display the various locations where the company is located. The list comes from the Organization table and Location is only one string field in this Oranization Table,
Step 2:
While the user is doing registration, the dropdown list will show the locations.. Now, user selects India, then this value (Location Name) should store in the UserLogin Table...
Now how to read the value from the dropdown and i hope you understand my question and thanks in advance
I would use view models:
public class RegisterViewModel
{
public string LocationName { get; set; }
public IEnumerable<SelectListItem> Locations { get; set; }
}
then a controller action that will serve the view:
public ActionResult Index()
{
var model = new RegisterViewModel();
model.Locations = new SelectList(dbcontext.Organization_Details, "OName", "OLocation");
return View(model);
}
then the corresponding strongly typed view:
#model RegisterViewModel
#using (Html.BeginForm())
{
#Html.LabelFor(x => x.LocationName)
#Html.DropDownListFor(x => x.LocationName, Model.Locations)
<button type="submit">OK</button>
}
and finally the controller action that will be invoked when the form is submitted:
[HttpPost]
public ActionResult Index(RegisterViewModel model)
{
// model.LocationName will contain the selected location here
...
}

IEnumerable property with MVC3 EditorTemplate

Similar to this post IEnumerable model property in an ASP.NET MVC 3 Editor Template, I have
Model
public class Student
{
public int StudentId { get; set; }
public string StudentName{ get; set; }
//FYI..Its virtual because of EF relationship
public virtual ICollection<Class> Classes{ get; set; }
}
public class Class
{
public int ClassId { get; set; }
public string ClassName{ get; set; }
}
View - EditStudent
#model Student
#Html.TextBoxFor(m => m.StudentName)
//I get the error for following..see below
#Html.EditorFor(m => m.Classes);
Student/EditorTemplates/Class
#model Class
<div>
#*checkbox here*#
#Html.LabelFor(x => x.ClassName)
</div>
Controller
public ActionResult EditStudent(int id)
{
ViewBag.Classes = repository.GetClasses();
Student student = repository.GetStudent(id);
return View("EditStudent", student);
}
Error in View on statement #Html.EditorFor(m => m.Classes); is..
The model item passes into the dictionary is of type
'System.Collections.Generic.HashSet`1[Class]', but this dictionary
required a model item of type 'Class'.
Basically, what I am trying to achieve is to display the list of all classes available with a checkbox next to it ( I have not reached to that part of code yet). Then check all classes to a student is enrolled and allow to change the selections.
How do I display the list of checkboxes with the given Model.
Should I bind my EditorTemplate with ViewBag.Classes (How?) or ?
I need to get selected checkbox values in Post ActionMethod as well.
I read some posts those suggest to create CheckBoxListHelper, but it should be possible to do with EditorTemplate as I need to display a simple list.
Please suggest. Thanks.
Okay, I figured it out. Thanks to very precise post here
How to provide an EditorTemplate for IEnumerable<MyModel>?
First, I renamed the EditorTemplate to StudentClass - not sure if this has anything to do with binding or not, but I did.
Second, modified EditorTemplate to bind with IEnumerable
#model IEnumerable<Class>
var checked = "";
#foreach (Class class in ViewBag.Classes)
{
if (Model != null)
{
Class class = Model.FirstOrDefault(c => c.ClassId.Equals(class.ClassId));
if (class != null)
{
checked = "checked=checked";
}
}
<input type="checkbox" name="Classes" value="#class.ClassId" #checked />
#class.ClassName
}
And I call the template with name
#Html.EditorFor(m => m.Classes, "StudentClass");
Now in controller's Post method I can get the array of Classes (name of checkboxes).

Two models in one view in ASP MVC 3

I have 2 models:
public class Person
{
public int PersonID { get; set; }
public string PersonName { get; set; }
}
public class Order
{
public int OrderID { get; set; }
public int TotalSum { get; set; }
}
I want edit objects of BOTH classes in SINGLE view, so I need something like:
#model _try2models.Models.Person
#model _try2models.Models.Order
#using(Html.BeginForm())
{
#Html.EditorFor(x => x.PersonID)
#Html.EditorFor(x => x.PersonName)
#Html.EditorFor(x=>x.OrderID)
#Html.EditorFor(x => x.TotalSum)
}
This, of course, don't work: Only one 'model' statement is allowed in a .cshtml file. May be there is some workaround?
Create a parent view model that contains both models.
public class MainPageModel{
public Model1 Model1{get; set;}
public Model2 Model2{get; set;}
}
This way you can add additional models at a later date with very minimum effort.
To use the tuple you need to do the following, in the view change the model to:
#model Tuple<Person,Order>
to use #html methods you need to do the following i.e:
#Html.DisplayNameFor(tuple => tuple.Item1.PersonId)
or
#Html.ActionLink("Edit", "Edit", new { id=Model.Item1.Id }) |
Item1 indicates the first parameter passed to the Tuple method and you can use Item2 to access the second model and so on.
in your controller you need to create a variable of type Tuple and then pass it to the view:
public ActionResult Details(int id = 0)
{
Person person = db.Persons.Find(id);
if (person == null)
{
return HttpNotFound();
}
var tuple = new Tuple<Person, Order>(person,new Order());
return View(tuple);
}
Another example : Multiple models in a view
Another option which doesn't have the need to create a custom Model is to use a Tuple<>.
#model Tuple<Person,Order>
It's not as clean as creating a new class which contains both, as per Andi's answer, but it is viable.
If you are a fan of having very flat models, just to support the view, you should create a model specific to this particular view...
public class EditViewModel
public int PersonID { get; set; }
public string PersonName { get; set; }
public int OrderID { get; set; }
public int TotalSum { get; set; }
}
Many people use AutoMapper to map from their domain objects to their flat views.
The idea of the view model is that it just supports the view - nothing else. You have one per view to ensure that it only contains what is required for that view - not loads of properties that you want for other views.
ok, everyone is making sense and I took all the pieces and put them here to help newbies like myself that need beginning to end explanation.
You make your big class that holds 2 classes, as per #Andrew's answer.
public class teamBoards{
public Boards Boards{get; set;}
public Team Team{get; set;}
}
Then in your controller you fill the 2 models. Sometimes you only need to fill one. Then in the return, you reference the big model and it will take the 2 inside with it to the View.
TeamBoards teamBoards = new TeamBoards();
teamBoards.Boards = (from b in db.Boards
where b.TeamId == id
select b).ToList();
teamBoards.Team = (from t in db.Teams
where t.TeamId == id
select t).FirstOrDefault();
return View(teamBoards);
At the top of the View
#model yourNamespace.Models.teamBoards
Then load your inputs or displays referencing the big Models contents:
#Html.EditorFor(m => Model.Board.yourField)
#Html.ValidationMessageFor(m => Model.Board.yourField, "", new { #class = "text-danger-yellow" })
#Html.EditorFor(m => Model.Team.yourField)
#Html.ValidationMessageFor(m => Model.Team.yourField, "", new { #class = "text-danger-yellow" })
And. . . .back at the ranch, when the Post comes in, reference the Big Class:
public ActionResult ContactNewspaper(teamBoards teamboards)
and make use of what the model(s) returned:
string yourVariable = teamboards.Team.yourField;
Probably have some DataAnnotation Validation stuff in the class and probably put if(ModelState.IsValid) at the top of the save/edit block. . .
In fact there is a way to use two or more models on one view without wrapping them in a class that contains both.
Using Employee as an example model:
#model Employee
Is actually treated like.
#{ var Model = ViewBag.model as Employee; }
So the View(employee) method is setting your model to the ViewBag and then the ViewEngine is casting it.
This means that,
ViewBag.departments = GetListOfDepartments();
return View(employee);
Can be used like,
#model Employee
#{
var DepartmentModel = ViewBag.departments as List<Department>;
}
Essentially, you can use whatever is in your ViewBag as a "Model" because that's how it works anyway. I'm not saying that this is architecturally ideal, but it is possible.
Just create a single view Model with all the needed information in it, normaly what I do is create a model for every view so I can be specific on every view, either that or make a parent model and inherit it. OR make a model which includes both the views.
Personally I would just add them into one model but thats the way I do it:
public class xViewModel
{
public int PersonID { get; set; }
public string PersonName { get; set; }
public int OrderID { get; set; }
public int TotalSum { get; set; }
}
#model project.Models.Home.xViewModel
#using(Html.BeginForm())
{
#Html.EditorFor(x => x.PersonID)
#Html.EditorFor(x => x.PersonName)
#Html.EditorFor(x => x.OrderID)
#Html.EditorFor(x => x.TotalSum)
}
You can use the presentation pattern http://martinfowler.com/eaaDev/PresentationModel.html
This presentation "View" model can contain both Person and Order, this new
class can be the model your view references.
Another way that is never talked about is
Create a view in MSSQL with all the data you want to present. Then use LINQ to SQL or whatever to map it. In your controller return it to the view. Done.
you can't declare two model on one view, try to use Html.Action("Person", "[YourController]") & Html.Action("Order", "[YourController]").
Good luck.
Beside of one view model in asp.net you can also make multiple partial views and assign different model view to every view, for example:
#{
Layout = null;
}
#model Person;
<input type="text" asp-for="PersonID" />
<input type="text" asp-for="PersonName" />
then another partial view Model for order model
#{
Layout = null;
}
#model Order;
<input type="text" asp-for="OrderID" />
<input type="text" asp-for="TotalSum" />
then in your main view load both partial view by
<partial name="PersonPartialView" />
<partial name="OrderPartialView" />
I hope you find it helpfull !!
i use ViewBag For Project and Model for task so in this way i am using two model in single view and in controller i defined viewbag's value or data
List<tblproject> Plist = new List<tblproject>();
Plist = ps.getmanagerproject(c, id);
ViewBag.projectList = Plist.Select(x => new SelectListItem
{
Value = x.ProjectId.ToString(),
Text = x.Title
});
and in view tbltask and projectlist are my two diff models
#{
IEnumerable<SelectListItem> plist = ViewBag.projectList;
}
#model List

How to post the selected value of a selectlist to the controller using a view model?

This question has been asked in various forms but none of the answers seem to fit my situation. I am simply trying to retrieve the selected value of a dropdown list in my controller.
Here is my code:
ViewModel.cs
public class ViewModel
{
public ViewModel() {}
public ViewModel(Contact contact, IEnumerable<State> states)
{
this.Contact = contact;
this.States = new SelectList(states, "Id", "Name", contact.StateId);
}
public Contact Contact {get;set;}
public SelectList States {get;set;}
}
Controller.cs
[HttpPost]
public ActionResult Edit(ViewModel viewModel)
{
_contactService.UpdateContact(viewModel.Contact);
return RedirectToAction("Item", new {id = viewModel.Contact.Id});
}
View.cshtml
<button type="submit" onclick="javascript:document.update.submit()"><span>Update</span></button>//aesthic usage.
#{using (Html.BeginForm("Edit", "Controller", FormMethod.Post, new { name = "update" }))
{
#Html.HiddenFor(m => m.Contact.Id)
#Html.LabelFor(m => m.Contact.Name, "Name:")
#Html.TextBoxFor(m => m.Contact.Name)
<label for="state">State:</label>
#Html.DropDownList("state", Model.States)
}
}
Everything works as expected except that no values from the dropdownlist are passed in my posted viewModel to the controller. The edit page and all fields load correctly. The dropdowns bind correctly and have their selected values displayed properly. However, when I post I only get a "Contact" object passed to the controller. The "States" SelectList object is null.
I tried mapping a "StateId" property in my viewModel contstructor but that did not work either. What am I doing wrong?
Thanks.
I hate answering my own questions but based on the multiple issues I had coupled with the myriad of answers available I thought I would summarize my findings.
First off thanks for Filip, his answer did not exactly fix my problem but it led me in the right direction. +1
If you are creating a form for viewing and editing that requires a drop down list, here are some suggestions and gotchas. I will start with a list of parameters that I needed to fit my needs.
Strongly typed views in my view are preferable. Minimize magic strings.
View models should contain as little logic and extraneous elements as possible. There only job should be to facilitate a collection of data objects.
The drop down list should display the selected value.
The selected value should map easily back to the view model on form submit.
This may sound like an obvious and easily obtainable list but for someone new to MVC, it is not. I will revise my code from above with comments. Here is what I did.
ViewModel.cs
public class ViewModel
{
public ViewModel() {}
public ViewModel(Contact contact, IList<State> states)
{
//no need to pass in a SelectList or IEnumerable, just what your service or repository spits out
this.Contact = contact;
this.States = states;
}
public Contact Contact {get;set;}
public IList<State> States {get;set;}
}
Controller.cs //nothing really different than above
public ActionResult Edit(int id)
{
var contact = _contactService.GetContactById(id);
var states = _stateService.GetAllStates();
return View(new ViewModel(contact, states));
}
public ActionResult Edit(ViewModel viewModel)
{
_contactService.UpdateContact(viewModel.Contact);
return RedirectToAction("Edit", new {id = viewModel.Contact.Id });
}
View//thanks goes to Artirto at this post
#{using (Html.BeginForm("Edit", "Controller", FormMethod.Post))
{
#Html.HiddenFor(m => m.Contact.Id)
#Html.DropDownListFor(m => m.Contact.StateId, new SelectList(Model.States, "Id", "Name", #Model.Contact.StateId))
<input type="submit" value="Save" />
}
}
Try using #Html.DropDownListFor instead, if "state" is a part of your model you can use it like this:
#Html.DropDownListFor(m => m.Contact.StateId, Model.States, "-- Please select a State --") where m.State holds the selected value.
Also not to confuse the IEnumerable with the Model, I would put that in the ViewBag / ViewData.
It would look something like this instead:
#Html.DropDownListFor(m => m.Contact.StateId, (IEnumerable<string>)ViewBag.States, "-- Please select a State --")
And in your action that returns this view you will need to initialize the State enumerable to the ViewBag.States property.

Resources