I am working on a ASP.NET MVC3 code to bind the CheckBoxes using the data from the column of my SQL Database present in App_Data directory.
My table SysUser3 contains two columns with values as follows:
ProductName || ProductId
Pencil 1
Eraser 2
Pen 3
Model:
public class StoreModel
{
public List<ProductModel> Products { get; set; }
}
public class ProductModel
{
public string ProductName { get; set; }
public bool Selected { get; set; }
}
Controller:
[HttpGet]
public ActionResult CheckView()
{
var model = new StoreModel
{
Products = m.GetCheckBoxes()
};
return View(model);
}
//GetCheckBoxes method()
public IList<ProductModel> GetCheckBoxes()
{
IList<ProductModel> items = new List<ProductModel>();
using (SqlConnection con = new SqlConnection(#"Data Source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|DeveloperReport.mdf;User Instance=true"))
{
con.Open();
string cmdString = "SELECT ProductName FROM SysUser3";
using (SqlCommand cmd = new SqlCommand(cmdString, con))
{
using (SqlDataReader dataRead = cmd.ExecuteReader())
{
while (dataRead.Read())
{
items.Add(new ProductModel
{
Text = dataRead["ProductName"].ToString()
});
}
}
}
}
return items;
}
View:
#using (Html.BeginForm())
{
<div>
#Html.HiddenFor(x => x.ProductName)
#Html.CheckBoxFor(x => x.Selected)
#Html.LabelFor(x => x.Selected, Model.ProductName)
</div>
}
However, my code isn't working fine and I am not able to see the binding taking place.
I just get a empty checkbox when I run the code.
Can someone tell me what am I doing wrong
Thanks in advance
In your DAL you seem to be defining the item variable to be an List<ProductModel>() and inside the while clause you seem to be adding elements of type RoleModel to this list assigning only the Text property and not the Selected property which is what the checkbox is bound to. You seem to be selecting only the ProductName (SELECT ProductName FROM SysUser3).
There doesn't seem to be a Selected boolean column in your table, so you cannot populate properly this property and thus the generated checkbox in the view will never be checked.
I guess you will have to rethink your database design. But that's another topic.
As far as ASP.NET MVC is concerned, as long as you provide a valid view model to the view:
public ActionResult CheckView()
{
var model = new StoreModel
{
Products = new[]
{
new ProductModel { ProductName = "product 1", Selected = true },
new ProductModel { ProductName = "product 2", Selected = false },
new ProductModel { ProductName = "product 3", Selected = true },
}.ToList()
};
return View(model);
}
no matter where this data comes from, the corresponding checkboxes in the view will be properly bound:
#model StoreModel
#using (Html.BeginForm())
{
#Html.EditorFor(x => x.Products)
<button type="submit">OK</button>
}
and in the corresponding editor template (~/Views/Shared/EditorTemplates/ProductModel.cshtml) which will be rendered for each element of the Products collection of your view model:
#model ProductModel
<div>
#Html.HiddenFor(x => x.ProductName)
#Html.CheckBoxFor(x => x.Selected)
#Html.LabelFor(x => x.Selected, Model.ProductName)
</div>
And then obviously you will have the corresponding POST action which will take your view model as argument and call the underlying DAL to do some processing:
[HttpPost]
public ActionResult CheckView(StoreModel model)
{
// the model.Products collection will be properly bound here
// with the values that the user selected in the form
...
}
Related
My View is bound to this model
public class HomepageViewModel
{
public HomepageViewModel()
{
Regions = new List<TMRegion>();
}
public List<TMRegion> Regions { get; set; }
public int SelectedRegion { get; set; }
public SelectList SelectListRegions
{
get
{
List<SelectListItem> items = new List<SelectListItem>();
foreach (var tmRegion in Regions)
{
items.Add(new SelectListItem()
{
Value = tmRegion.Value.ToString(),
Text = tmRegion.Display
});
}
return new SelectList(items);
}
}
}
My view is like this -
#model ProSell.Admin.Models.ViewModels.HomepageViewModel
#using (Html.BeginForm("Index", "Search", FormMethod.Post, new { viewModel = Model }))
{
#Html.HiddenFor(m=>m.Regions)
#Html.DropDownListFor(model => model.SelectedRegion, Model.SelectListRegions.Items as List<SelectListItem>, "Select a region")
<input type="submit"/>
}
My controller populates Regions like this -
// GET: Search
public async Task<ViewResult> Index(HomepageViewModel viewModel)
{
if (viewModel.Regions.Count == 0)
{
viewModel = new HomepageViewModel();
JavaScriptSerializer js = new JavaScriptSerializer();
viewModel.Regions =
js.Deserialize<TMRegion[]>(
await _ApiConsumer.ExecuteGetMethod("myurlinhere"))
.ToList();
}
return View(viewModel);
}
The Drop down is populated in the view. When i select a region and submit the HomepageViewModel has the SelectedRegion correctly set to whatever id was selected, but the collection of Regions is empty.
How do I maintain the list in the model on submit?
Generally you should attempt to reduce the amount of data the client sends back to the server especially with collections since it's far more efficient for the server to make a database call to retrieve the collection than have it passed back with the form data.
As Stephen said you can re-populate the collections Regions from your controller or if you need it when returning the view due to ModelState error you can add the population code into your model's constructor.
Also you can clean up your collection property like this:
public IEnumerable<SelectListItem> SelectListRegions
{
get
{
return Regions.Select(x => new SelectListItem
{
Text = x.Display,
Value = x.Value.ToString()
});
}
}
and in your View:
#Html.DropDownListFor(model => model.SelectedRegion, Model.SelectListRegions, "Select a region")
I am so confused how to make the dropdownlist to show the selected value.
Model:
public class SampleModel
{
public string State { get; set; }
}
Controller:
public ActionResult EditInformation()
{
ViewBag.State = new SelectList(db.States, "StateName", "StateName");
string userEmail = User.Identity.GetUserName();
Sample model = new SampleModel();
model.State = "Melbourne";
return View(model);
}
View :
#Html.DropdownListFor(m => m.State, ViewBag.State as IEnumerable<SelectListItem>, "-- Select State --")
The list is showing the states just fine, but it doesn't automatically select the value I assigned ("Melbourne"). I have tried using the selectlist constructor to assign the selectedValue, but after doing a lot of research, someone wrote that it is redundant to use the selectlist constructor if you are using Html.DropdownListFor() since you will be using the value assigned to the model.
EDIT:
Here is my db.State model:
public class State
{
[Key]
public int StateId { get; set; }
public string StateName { get; set; }
}
Again to clarify, I want to use StateName as the value and the text for the selectlistitem.
EDIT:
My full action method:
public ActionResult EditInformation()
{
//var states = ndb.States.Select(s => new SelectListItem { Text = s.StateName, Value = s.StateName , Selected = s.StateName == "Jawa Timur" }).ToList();
ViewBag.State = new SelectList(ndb.States, "StateName", "StateName");
ViewBag.City = new SelectList(ndb.Cities, "CityName", "CityName");
string[] countries = { "Indonesia" };
ViewBag.Country = new SelectList(countries);
string userEmail = User.Identity.GetUserName();
try
{
UserInformation userInfo = ndb.UserInformations.Single(m => m.Email == userEmail);
UserAccountViewModel model = new UserAccountViewModel();
model.Address = userInfo.Address;
model.Email = userEmail;
model.FirstName = userInfo.FirstName;
model.LastName = userInfo.LastName;
model.Phone = userInfo.Phone;
model.PostalCode = userInfo.PostalCode;
Debug.Print(userInfo.State);
model.State = userInfo.State;
model.City = userInfo.City;
model.Country = userInfo.Country;
return View(model);
}catch { }
return View();
}
public ActionResult EditInformation(int? id /*this will be passed from your route values in your view*/)
{
State myState = db.States.Find(id)
ViewBag.State = new SelectList(ndb.States, "StateId", "StateName", myState.StateId);
}//I only added this because this is what the question pertains to.
In your EditInformation View you need to have an actionlink to link to the user's id so that you pull up the right information, so:
EditInformation View:
#foreach (var item in Model)
{
#Html.ActionLink("Edit Information", "EditInformation", /*Controller Name if this view is rendered from a different controller*/, new { id = item.id })
}
try this:
public class SampleModel
{
public int Id { get; set; }
public string State { get; set; }
}
Controller:
public ActionResult EditInformation()
{
//Select default value like this (of course if your db.States have an Id):
ViewBag.State = new SelectList(db.States, "Id", "StateName", 1 /*Default Value Id or Text*/);
. . .
return View();
}
SelectList(IEnumerable, String, String, Object) - Initializes a new instance of the SelectList class by using the specified items for the list, the data value field, the data text field, and a selected value.
View:
#Html.DropdownList("State", null, "-- Select State --")
Or Like you do:
#Html.DropdownListFor(m => m.State, ViewBag.State as IEnumerable<SelectListItem>, "-- Select State --")
UPDATE:
You can get Selected text using jQuery like so:
Add #Html.HiddenFor(x => x.State)
#Html.DropdownListFor(m => m.State, ViewBag.State as IEnumerable<SelectListItem>, "-- Select State --", new { id = "stateId" })
#Html.HiddenFor(x => x.State)
JS:
$(function () {
$("form").submit(function(){
var selectedText= $("#stateId :selected").text();
$("#State").val(selTypeText);
});
});
Post:
[HttpPost]
public void UploadDocument(State model)
{
if(ModelState.IsValid)
{
string state = model.State;
}
}
OKAY, So after researching for quite some time, the problem lies in the naming convention. Apparently, you cannot use ViewBag.State for Html.DropdownListFor(m => m.State), this somehow causes the Html.DropdownListFor(m => m.State) to not reading the data properly.
So I fixed the issue from a previous question I asked here, now I am getting what I was looking for but when I hit save it does nothing? No error or postback and nothing saved to the database? The button doesn't do anything in my view?
I have a many to many relationship with Post and Tag in my EF Model. I am trying to assign a post to a tag. I was following this tutorial. However the button does nothing when I click save.
PostController:
public ActionResult EditPostTag(int id = 12) // hardcoded the post for now
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var postsViewModel = new PostsViewModel
{
posts = db.Posts.Include(i => i.Tags).First(i => i.Id == id),
};
if (postsViewModel.posts == null)
return HttpNotFound();
var tagsList = db.Tags.ToList();
postsViewModel.Tags = tagsList.Select(o => new SelectListItem
{
Text = o.Name,
Value = o.Id.ToString()
});
ViewBag.UserID =
new SelectList(db.BlogUsers, "UserID", "Email", postsViewModel.posts.Id);
return View(postsViewModel);
}
Your view does not have a form tag
#model MyBlogger.ViewModel.PostsViewModel
#{
ViewBag.Title = "EditPostTag";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>EditPostTag</h2>
#using (Html.BeginForm()) // add this
{
....
}
Edit (further to comments and some misunderstanding by OP)
Using a view model as you have done is always good practice however you are not taking advantage of it by continuing to use ViewBag and using it to hold a data model instead of including just the properties you need to the view. I recommend it be
public class PostViewModel // its for a single post - not plural?
{
public int ID { get; set; }
[Required(ErrorMessage = "Please enter a title")]
public string Title { get; set; }
[Display(Name = "Tags")] // plus [Required] is at least one tag must be selected
public List<int> SelectedTags { get; set; }
public SelectList TagsList { get; set; }
// Its unclear if you really need the following 2 properties (see notes)
[Display(Name = "User")]
[Required(ErrorMessage = "Please select a user")]
public int UserID { get; set; }
public SelectList UserList { get; set; }
}
Side note: Its a bit unclear why you are allowing the user to select another user to be associated with the Post object. I suspect that when you save the Post you should be just assigning the current user in the controllers POST method
Your controller methods would then be (assume this is PostController)
public ActionResult Edit(int id)
{
Post post = db.Posts.Include(i => i.Tags).FirstOrDefault(i => i.Id == id); // First() will throw an exception is the item is not found
if (post == null) { return HttpNotFound(); }
PostViewModel model = new PostViewModel()
{
ID = post.ID,
Title = post.Title,
SelectedTags = post.Tags.Select(t => t.Id)
}; // include UserId property?
ConfigureEditModel(model);
return View(model);
}
[HttpPost]
public ActionResult Edit(PostViewModel model)
{
if (!ModelState.IsValid)
{
ConfigureEditModel(model);
return View(model);
}
// map your view model to a data model, save it and redirect
}
private void ConfigureEditModel(PostViewModel model)
{
model.TagsList = new SelectList(db.Tags, "Id", "Name");
model.UserList = new BlogUsers(db.Tags, "UserID", "Email"); // ??
}
Side note: Either SelectList or IEnumerable<SelectListItem> is acceptable (I find SelectList easier to read but its a millisecond or two slower because it uses reflection to generate the IEnumerable<SelectListItem>) but there is no point using the 4th parameter as you did with new SelectList(db.BlogUsers, "UserID", "Email", postsViewModel.posts.Id); - your binding to a property and the selected item will be the value of the property, so trying to set the Selected property is just ignored)
And finally the view (simplified to show only helpers without html attributes)
#model MyBlogger.ViewModel.PostViewModel
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
// #Html.HiddenFor(model => model.posts.Id) not required
#Html.LabelFor(m => m.Title)
#Html.TextBoxFor(m => m.Title)
#Html.ValidationMessageFor(m => m.Title)
// Is the UserId property required?
#Html.LabelFor(m => m.UserID)
#Html.DropDownListFor(m => m.UserID, Model.UserList, "Please select")
#Html.ValidationMessageFor(m => m.UserID)
#Html.LabelFor(model => model.SelectedTags)
#Html.ListBoxFor(m => m.SelectedTags, Model.TagsList)
// Add ValidationMessageFor() if at least one should be selected
<input type="submit" value="Save" class="btn btn-default" />
}
Side note: Since the parameter of your method is named id, the value of the id property will be added to the route parameters so it is not necessary to add a hidden input for the view models ID property (the DefaultModelBinder reads route values in addition to form values, so the view models ID property will be correctly bound (to 12 in you case)
I am trying to create a multiple select from a single select drop down menu.
my model originally had:
public int country_id { get; set; }
and my view had:
#Html.DropDownList("country_id", String.Empty)
to change it to multiple select i changed my model to:
public List<Country> country_id { get; set; }
and my view to:
#Html.ListBoxFor(model => model.country_id, ViewBag.ActionsList as MultiSelectList, new { #class = "multiselect", #style = "width: 450px;height:200px" })
the problem i am having is updating my databse using migration since the i am changing int to list, however, it keeps saying
"Cannot drop the index 'dbo.People.IX_country_id', because it does
not exist or you do not have permission."
I do have permission so I am not sure if I am missing something?
My list of countries is coming straight from the country database.
thanks for your inputs.
You need to populate a selectlist in the controller & pass it to the view, something like this:
var countries = from d in db.Countries
select new
{
Id = d.Id,
Name = d.Name
};
// I'd pass this in a model, but use ViewBag if that's what you're familiar with
ViewBag.ActionsList = new SelectList(countries , "Id", "Name");
And in the View:
#Html.DropDownListFor(model => model.country_id, ViewBag.ActionsList)
UPDATE:
You should use a ViewModel for this:
public class CountryList
{
// this may have to be a List<SelectListItems> to work with MultiSelectList - check.
public SelectList Countries{ get; set; }
public List<int> SelectedCountryIds { get; set; }
}
In the controller:
var model = new CountryList
{
SelectList = //assign the selectlist created earlier
}
return View(model);
In the View:
#Html.ListBoxFor(m => m.SelectedCountryIds, new MultiSelectList(#Model.Countries, "Id", "Name", #Model.SelectedCountryIds))
I would like to create a simple drop-down box that displays a list of countries. The data for it comes from the database and is accessed using the entity framework data context. The user should select a country before posting the data back.(simple validation check).
I've created the view model and have also written some code, but I'm not sure about my design and I also need help to complete the code. I've done some search, but I couldn't find a simple way of doing this. I'm still getting the data from context because I am still not sure about how to use repositories. At the moment, I just want the basic drop-down to work without getting too advanced. Please help. Thanks
Updated
View Model - Country.cs
public class Country
{ [Required]
public int Id { get; set; }
public IEnumerable<SelectListItem> Countries { get; set; }
}
Controller
Public ActionResult CountriesDropDown()
{
Models.Country countryModel = new Models.Country();
using (ctx)
{
var model = (from q in ctx.Countries
select new SelectListItem
{
Text = q.CountryId,
Value = q.CountryName
}).ToList();
countryModel.Countries = model;
}
return View("Test",countryModel);
}
Countries View
#using (Html.BeginForm("DoSomething", "Test", FormMethod.Post))
{
#Html.DropDownListFor(model => model.Id, Model.Countries, "Please Select")
#Html.ValidationMessageFor(model => model.Id) //The validation part still Not Working. I want the user to select a country before submitting the form. Please help
<input type = submit value="submit" />
}
[HttpPost]
public ActionResult DoSomething(Models.Country Selectedcountry)
//country.Id holds the value of selected drop-down and it works fine
{
if (ModelState.IsValid)
//I need to do server-side validation and return back to client if Selectedcountry.Id is null (just in case, the client-side validation doesn't work)
{
return View();
}
else
{
return View("Test");
}
}
Thanks
Do this in your controller:
var model = new Country {
// assuming that the country with "id" exists
CountryId = ctx.Countries.Get(id).Id
};
model.Countries =
from q in ctx.Countries
select new SelectListItem {
Text = q.Id,
Value = q.Name
};
return view("countries", model);
Do this in your model
#model Models.Country
#Html.DropDownListFor(model => model.CountryId, model.Countries)
try this:
[Required(ErrorMessage = "Please select a Country")]
public string CountryCode{ get; set; }
public IEnumerable<SelectListItem> CountryList
{
get
{
return Country
.GetAllCountry()
.Select(Country=> new SelectListItem
{
Text = Country.Value,
Value = Country.Value
})
.ToList();
}
}
and then you could add a corresponding validation error message:
#Html.DropDownListFor(model => model.CountryCode, Model.CountryList, "select")
#Html.ValidationMessageFor(model => model.CountryCode)