On a mass-edit form page I display about 50 objects that have some boolean properties as well. The controller receives a FormCollection with all values from the edit page.
public void _EditAll(FormCollection c)
{
int i = 0;
if (ModelState.IsValid)
{
var arrId = c.GetValues("channel.ID");
var arrName = c.GetValues("channel.displayedName");
var arrCheckbox = c.GetValues("channel.isActive");
for (i = 0; i < arrId.Count(); i++)
{
Channel chan = db.Channels.Find(Convert.ToInt32(arrId[i]));
chan.displayedName = arrName[i];
chan.isActive = Convert.ToBoolean(arrCheckbox[i]);
db.Entry(chan).State = EntityState.Modified;
}
db.SaveChanges();
}
}
Now, for checkboxes, MVC creates hidden inputs on the form (otherwise "false" could not be posted back). In the controller, when receiving the FormCollection, this leads to the case that I receive an array of say
50 IDs,
50 names and ..
71 or so values for the checkboxes,
since the hidden checkbox has the same name as the visible one.
What's a good way to handle that and get the proper value of the checkbox?
Sample for editing array of entities that have boolean field.
Entity:
public class Entity
{
public int Id { get; set; }
public bool State { get; set; }
}
Controller:
public ActionResult Index()
{
Entity[] model = new Entity[]
{
new Entity() {Id = 1, State = true},
new Entity() {Id = 2, State = false},
new Entity() {Id = 3, State = true}
};
return View(model);
}
[HttpPost]
public ActionResult Index(Entity[] entities)
{
// here you can see populated model
throw new NotImplementedException();
}
View:
#model Entity[]
#{
using (Html.BeginForm())
{
for (int i = 0; i < Model.Count(); i++ )
{
#Html.Hidden("entities[" + i + "].Id", Model[i].Id)
#Html.CheckBox("entities[" + i + "].State", Model[i].State)
}
<input type="submit"/>
}
}
The only tricky thing is html elements naming.
More info about binding arrays.
I'm converting all arrays containing checkbox-values:
"false" => "false", if not preceded by "true"
Related
after I read data from my database, I try to show those datas in Html.Helper checkbox and I do that. But later when I try to get checked values back to the controller, model always returns null. Here's my controller part:
[HttpGet]
public ActionResult NewClient()
{
HizmetModel hizmetModel = new HizmetModel();
hizmetModel.hizmet = db.Hizmet.ToList<Hizmet>();
return View(hizmetModel);
}
[HttpPost]
public ActionResult NewClientPost(string name, string lastname, string telephone, string plate, HizmetModel hizmet)
{
Musteri musteri = new Musteri();
if (!db.Musteri.Where(x => x.plaka == plate).Any())
{
musteri.isim = name;
musteri.soyisim = lastname;
musteri.telefon = telephone;
musteri.plaka = plate;
db.Musteri.Add(musteri);
db.SaveChanges();
}
Islem islem = new Islem();
IslemHizmet islemhizmet = new IslemHizmet();
islem.giristarihi = DateTime.Now;
islem.plaka = plate;
var selectedHizmet = hizmet.hizmet.Where(x => x.isChecked == true).ToList<Hizmet>();
db.Islem.Add(islem);
db.SaveChanges();
var onprocessplate = db.Islem.Where(x => x.plaka == plate).FirstOrDefault();
foreach (var item in selectedHizmet)
{
islemhizmet.islem_id = onprocessplate.islem_id;
islemhizmet.hizmet_id = item.hizmet_id;
db.IslemHizmet.Add(islemhizmet);
db.SaveChanges();
islemhizmet = new IslemHizmet();
}
TempData["Success"] = "Müşteri başarıyla eklendi...";
return RedirectToAction("CurrentClients", "Admin");
}
This is my model for the list:
public class HizmetModel
{
public List<Hizmet> hizmet { get; set; }
}
I use this model in the cshtml file:
#model otoyikama.Models.Model.HizmetModel
And this is the loop for displaying checkboxes
#for (int i = 0; i < Model.hizmet.Count; i++)
{
<li>
<label>#Model.hizmet[i].hizmetisim</label>
#Html.CheckBoxFor(m => m.hizmet[i].isChecked)
#Html.HiddenFor(m => m.hizmet[i].hizmet_id)
#Html.HiddenFor(m => m.hizmet[i].hizmetisim)
</li>
}
I couldn't figure what's the problem here, my get action works fine, I can see all the data from database but I can't pass them back to controller.
As a first think , u need to create a object in your controller parameters such as like List<int> serviceIDs or List<Service> services so you can keep more data than one.
The html part:
#foreach(item in Model.Service)
{
<label>#item.ServiceName</label><br>
<input type="checkbox" name="services" value="#item.ServiceID">
<input type="hidden" name="services" value="#item.ServiceName">
}
The backend part:
[HttpPost]
public ActionResult NewClientPost(string name, string lastname, string telephone, string plate, List<Service> services)
{
}
when u do in that way, u will able to hold more services than to one and i think u can pass them the controller more easly.
when i face with that stuation, im taking those services ID's and calling them on the controller side like bellow;
The html part:
#foreach(item in Model.Service)
{
<label>#item.ServiceName</label><br>
<input type="checkbox" name="serviceIDs" value="#item.ServiceID">
}
The backend part:
[HttpPost]
public ActionResult NewClientPost(string name, string lastname, string telephone, string plate, List<int> serviceIDs)
{
List<Service> services= new List<Service>();
foreach(var item in serviceIDs)
{
var service=db.Service.Where(x => x.ServiceID == item).Any()
if(service.Count!=0)
{
services.Add(service);
}
}
}
I have three dropdown list:
#Html.DropDownListFor(m => m.Edit.SelectCountryId, Model.Edit.Countries, "Please select", new { id = "CountryID", #class = "form-control", #onchange = "LoadRegions();" })
#Html.DropDownListFor(m => m.Edit.SelectRegionId,Model.Edit.Region, "Please select", new { id = "RegionID", #class = "form-control", #onchange = "LoadCities();" })
#Html.DropDownListFor(m => m.Edit.SelectCityId, Model.Edit.City, "Please select", new { id = "CityID", #class = "form-control" })
I populate second one depending on first one and third depending on second...but when i want to display those dropdown lists for some specific user im able to display just countries
My model:
public int SelectCountryId { get; set; }
public int SelectRegionId { get; set; }
public int SelectCityId {get; set;}
public System.Web.Mvc.SelectList City { get; set; }
public System.Web.Mvc.SelectList Region{ get; set; }
public System.Web.Mvc.SelectList Country{ get; set; }
My controller:
model.Edit.SelectCountryId = user.ContactInformation.First().City.Region.Country.Id;
model.Edit.SelectRegionId = user.ContactInformation.First().City.Region.Id;
model.Edit.SelectCityId = user.ContactInformation.First().City.Id;
I get values on controller but i cant get display them in view
EDIT:
My scripts:
var region = $('#RegionID');
var urlCountries = "/Account/GetRegions";
$.getJSON(urlCountries, { countryId: $('#CountryID').val() }, function (response) {
// clear and add default (null) option
region.empty().append($('<option></option>').val('').text('Please select'));
for (var i = 0; i < response.length; i++) {
region.append($('<option></option>').val(response[i].Id).text(response[i].Name));
}
});
var city = $('#CityID');
var url = "/Account/GetCities"; // use the helper (dont hard code)
$.getJSON(url, { regionId: $('#RegionID').val() }, function (response) {
// clear and add default (null) option
city.empty().append($('<option></option>').val('').text('Please select'));
for (var i = 0; i < response.length; i++) {
city.append($('<option></option>').val(response[i].Id).text(response[i].Name));
}
});
My methods for GetCity() and GetRegions()
public IEnumerable<IRegion> GetRegions(int countryId)
{
Model.Administration.Region[] modelRegions = Model.Administration.Region.FindAll(DetachedCriteria.For<Model.Administration.Region>().Add(Restrictions.Eq("Country.Id", countryId)));
return modelRegions.Select(Region.ConvertFromModel).Cast<IRegion>().ToList();
}
public IEnumerable<ICity> GetCities(int regionId)
{
CityLimited[] modelCities = CityLimited.FindAll(DetachedCriteria.For<CityLimited>().Add(Restrictions.Eq("Region.Id", regionId)));
return modelCities.Select(City.ConvertFromModel).Cast<ICity>().ToList();
}
You need to pass an additional value indicating the selected option in the json data your returning to the view. Your GetRegions() method should look like
public JsonResult GetRegions(int countryId) // return a JsonResult
{
Model.Administration.Region[] modelRegions = Model.Administration.Region.FindAll(DetachedCriteria.For<Model.Administration.Region>().Add(Restrictions.Eq("Country.Id", countryId)));
var options = modelRegions.Select(Region.ConvertFromModel).Cast<IRegion>().ToList();
var selection = 1; // set the value of the selected option here - must match the `ID` property of one of one of the `IRegion` your returning
return Json(new { options = options, selection = selection }, JsonRequestBehavior.AllowGet);
}
Side note: Its not clear what modelRegions.Select(Region.ConvertFromModel).Cast<IRegion>().ToList(); returns but if IRegion contains more that just the ID and Name properties you need for the view, consider creating a collection of anonymous objects so you don't send unnecessary data across the wire
Then the corresponding script should be
var url = '#Url.Action("GetRegions", "Account")'; // use the helper (dont hard code)
var regions = $('#RegionID'); // cache the element
$('#CountryID').change(function() {
$.getJSON(url, { id: $(this).val() }, function(response) {
// clear and add default (null) option
regions.empty().append($('<option></option>').val('').text('Please select'));
$.each(response.options, function(index, item) {
cities.append($('<option></option>').val(item.Value).text(item.Text));
});
regions.val(response.selection); // set the selected value
});
});
and remove the #onchange = "LoadRegions(); from you markup
Ditto for the GetCities() controller method and the corresponding script
I have been stuck on this problem for too long and would love some help.
On a view people can select two items from two radiobutton lists which returns via a FormMethod.Get to the Index event in HomeController.
These 2 values, 'parts and 'use' are queried to return a result and its passed back to the view via a viewbag. However the viewbag returns a line like { Item = Kona, Price = 400.0000, Quantity = 2 } in the view.
Whereas I want to return each item such as item.Item, Item.Price so I can use them individually.
I have tried everything I can find to no avail.
Anonymous classes items also throw red errors
View
foreach(var item in ViewBag.getstock)
{ //Show it and then make a new line with the <BR/>
#item < br / >
//{ Item = Kona, Price = 400.0000, Quantity = 2 }
}
HomeController
public ActionResult Index()
{
//this returns the entire query string
ViewBag.querystring = Request.QueryString;
//if any keys are in the url from the view
if (Request.QueryString.HasKeys())
{
//extract them out so that you can use them
//key = the name such as Part or Use it goes Key & Value this is passed to a Viewbag
//get(o) is getting the value at place 0, the first value in the url
ViewBag.querystringvalue0 = Request.QueryString.Get(0);
ViewBag.querystringvalue1 = Request.QueryString.Get(1);
}
//if there is any query string
if (Request.QueryString.HasKeys())
{
//pass the data to a couple of variables,
var parts = Request.QueryString.Get(0);
var use = Request.QueryString.Get(1);
//put them in a new query and return the results
ViewBag.getstock = from p in Bikeshopdb.Stocks
where p.PartName == parts && (p.Road == use || p.Mtn == use || p.Hybrid == use) select new
{
p.Item, p.Price, p.Quantity
};
}
return View(Bikeshopdb.Stocks.ToList());
Use a ViewModel class to hold the query results and pass back to the view. For example:
HomeController
public class MatchingStock()
{
public int ID { get; set; }
public string Item { get; set; }
public int Price { get; set; }
public int Quantity { get; set; }
}
public ActionResult Index()
{
//...
var list =
(from p in Bikeshopdb.Stocks
where p.PartName == parts &&
(p.Road == use || p.Mtn == use || p.Hybrid == use)
select new MatchingStock() {
ID = p.ID,
Item = p.Item,
Price = p.Price,
Quantity = p.Quantity}).ToList();
ViewBag.getstock = list;
//...
}
View
#foreach (var item in (List<MatchingStock>)ViewBag.getstock)
{
#item.Item #item.Price #item.Quantity
<br />
}
I have created an entity data model from my database. however in certain areas of the application i need to pass two models. thus i create a third model which has as properties the objects of each required model.
In the scenario, i want to use one model just to show some data to the user and the other is to be populated by the user using form elements. therefore, i create a constructor in my custom model to populate it. here's the code:
THE CUSTOM MODEL
public class ordersModel
{
public ordersModel(order or)
{
this.prods = new order_products();
this.new_order = new order();
this.new_order.customer_id = or.customer_id;
this.new_order.my_id = or.my_id;
this.new_order.my_order_id = or.my_order_id;
this.new_order.order_date = or.order_date;
this.new_order.order_status_id = or.order_status_id;
}
public order new_order { get; set; }
public order_products prods { get; set; }
}
IT IS USED IN THE CONTROLLER AS FOLLOWS:
public ActionResult Create()
{
order or = new order();
// Store logged-in user's company id in Session
//or.my_id = Session["my_id"].ToString();
//do something to allow user to select customer, maybe use ajax
or.customer_id = "123";
or.order_amount = 0;
or.my_id = "74973f59-1f6c-4f4c-b013-809fa607cad5";
// display date picker to select date
or.order_date = DateTime.Now.Date;
// fetch statuses from database and show in select list box
or.order_status_id = 1;
return View(or);
}
//
// POST: /Orders/Create
[HttpPost]
public ActionResult Create(order or)
{
using (invoicrEntities db = new invoicrEntities())
{
var temp = db.last_order_number.SingleOrDefault(p => p.my_id == or.my_id);
if (temp != null)
{
or.my_order_id = temp.my_order_id + 1;
if (ModelState.IsValid)
{
ordersModel ord = new ordersModel(or);
db.orders.AddObject(or);
temp.my_order_id = temp.my_order_id + 1;
//TempData["my_order_id"] = or.my_order_id;
db.SaveChanges();
return RedirectToAction("AddProducts", ord);
//return RedirectToAction("AddProducts", new { id = or.my_order_id });
}
return View(or);
}
return RedirectToAction("someErrorPageDueToCreateOrder");
}
}
public ActionResult AddProducts()
{
using (invoicrEntities db = new invoicrEntities())
{
//string my_id = TempData["my_id"].ToString();
//string my_order_id = TempData["my_order_id"].ToString();
string my_id = "74973f59-1f6c-4f4c-b013-809fa607cad5";
int my_order_id = 1;
//Int64 my_order_id = Convert.ToInt64(RouteData.Values["order_id"]);
// Display this list in the view
var prods = db.order_products.Where(p => p.my_id == my_id).Where(p => p.my_order_id == my_order_id).ToList();
var or = db.orders.Where(p => p.my_id == my_id).Where(p => p.my_order_id == my_order_id).ToList();
if (or.Count == 1)
{
//ViewData["name"] = "sameer";
ViewData["products_in_list"] = prods;
ViewData["order"] = or[0];
return View();
}
return RedirectToAction("someErrorPageDueToAddProducts");
}
}
[HttpPost]
public ActionResult AddProducts(order_products prod)
{
prod.my_id = "74973f59-1f6c-4f4c-b013-809fa607cad5";
// find a way to get the my_order_id
prod.my_order_id = 1;
return View();
}
THIS ALL WORKS OUT WELL, UNTIL IN THE "ADDPRODUCTS" VIEW:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<invoicr.Models.ordersModel>" %>
AddProducts
<h2>AddProducts</h2>
<%: Model.new_order.my_id %>
the above statement gives an error
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.
what am i doing wrong here?
You seem to be passing the wrong model when redisplaying your Create view.
Try passing the ord instance which is of type ordersModel and which is what your view is strongly typed to:
public ActionResult Create(order or)
{
using (invoicrEntities db = new invoicrEntities())
{
var temp = db.last_order_number.SingleOrDefault(p => p.my_id == or.my_id);
if (temp != null)
{
or.my_order_id = temp.my_order_id + 1;
ordersModel ord = new ordersModel(or);
if (ModelState.IsValid)
{
db.orders.AddObject(or);
temp.my_order_id = temp.my_order_id + 1;
db.SaveChanges();
return RedirectToAction("AddProducts", ord);
}
return View(ord);
}
return RedirectToAction("someErrorPageDueToCreateOrder");
}
}
UPDATE:
Now that you have shown your AddProducts action you are not passing any model to the view although your view expects an ordersModel instance. So don't just return View();. You need to pass an instance of ordersModel:
if (or.Count == 1)
{
ViewData["products_in_list"] = prods;
ViewData["order"] = or[0];
ordersModel ord = new ordersModel(or[0]);
return View(ord);
}
I am using a checkboxlist helper and dynamically binding it..Now i want to maintain the state of checkboxes?
public ActionResult Step3()
{
CustomerQuestion _Cust = new CustomerQuestion();
//Retrieve the answer from database by siteid
var Questions = QAService.GetAllAnswer(1);
var Questionscount = QAService.GetAllAnswer(1).Count();
_Cust.Question18 = Questions.Where(s => s.QuestionID == 18);
return View(_Cust);
}
and here is the view
<%= Html.CheckBoxList("Question18", new SelectList(Model.Question18, "AnswerID", "Answer"))%>
show the code so we can help you out.
I guess you want to maintain the checkboxes when you return the view and there is formvalidation.
did you return your object in the view?
EDIT:
You cant make more than 1 value selected in a SelectList. Better is to use List<SelectListItem>
I use this code.
public static IList<SelectListItem> ToSelectList<T>(this IEnumerable<T> itemsToMap, Func<T, string> textProperty, Func<T, string> valueProperty, Predicate<T> isSelected) {
var result = new List<SelectListItem>();
foreach (var item in itemsToMap) {
result.Add(new SelectListItem {
Value = valueProperty(item),
Text = textProperty(item),
Selected = isSelected(item)
});
}
return result;
}
then
ViewData["test"]= _Cust.Question18.ToSelectList(q=>q.Answer, q=>q.AnswerID, q=>someListOfAnswerIDS.Contains(q.AnswerID));
<%= Html.CheckBoxList("Question18", ViewData["test"] as List<SelectListItem>)%>
public ActionResult Step3()
{
CustomerQuestion _Cust = new CustomerQuestion();
//Retrieve the answer from database by siteid
var Questions = QAService.GetAllAnswer(1);
var Questionscount = QAService.GetAllAnswer(1).Count();
_Cust.Question18 = Questions.Where(s => s.QuestionID == 18);
return View(_Cust);
}
and here is the view
i am binding all the languages from the database in this list.
if there is some validation error on this page than i want to maintain the check state of
the check boxes