MVC trying to pass model from razor view to controller - asp.net-mvc

So my story is that I am having trouble with the post to the controller, the view seems to work fine. When the postback happens the tm.BookId is 0 (should be 1) and the list count is 0. First I will display the model:
public class TransferModel
{
public TransferModel()
{
cbItems = new List<CheckBoxItem>();
}
public List<CheckBoxItem> cbItems {get;set;}
public int BookId;
public class CheckBoxItem
{
public int AttributeId { get; set; }
public string Attribute { get; set; }
public bool Selected { get; set; }
}
}
The Controller part:
public ActionResult AddAttributes(int id = 0)
{
db.transMod.BookId = id;
BookInfo book = db.BookInfoes.Find(id);
var latts = db.BookAtts.ToList();
foreach (BookAtt ba in latts)
{
db.transMod.cbItems.Add(new TransferModel.CheckBoxItem { Attribute = ba.Attribute, AttributeId = ba.BookAttId, Selected = false });
}
List<BookAtt> atInList = book.BookAtts.ToList();
foreach (TransferModel.CheckBoxItem cb in db.transMod.cbItems)
{
if (atInList.Exists(item => item.Attribute == cb.Attribute))
cb.Selected = true;
}
return View(db.transMod);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AddAttributes(TransferModel tm)
{
List<BookAtt> atPool = db.BookAtts.ToList();
BookInfo book = db.BookInfoes.Find(tm.BookId);
foreach (TransferModel.CheckBoxItem sel in tm.cbItems)
{
if (sel.Selected)
book.BookAtts.Add(atPool.Find(item1 => item1.Attribute == sel.Attribute));
}
db.SaveChanges();
return RedirectToAction("AddAttributes");
}`enter code here`
And finally the view:
#model BrightStar.Models.TransferModel
#{
ViewBag.Title = "Update Attributes";
}
<h2>Add Attributes</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
<table>
#Html.HiddenFor(model => Model.BookId)
#Html.HiddenFor(model => Model.cbItems)
#foreach (var itm in Model.cbItems)
{
<tr>
<td>#Html.HiddenFor(mo => itm.AttributeId)</td>
<td>#Html.CheckBoxFor(mo => itm.Selected)</td>
<td>#Html.DisplayFor(mo => itm.Attribute)</td>
</tr>
}
</table>
<p>
<input type="submit" value="Save" />
</p>
}
enter code here

Model binding doesn't happen automatically, items needs to be in certain format to get binded to list properties in POST actions. Check this out.
Try checking out the value of BookId property in the DOM to confirm it is 1, otherwise it should bind normally.

You should reference your model's properties in helpers to correctly generate names for your controls:
#Html.HiddenFor(model => Model.cbItems)
should be
#Html.HiddenFor(model => model.cbItems)

Related

How to shorten the url when using pagination and filtering with multiple checkboxes

I am using PagedList for server side paging, and also have a textbox in the view for filtering the data, along with checkboxes to determine which fields in my model to filter based on the search text.
My current code is
View model
public class SearchPagingViewModels
{
public IPagedList<AllResolution> Resolutions { get; set; }
public string Keyword { get; set; } // serach text
public bool IsResYearChecked { get; set; } // used to filter the ResolutionYear field
public bool IsResNumChecked { get; set; } // used to filter the ResolutionNumber field
public bool IsResTextChecked { get; set; } // used to filter the ResolutionText field
}
Controller
public ViewResult Index(int? page string keyword, bool? isResYearChecked, bool? isResNumChecked, bool? isResTextChecked)
{
int pageSize = 25;
int pageNumber = (page ?? 1);
bool searchYear = isResYearChecked.GetValueOrDefault();
....
IQueryable<> resolutions = db.AllResolutions;
if (searchKeyword != null)
{
if (searchYear)
{
resolutions = resolutions.Where(x => x.ResolutionYear.Contains(searchKeyword));
}
....
}
resolutions = resolutions.OrderBy(c => c.ResolutionYear).ThenBy(c => c.ResolutionNumber);
SearchPagingViewModels model = new SearchPagingViewModels
{
Keyword = keyword,
IsResYearChecked = searchYear,
....
Resolutions = resolutions.ToPagedList(pageNumber, pageSize)
};
return View(model);
}
View
#model SearchPagingViewModels
....
#using (Html.BeginForm("Index", "Resolutions", FormMethod.Get))
{
#Html.LabelFor(m => m.Keyword)
#Html.TextBoxFor(m => m.Keyword)
#Html.LabelFor(m => m.IsResYearChecked)
#Html.CheckBoxFor(m => m.IsResYearChecked)
// .. ditto for IsResNumChecked etc
<input type="submit" value="search" />
}
<table>
<thead>
....
</thead>
<tbody>
#foreach (var task in Model.Resolutions)
{
// .... build table rows
}
</tbody>
</table>
#Html.PagedListPager(Model.Resolutions, page => Url.Action("Index", new { page, Keyword = Model.Keyword, IsResYearChecked = Model.IsResYearChecked, IsResNumChecked = IsResNumChecked IsResTextChecked = Model.IsResTextChecked }))
While this works, the issue is that the for generates a long and ugly query string, for example
.../Index?Keyword=someText&IsResYearChecked=true&IsResYearChecked=false&IsResNumChecked=false&IsResTextChecked=true&IsResTextChecked=false
And now I want to add additional bool properties for filtering the records making it even worse and potentially exceeding the query string limit.
Is there a way to shorten the URL? Would this be related to routing? Would a new ViewModel be in order to accomplish this?
Your could replace all your bool properties with an enum marked with the [Flags] attribute where each value in the enum represents a property in your model to search.
[Flags]
public enum FilterProperties
{
None = 0,
ResolutionYear = 1,
ResolutionNumber = 2,
ResolutionText = 4,
.... // more properties
}
and the view model will be
public class SearchPagingViewModels
{
public string Keyword { get; set; }
public FilterProperties Filter { get; set; }
public IPagedList<AllResolution> Resolutions { get; set; }
}
The controller method then becomes
public ViewResult Index(int? page string keyword, FilterProperties filter = FilterProperties.None)
{
IQueryable<AllResolution> resolutions = db.AllResolutions;
if (searchKeyword != null)
{
if (filter.HasFlag(FilterProperties.ResolutionYear)
{
resolutions = resolutions.Where(x => x.ResolutionYear.Contains(feyword));
}
// .... more if blocks for other enum values
}
resolutions = resolutions.OrderBy(c => c.ResolutionYear).ThenBy(c => c.ResolutionNumber);
SearchPagingViewModels model = new SearchPagingViewModels
{
Keyword = keyword,
Filter = filter,
Resolutions = resolutions.ToPagedList(pageNumber, pageSize)
};
return View(model);
}
You view will then be
#using (Html.BeginForm("Index", "Resolutions", FormMethod.Get))
{
#Html.LabelFor(m => m.Keyword)
#Html.TextBoxFor(m => m.Keyword)
#Html.ValidationMessageFor(m => m.Keyword)
#Html.HiddenFor(m => m.Filter)
foreach (Enum item in Enum.GetValues(typeof(Tables.Controllers.FilterProperties)))
{
if (item.Equals(Tables.Controllers.FilterProperties.None))
{
continue;
}
<div>
<label>
<input type="checkbox" value="#((int)(object)item)" checked=#Model.Filter.HasFlag(item) />
<span>#item</span>
</label>
</div>
}
<span id="filtererror" class="field-validation-error" hidden >Please select at least one property to search</span>
<input type="submit" value="Search" />
}
<table>
....
</table>
#Html.PagedListPager(Model.Resolutions, page => Url.Action("Index", new { page, Keyword = Model.Keyword, Filter = (int)Model.Filter }))
And then use javascript to andles the forms .submit() event to update the hidden input value for Filter (note I have also assumed that you want at least one checkbox selected if the value of Keyword is not null)
<script>
var checkboxes = $('input:checkbox');
var keyword = $('#Keyword');
$('form').submit(function () {
var filter = 0;
// validate at least one checkbox must be checked if Keyword has a value
if (keyword.val() && checkboxes.filter(':checked').length == 0) {
$('#filtererror').show();
return false;
}
$.each(checkboxes, function () {
if ($(this).is(':checked')) {
filter += Number($(this).val());
}
// disable checkboxes to prevent their value being added to the query string
$(this).prop('disabled', true);
})
$('#Filter').val(filter);
})
checkboxes.click(function () {
if (keyword.val() && checkboxes.filter(':checked').length == 0) {
$('#filtererror').show();
} else {
$('#filtererror').hide();
}
})
</script>
Your URL (based on ResolutionYear and ResolutionText being checked) will now be
.../Index?Keyword=someText&Filter=5
instead of
.../Index?Keyword=someText&IsResYearChecked=true&IsResYearChecked=false&IsResNumChecked=false&IsResTextChecked=true&IsResTextChecked=false

ASP.NET MVC 5 Html.CheckboxFor only return default value on post

I have read the tutorials and prepared a list of checkboxes for the page. When the form is submitted, the Selected property only get the value false.
Is there something I missed?
The Model
public class SelectStudentModel
{
public int StudentID { get; set; }
public string CardID { get; set; }
public string Name { get; set; }
public bool Selected { get; set;}
}
The ViewModel
public class SelectStudentViewModel
{
public List<SelectStudentModel> VMList;
public SelectStudentViewModel()
{
VMList = SelectStudentModel.GETStudent();
}
}
The View
#using Student.Models
#model SelectStudentViewModel
#using (Html.BeginForm("AddStudent", "SectionStudent", FormMethod.Post, new { #role = "form" }))
{
#{ for (int i = 0; i < Model.VMList.Count(); i++)
{
<tr>
<td>#Html.CheckBoxFor(m => m.VMList[i].Selected)</td>
<td>#Html.DisplayFor(model => model.VMList[i].Name)</td>
</tr>
}
}
<input type="submit" value="submit" />
}#* end form *#
The Controller for posted data
[HttpPost]
public ActionResult AddStudent(SelectStudentViewModel model)
{
foreach (SelectStudentModel m in model.VMList)
{
Console.Write(m.Selected.ToString());
}
return PartialView("StudentSelectForm", model);
}
VMList is a field in your SelectStudentViewModel model. You need to change it to a property (with a getter/setter) so the DefaultModelBinder can set the values
public class SelectStudentViewModel
{
public List<SelectStudentModel> VMList { get; set; } // change
public SelectStudentViewModel()
{
VMList = SelectStudentModel.GETStudent();
}
}
Side note: Suggest you change #Html.DisplayFor(model => model.VMList[i].Name) to #Html.LabelFor(m => m.VMList[i].Selected, Model.MList[i].Name) so that you get a label associated with the checkbox

For each loop with submitting form

Model Room:
public class Room
{
public int Id { get; set; }
public string NumberRoom { get; set; }
public double CostPerNight { get; set; }
public virtual Category Category { get; set; }
}
My view model code
public class RoomModel
{
public IList<Room> Rooms { get; set; }
}
My Razor code:
#model hotel.Models.RoomModel
#using (Html.BeginForm("ComfortLevelView", "Category"))
{
for (int i = 0; i < Model.Rooms.Count(); i++)
{
<table class="simple-little-table" cellspacing='0'>
<tr>
<td>#Html.DisplayFor(m => Model.Rooms[i].NumberRoom) </td>
<td>#Html.DisplayFor(m => Model.Rooms[i].Categoryid)</td>
<td>#Html.DisplayFor(m => Model.Rooms[i].NumberOfSeats) </td>
<td>
#{ var result = Model.Rooms[i].CostPerNight * numberNights; }
<p>#ViewBag.NumberNights ночей</p>:#result
</td>
<td>
<input type="submit" id="submit" value="Booking" />
</td>
</tr>
</table>
</div>
}
}
Controller:
public ActionResult ComfortLevelView(int NumberNights, int CategoryId, int NumberPeoples ,DateTime SelectedDate)
{
IRoomService roomService = new RoomService();;
return View(roomService.GetRoomsByCategory(CategoryId, SelectedDate, NumberNights, NumberPeoples));
}
[HttpPost]
public ActionResult ComfortLevelView(RoomModel model)
{
//
}
The model item passed into the dictionary is of type 'System.Data.Entity.Infrastructure.DbQuery`1[Hotel.BusinessObject.Room]', but this dictionary requires a model item of type 'hotel.Models.RoomModel'.
The error message is self explanatory. You have this in your view
#model hotel.Models.RoomModel
but you pass an instance of System.Data.Entity.Infrastructure.DbQuery<Hotel.BusinessObject.Room> to your view because of this line of code in your controller
return View(roomService.GetRoomsByCategory(CategoryId, SelectedDate, NumberNights, NumberPeoples));
You need to pass an instance of RoomModel instead of System.Data.Entity.Infrastructure.DbQuery<Hotel.BusinessObject.Room>. I would suggest changing your controller code to below
public ActionResult ComfortLevelView(int NumberNights, int CategoryId, int NumberPeoples, DateTime SelectedDate)
{
IRoomService roomService = new RoomService();
var rooms = roomService.GetRoomsByCategory(CategoryId, SelectedDate, NumberNights, NumberPeoples);
RoomModel model = new RoomModel();
model.Rooms = rooms.ToList();
return View(model);
}

Values not being posted from Partial View MVC4

For some reason my partial view is always returning null values to the controller. I've tried implementing this in several different ways (including editor templates) but here's my latest.
Basically I'm rendering a view with a jquery ui datepicker. When the user picks a date I then render my partial view. The partial view is being rendered correctly but when I submit the partial view it's returning a null object to the controller
View Model:
public class Attendance
{
public int RecordId { get; set; }
public int GroupId { get; set; }
public int MemberId { get; set; }
public string MemberName { get; set; }
public System.DateTime Date { get; set; }
public bool Present { get; set; }
}
Parent View:
#{
ViewBag.Title = "Take Attendance";
}
<h2>Take Attendance</h2>
#Html.Hidden("GroupId", (int)ViewBag.GroupId)
<div>
#Html.Label("Select A Date")
#Html.JQueryUI().Datepicker("attendanceDate",ViewBag.AttendanceDate)
.OnSelect("attendanceDate.onSelect")
</div>
<div id="attendanceRecords">
</div>
#section scripts
{
<script src="#Url.Content("../../Scripts/Views/TakeAttendance.js")" type="text/javascript"></script>
}
Partial View:
#model IEnumerable<AT.Manager.Models.Attendance>
#using (Html.BeginForm("TakeAttendance", "Groups")) {
#Html.ValidationSummary(true)
<table>
<tr>
<th>Name</th>
<th>Present?</th>
</tr>
#for (int i = 0; i < Model.Count(); i++ )
{
<tr>
<td>#Html.DisplayFor(model => Model.ElementAt(i).MemberName)</td>
<td>#Html.EditorFor(model => Model.ElementAt(i).Present)</td>
<td>#Html.HiddenFor(model => Model.ElementAt(i).GroupId)</td>
<td>#Html.HiddenFor(model => Model.ElementAt(i).MemberId)</td>
<td>#Html.HiddenFor(model => Model.ElementAt(i).RecordId)</td>
</tr>
}
</table>
<p>
<input type="submit" value="Create" />
</p>
}
Controller Action:
public ActionResult TakeAttendance(IEnumerable<Attendance> records)
{
if (ModelState.IsValid)
{
var newRecords = records.Select(x => new da.Attendance() { Id = x.RecordId, GroupId = x.GroupId, MemberId = x.MemberId, Date = x.Date });
AttendanceRepository attendanceRepo = new AttendanceRepository();
newRecords.Each(x => attendanceRepo.InsertOrUpdate(x));
return RedirectToAction("Index");
}
else
{
return View();
}
}
Any suggestions would be much appreciated.
You could try passing IList<Attendance> records to the view and then manipulate it like this:
for (int i = 0; i < Model.Count(); i++ )
{
<tr>
<td>#Html.DisplayFor(model => Model[i].MemberName)</td>
<td>#Html.CheckBoxFor(model => Model[i].Present)</td>
// ...
</tr>
}
Be sure to change #model IEnumerable<AT.Manager.Models.Attendance> to reflect the change of collection to IList.

how can get data in drop down list from data base in mvc3.?

I am designing a simple webpage using MVC3 in asp.net. I prepared a database using Sql Server, and i have attached this database in App_Data. There is a table employee in that database.
i want to get name of employee in a drop down list. so i can select name of employee.
so please suggest me what will be model,view and controller code for accessing employee name in drop down list.
I would start by designing a view model which will hold the data:
public class EmployeeViewModel
{
public string SelectedEmployeeName { get; set; }
public IEnumerable<SelectListItem> Employees { get; set; }
}
then a controller:
public class HomeController: Controller
{
public ActionResult Index()
{
IEnumerable<Employee> employees = GetEmployeesFromDb();
var model = new EmployeeViewModel
{
Employees = employees.Select(x => new SelectListItem
{
Value = x.Name,
Text = x.Name
})
};
return View(model);
}
[HttpPost]
public ActionResult Index(EmployeeViewModel model)
{
return Content("Selected employee name: " + model.SelectedEmployeeName, "text/plain");
}
}
and finally the strongly typed view:
#model EmployeeViewModel
#using (Html.BeginForm())
{
#Html.DropDownListFor(
x => x.SelectedEmployeeName,
new SelectList(Model.Employees, "Value", "Text")
)
<input type="submit" value="OK" />
}
1) Create a Method to populate the list from DB
2) Set a ViewModel to hold the List and selected value
//MODEL
public List<SelectListItem> CategoriesSelectList()
{
var query = from c in _yourRepository.GetAll()
select c;
List<SelectListItem> obj = new List<SelectListItem>();
foreach (var item in query)
{
var result = new SelectListItem();
result.Text = item.name;
result.Value = item.id.ToString();
obj.Add(result);
}
return obj;
}
//VIEWMODEL
public class ViewModel
{
[DisplayName("Category")]
public int categoryId { get; set; }
public List<SelectListItem> CategoryList()
{
return new Model().CategoriesSelectList();
}
}
//CONTROLLER
public ActionResult Create()
{
//set the id for the VIEWMODEL property, if necesary
var e = new ViewModel();
e.categoryId = 1;//set the selected value
return View(e);
}
//VIEW
<div class="editor-label">
<%: Html.LabelFor(model => model.categoryId) %>
</div>
<div class="editor-field">
<%: Html.DropDownListFor(model => model.categoryId,Model.CategoryList()) %>
<%: Html.ValidationMessageFor(model => model.categoryId) %>
</div>

Resources