NonFactors Paging issue following search - asp.net

I am using NonFactors mvcgrid6 (https://mvc6-grid.azurewebsites.net/) in my project and Paging is breaking following a search. When selecting Page 2 for example after a search has been made the whole data set is returned and not just the searched data.
Looking for a way to persist the searched for data when using the paging. Is this possible to do with this plugin?
Controller
public IActionResult Index(IndexResidentVM searchValues)
{
var indexVm = new IndexResidentVM()
{
SearchItems = _residentService.GetAllResidents(searchValues).AsQueryable(),
Groups = _groupService.GetGroups(),
Users = _userService.GetUsers()
};
return View(indexVm);
}
Index
#using CommunityContact.Core.Enums
#using NonFactors.Mvc.Grid
#model CommunityContact.Core.ViewModels.Resident.IndexResidentVM
#{
ViewData["Title"] = "Residents";
}
<h3>Allocated Residents</h3>
<form asp-action="Index" id="indexSearchForm">
<div class="form-row">
<div class="form-group col-md-3" style="margin-top: 15px;">
<label class="col-md-3">Group: </label>
<select id="indexGroupDdl" asp-for="Group" asp-items="#Model.Groups.Select(p => new SelectListItem()
{
Value = p.Name,
Text = p.Name
})" class="form-control-sm col-md-8">
<option selected="selected" value="">Please select </option>
</select>
</div>
<div class="form-group col-md-4" style="margin-top: 15px;">
<label class="col-md-4">RAG Status:</label>
<select id="indexRagStatusDdl" asp-for="RagStatus" asp-items="Html.GetEnumSelectList<Enums.RagStatus>()" class="form-control-sm col-md-6">
<option selected="selected" value="">Please select </option>
</select>
</div>
<div class="form-group col-md-4" style="margin-top: 15px;">
<label class="col-md-4">Allocated to: </label>
<select id="indexAllocatedToDdl" asp-for="User" asp-items="#Model.Users.Select(p => new SelectListItem()
{
Value = p.Name,
Text = p.Name
})" class="form-control-sm col-md-6">
<option selected="selected" value="">Please select </option>
</select>
</div>
<div class="col-md-1" style="margin-top: 15px;">
<button id="clearSearch" class="btn-sm btn-primary clear-search" title="Clear Search"><span class="fa fa-times"></span></button>
</div>
</div>
</form>
<hr />
#(Html.Grid(Model.SearchItems)
.Build(columns =>
{
columns.Add(model => model.Id).Css("hidden-column");
columns.Add(model => model.ResidentId).Titled("Resident ID");
columns.Add(model => model.Forename1).Titled("Forename");
columns.Add(model => model.Surname).Titled("Surname");
columns.Add(model => model.Group).Titled("Group");
columns.Add(model => model.CallBackDue.HasValue ? model.CallBackDue.Value.ToShortDateString() : string.Empty).Titled("Call Back Due");
columns.Add(model => model.RagStatus).Titled("RAG Status");
columns.Add(model => model.AllocatedTo).Titled("Allocated to");
columns.Add(model => Html.ActionLink(" ", "Edit", "Resident", new { id = model.Id }, new { #class = "fa fa-edit", title = "Edit" })).Css("archive-column-width");
columns.Add(model => Html.ActionLink(" ", " ", new {}, new {#class="fa fa-archive", href ="#", onclick="return archivePrompt('"+ model.Id +"')", title ="Archive"})).Css("archive-column-width");
})
.Pageable()
.RowAttributed(model => new {#class = model.RagStatus == Enums.RagStatus.Red.ToString() ? "rag-status-red" : model.RagStatus == Enums.RagStatus.Amber.ToString() ? "rag-status-amber" : model.RagStatus == Enums.RagStatus.Green.ToString() ? "rag-status-green" : null }))
#*#Html.AjaxGrid(Url.Action("Index"))*#
#section Scripts
{
<script>
// MvcGrid
[].forEach.call(document.getElementsByClassName('mvc-grid'), function (element) {
new MvcGrid(element);
});
</script>
}
View Model
public class IndexResidentVM
{
public int Id { get; set; }
public string ResidentId { get; set; }
public string Forename1 { get; set; }
public string Surname { get; set; }
public string Group { get; set; }
public string User { get; set; }
public Enums.Enums.RagStatus RagStatus { get; set; }
public IQueryable<ResidentSearchItemVM> SearchItems { get; set; }
public IEnumerable<EditUserVM> Users { get; set; }
public IEnumerable<GroupVM> Groups { get; set; }
}

This was solved by making the call to the controller a GET request and not POST. Adding the following to my form tag fixed it for me -
method="GET"

Related

Model coming through as null in controller

I have a relatively simple form that's posting to a controller, yet the model is coming through as null every time and I don't understand why. I'm using Umbraco CMS (so please ignore any Umbraco references if they're not relevant) and jquery combined with an image plugin (Filepond) to post the data. This is my model:
public class PropertyViewModel: PublishedContentWrapped
{
public PropertyViewModel(IPublishedContent content, IPublishedValueFallback publishedValueFallback) : base(content, publishedValueFallback)
{
this.Countries = GetCountries();
}
[Required]
public string Country { get; set; }
[Required]
public string DisplayName { get; set; }
[Required]
public string Summary { get; set; }
[Required]
public string Description { get; set; }
[Required(ErrorMessage = "Please select at least one image")]
public List<IFormFile> Images { get; set; }
public SelectList Countries { get; set; }
public SelectList GetCountries()
{
List<string> countryNames = Bia.Countries.Iso3166.Countries.GetAllActiveDirectoryNames();
var countries = new List<SelectListItem>();
foreach (var country in countryNames)
countries.Add(new SelectListItem { Text = country, Value = country });
return new SelectList(countries, "Text", "Value");
}
}
Here is the controller:
[HttpPost]
[ValidateAntiForgeryToken]
[ValidateUmbracoFormRouteString]
public async Task<IActionResult> HandleAddProperty(PropertyViewModel model)
{
if (ModelState.IsValid == false)
{
return CurrentUmbracoPage();
}
// Carry on execution..
}
Finally, here is my view. I should mention that model is a new instance of the PropertyViewModel class:
#using (Html.BeginUmbracoForm<PropertySurfaceController>("HandleAddProperty", null, new { #id = "form-add" }))
{
<div asp-validation-summary="All" class="text-danger"></div>
<div class="mb-3">
<select asp-for="#Model.Country" asp-items="Model.Countries" class="form-select">
<option value="">Select country</option>
</select>
<span asp-validation-for="#Model.Country" class="form-text text-danger"></span>
</div>
<div class="mb-3">
<label asp-for="#Model.DisplayName" class="form-label"></label>
<input asp-for="#Model.DisplayName" class="form-control" aria-required="true" />
<span asp-validation-for="#Model.DisplayName" class="form-text text-danger"></span>
</div>
<div class="mb-3">
<label asp-for="#Model.Summary" class="form-label"></label>
<textarea asp-for="#Model.Summary" class="form-control" aria-required="true"></textarea>
<span asp-validation-for="#Model.Summary" class="form-text text-danger"></span>
</div>
<div class="mb-3">
<label asp-for="#Model.Description" class="form-label"></label>
<textarea asp-for="#Model.Description" class="form-control" aria-required="true"></textarea>
<span asp-validation-for="#Model.Description" class="form-text text-danger"></span>
</div>
<div class="mb-3">
<input type="file" class="filepond" id="file" name="filepond" multiple data-max-file-size="5MB" data-max-files="10" accept="image/png, image/jpeg, image/gif">
</div>
<button type="submit" class="btn btn-primary" id="submit">Submit</button>
}
<script>
$(document).ready(function(e){
FilePond.registerPlugin(
FilePondPluginImageResize,
FilePondPluginFileValidateSize
);
var pond = FilePond.create(
document.querySelector('#file'), {
allowMultiple: true,
instantUpload: false,
allowProcess: false,
imageResizeTargetWidth: 1280,
imageResizeUpscale: false,
imageResizeMode: 'contain'
});
$("#form-add").submit(function (e) {
e.preventDefault();
var formdata = new FormData(this);
// append FilePond files into the form data
var pondFiles = pond.getFiles();
for (var i = 0; i < pondFiles.length; i++) {
formdata.append('Images', pondFiles[i].file);
}
console.log('formdata', formdata);
$.ajax({
url: "/umbraco/surface/PropertySurface/HandleAddProperty",
data: formdata,
dataType: 'JSON',
processData: false,
contentType: false,
method:"post"
}).done(function (response) {
// todo
});
})
});
</script>
When I inspect in Chrome to see what data is being sent to the server I can see the following:
So I can see the fields are matching the model, yet it's still coming through as null. I can't figure out where it's failing.
I've tried using [FromBody] and [FromForm] with the controller parameter, but it made no difference.
Is anyone able to spot where I'm going wrong?
I think the second parameter in BeginUmbracoForm is supposed to be the model, not null. So:
#using (Html.BeginUmbracoForm<PropertySurfaceController>("HandleAddProperty", new { #id = "form-add" }))
{ ... }

Dropdown Data Binding Problem in ASP.NET Core 6 MVC

I am using SelectListItem in the controller for binding my dropdown data. All the dropdown options are showing perfectly in the dropdown list, but when I try to save, the problem occurs. It's not adding the dropdown options data rather than its adding dropdown data's id.
All the related models, controller and views are shown here:
BuyerSelectList model class:
public class BuyerSelectList
{
[Key]
public int Id { get; set; }
[DisplayName("BUYER")]
public string Buyer { get; set; }
}
ItemSelectList model class:
public class ItemSelectList
{
[Key]
public int Id { get; set; }
[DisplayName("ITEM")]
public string Item { get; set; }
}
BTBNewLien2 model class:
public class BTBNewLien2
{
public int Id { get; set; }
[Required]
[DisplayName("Buyer")]
public int BuyerSelectListId { get; set; }
[ForeignKey("BuyerSelectListId")]
[ValidateNever]
public BuyerSelectList BuyerSelectList { get; set; }
[Required]
[DisplayName("Item")]
public int ItemSelectListId { get; set; }
[ForeignKey("ItemSelectListId")]
[ValidateNever]
public ItemSelectList ItemSelectList { get; set; }
}
BTBNewLien2 controller (here I added all the data binding functionalities for my dropdown):
namespace CommercialCalculatorWeb.Areas.Admin.Controllers
{
public class BTBNewLien2Controller : Controller
{
private readonly IUnitOfWork _unitOfWork;
public BTBNewLien2Controller(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public IActionResult Index()
{
IEnumerable<BTBNewLien2> objBTBNewLienList = _unitOfWork.BTBNewLien2.GetAll();
return View(objBTBNewLienList);
}
public IActionResult Create()
{
BTBNewLien2 btbNewLien2 = new();
IEnumerable<SelectListItem> BuyerSelectList = _unitOfWork.Buyer.GetAll().Select(
c => new SelectListItem
{
Text = c.Buyer,
Value = c.Id.ToString()
});
IEnumerable<SelectListItem> ItemSelectList = _unitOfWork.Item.GetAll().Select(
c => new SelectListItem
{
Text = c.Item,
Value = c.Id.ToString()
});
ViewBag.BuyerSelectList = BuyerSelectList;
ViewBag.ItemSelectList = ItemSelectList;
return View(btbNewLien2);
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(BTBNewLien2 obj)
{
if (ModelState.IsValid)
{
_unitOfWork.BTBNewLien2.Add(obj);
_unitOfWork.Save();
TempData["success"] = "Row Created Successfully!";
return RedirectToAction("Index");
}
return View(obj);
}
}
}
BTBNewLien2 create view:
#model CommercialCalculator.Models.BTBNewLien2
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>BTBNewLien2</h4>
<hr />
<div class="row ml-6">
<div class="col-md-4">
<form asp-action="Create">
<div class="form-group">
<label asp-for="BuyerSelectListId" class="control-label">Buyer</label>
<select asp-for="BuyerSelectListId" asp-items="ViewBag.BuyerSelectList" class="form-control">
<option disabled selected>--Select Buyer--</option>
</select>
</div>
<div class="form-group">
<label asp-for="ItemSelectListId" class="control-label">Item</label>
<select asp-for="ItemSelectListId" asp-items="ViewBag.ItemSelectList" class="form-control">
<option disabled selected>--Select Item--</option>
</select>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
BTBNewLien2 index view:
#model IEnumerable<CommercialCalculator.Models.BTBNewLien2>
#{
ViewData["Title"] = "Index";
}
<table class="table table-bordered table-hover table-sm align-middle m-0" id="header">
<tr class="m-0" style="text-align:center;background-color: #17A2B8">
<th width="20%">
#Html.DisplayNameFor(model => model.BuyerSelectList)
</th>
<th>
#Html.DisplayNameFor(model => model.ItemSelectList)
</th>
</tr>
#foreach (var BTBNewLien2 in Model)
{
<tr class="m-0">
<td>
#Html.DisplayFor(modelItem => BTBNewLien2.BuyerSelectList)
</td>
<td>
#Html.DisplayFor(modelItem => BTBNewLien2.ItemSelectList)
</td>
</tr>
}
</table>
Try this way:
#Html.DropDownList("ItemSelectListId", new SelectList(ViewBag.ItemSelectListId, "Text", "Text"), "-- Select Item --", new { required = true, #class = "form-control" })
In my code, it works fine:
Controller:
[HttpGet]
public IActionResult Create()
{
List<SelectListItem> test = new()
{
new SelectListItem { Value = "1", Text = "test1" },
new SelectListItem { Value = "2", Text = "test2" },
new SelectListItem { Value = "3", Text = "test3" },
new SelectListItem { Value = "4", Text = "test4" }
};
ViewBag.ItemSelectListId = test;
return View();
}
[HttpPost]
public IActionResult Create(Test test)
{
return View();
}
View:
<div class="row ml-6">
<div class="col-md-4">
<form asp-action="Create">
<div class="form-group">
<label asp-for="ItemSelectListId" class="control-label">Buyer</label>
#Html.DropDownList("ItemSelectListId", new SelectList(ViewBag.ItemSelectListId, "Text", "Text"), "-- Select Item --", new { required = true, #class = "form-control" })
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
Test Result:

UpdateAsync does not save the data

I have edit page, which should to edit data of IdentityUser. On this page, I have a form with method="post" and also I have a controller, that have a method with [HttpPost] attribute. So, I try to use _userManager.UpdateAsync(user) but unfortunately it does not work. I have checked on succeeded updating user data, but I have no idea why data not updates. Below you will see the code of View and Controllers.
Edit View
#model BookStore.ViewModels.EditViewModel
#{
ViewData["Title"] = "Edit Info";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<form method="post" asp-controller="Profile" asp-action="EditInfo">
<h3 class="registerTitle">Edit Data</h3>
<hr />
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<div><label asp-for="Email"></label></div>
<input asp-for="Email" placeholder="example#mail.com" />
<span class="text-danger" asp-validation-for="Email"></span>
</div>
<!--Birthday inputs-->
<div class="form-group">
<div>
<label type="text" asp-for="birthDate"></label>
</div>
#Html.DropDownList("Month", Enumerable.Range(1, 12).Select(i => new SelectListItem { Value = i.ToString(), Text = System.Globalization.CultureInfo.InvariantCulture.DateTimeFormat.GetMonthName(i) }), "Select Month")
#Html.DropDownList("Day", Enumerable.Range(1, 31).Select(i => new SelectListItem { Value = i.ToString(), Text = i.ToString() }), "Select Day")
#Html.DropDownList("Year", Enumerable.Range(1900, 109).Select(i => new SelectListItem { Value = i.ToString(), Text = i.ToString() }), "Select Year")
</div>
<!--First Name input-->
<div class="form-group">
<div>
<label type="text" asp-for="FirstName"></label>
</div>
<input type="text" asp-for="FirstName" placeholder="First Name" />
<span class="text-danger" asp-validation-for="FirstName"></span>
</div>
<!--Last Name input-->
<div class="form-group">
<div>
<label type="text" asp-for="LastName"></label>
</div>
<input type="text" asp-for="LastName" placeholder="First Name" />
<span class="text-danger" asp-validation-for="LastName"></span>
</div>
<!--User Name input-->
<div class="form-group">
<div>
<label type="text" asp-for="UserName"></label>
</div>
<input type="text" asp-for="UserName" placeholder="Some_username" />
<span class="text-danger" asp-validation-for="UserName"></span>
</div>
<div class="row">
<div class="col-md"></div>
<div class="col-md"><input type="submit" value="Save"/></div>
<div class="col-md"></div>
</div>
</form>
ProfileController
[Route("Info/Edit/{username}")]
[HttpGet]
public async Task<IActionResult> Edit(string username)
{
var user = await _userManager.FindByNameAsync(username);
EditViewModel edit = new EditViewModel()
{
FirstName = user.FirstName,
LastName = user.LastName,
Month = user.birthDate.Month,
Year = user.birthDate.Year,
Day = user.birthDate.Day,
Email = user.Email,
UserName = user.UserName
};
return View(edit);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EditInfo(EditViewModel editModel)
{
var user = await _userManager.FindByNameAsync(User.Identity.Name);
if (user == null) return BadRequest("user is not found");
user.FirstName = editModel.FirstName;
user.LastName = editModel.LastName;
user.UserName = editModel.UserName;
user.Year = editModel.birthDate.Year;
user.Month = editModel.birthDate.Month;
user.Day = editModel.birthDate.Day;
user.Email = editModel.Email;
IdentityResult result = await _userManager.UpdateAsync(user);
if (result.Succeeded)
{
return Content("Ok");
}
return View(editModel);
}
EditViewModel class
public class EditViewModel
{
[EmailAddress]
public string Email { get; set; }
[Display(Name = "Username")]
public string UserName { get; set; }
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Display(Name = "Last Name")]
public string LastName { get; set; }
[Display(Name = "Date of birth")]
[DataType(DataType.Date)]
public DateTime birthDate
{
get
{
return new DateTime(Year,Month,Day);
}
}
public int Day { get; set; }
public int Month { get; set; }
public int Year { get; set; }
}
Explicitly add an antiforgery token to a form element
<form asp-action="EditInfo" asp-controller="Profile" method="post">
#Html.AntiForgeryToken()
<!-- ... -->
</form>
and fix the action code, return badrequest if user is null
var user = await _userManager.FindByNameAsync(editModel.UserName);
if(user == null) return BadRequest("user is not found");
user.FirstName = editModel.FirstName;
....
var result = await _userManager.UpdateAsync(user);
if (result.Succeeded)
{
return Content("Ok");
}
......
and I can see some bugs in the view too. For example
#Html.DropDownList("Day", Enumerable.Range(1, 31).Select(i => new SelectListItem { Value = i.ToString(), Text = i.ToString() }), "Select Day")
should be
#Html.DropDownListFor(model=> Model.Day, Enumerable.Range(1, 31).Select(i => new SelectListItem { Value = i.ToString(), Text = i.ToString() }), "Select Day")
I can see in the post action
user.Day = editModel.birthDate.Day;
it should be
user.Day = editModel.Day;
but i see this in Get action
editModel.Month = user.birthDate.Month,
I am amazed how you could compile it.

ASP.Net throws an error but still posts the data

We are making a movie rating web application but we come across the error Object reference not set to an instance of an object. even though we get that error when we navigate back to the ratings page the rating got added.
Contoller:
public ActionResult RateMedia(string Item, int Rate)
{
PopMediaDDL();
var userStore = new UserStore<ApplicationUser>(ldb);
var manager = new UserManager<ApplicationUser>(userStore);
var user = manager.FindById(User.Identity.GetUserId());
MediaRating rate = new MediaRating();
rate.Title = ldb.MediaData.First(c => c.MediaName == Item);
rate.Rating = Rate;
rate.RateFor = user;
ldb.MediaRatingData.Add(rate);
ldb.SaveChanges();
return View();
}
Model:
public class MediaRating
{
[Key]
public int MediaRatingID { get; set; }
public virtual ApplicationUser RateFor { get; set; }
public virtual Media Title { get; set; }
public int Rating { get; set; }
}
View:
#using CollectionCompanion.Extensions
#using CollectionCompanion.Models;
#model CollectionCompanion.Models.UserCollectionItem
<h2>Rate Media!</h2>
<hr />
#using (Html.BeginForm())
{
<div style="width:80%;">
<div id="AddColItem">
#Html.AntiForgeryToken()
#Html.TextBoxFor(m => m.Item, new { #id = "MediaItem",#style= "color:black;", #name = "MediaItem", #class = "form-control" })
</div>
<div id="AddColDD">
<select id="Rate" name="Rate">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
</div>
<div id="AddColBTN">
<input type="hidden" name="UserCollection" value="#ViewBag.UserCollectionID" />
<button type="submit" class="btn btn-default btn-info"><span class="glyphicon glyphicon-plus"></span> Add</button>
</div>
</div>
}
#foreach (MediaRating rating in ViewBag.Ratings)
{
<div>
<div style="width:50%;">
#rating.Title.MediaName
</div>
<div style="width:50%">
#rating.Rating
</div>
</div>
}
<script type="text/javascript">
$("#MediaItem").autocomplete({
change: function (event, ui) { },
source: "../../Media/GetMediaResult/",
minLength: 2
}
);
</script>
You are returning the view without a model at the end of your controller action:
return View();
I suspect that you are getting a null reference exception because you're not providing the model nor the ViewBag required to populate the View. It should look similar to the HttpGet controller action as that is providing everything required to build the View in the first place.

How do I render a group of checkboxes using MVC 4 and View Models (strongly typed)

I'm rather new to the ASP.net MVC world and I'm trying to figure out how to render a group of checkboxes that are strongly typed to a view model. In webforms I would just use the checkboxlist control but im a bit lost with MVC.
I'm building a simple contact form for a wedding planning business and need to pass whatever checkbox values the user selects to my controller.
The form checkboxes need to look like this:
Your help would be greatly appreciated. Thanks!
Here's what I have so far.
CONTROLLER
[HttpPost]
public ActionResult Contact(ContactViewModel ContactVM)
{
if (!ModelState.IsValid)
{
return View(ContactVM);
}
else
{
//Send email logic
return RedirectToAction("ContactConfirm");
}
}
VIEW MODEL
public class ContactViewModel
{
[Required]
public string Name { get; set; }
[Required]
public string Phone { get; set; }
[Required]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
[Required]
public string Subject { get; set; }
public IEnumerable<SelectListItem> SubjectValues
{
get
{
return new[]
{
new SelectListItem { Value = "General Inquiry", Text = "General Inquiry" },
new SelectListItem { Value = "Full Wedding Package", Text = "Full Wedding Package" },
new SelectListItem { Value = "Day of Wedding", Text = "Day of Wedding" },
new SelectListItem { Value = "Hourly Consultation", Text = "Hourly Consultation" }
};
}
}
//Not sure what I should do for checkboxes...
}
VIEW
#model NBP.ViewModels.ContactViewModel
#{
ViewBag.Title = "Contact";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using (Html.BeginForm())
{
<div id="ContactContainer">
<div><span class="RequiredField">* </span>Your Name:</div>
<div>
#Html.TextBoxFor(model => model.Name)
</div>
<div><span class="RequiredField">* </span>Your Phone:</div>
<div>
#Html.TextBoxFor(model => model.Phone)
</div>
<div><span class="RequiredField">* </span>Your Email:</div>
<div>
#Html.TextBoxFor(model => model.Email)
</div>
<div>Subject:</div>
<div>
#Html.DropDownListFor(model => model.Subject, Model.SubjectValues)
</div>
<div>Vendor Assistance:</div>
<div>
<!-- CHECKBOXES HERE -->
</div>
<div>
<input id="btnSubmit" type="submit" value="Submit" />
</div>
</div>
}
You could enrich your view model:
public class VendorAssistanceViewModel
{
public string Name { get; set; }
public bool Checked { get; set; }
}
public class ContactViewModel
{
public ContactViewModel()
{
VendorAssistances = new[]
{
new VendorAssistanceViewModel { Name = "DJ/BAND" },
new VendorAssistanceViewModel { Name = "Officiant" },
new VendorAssistanceViewModel { Name = "Florist" },
new VendorAssistanceViewModel { Name = "Photographer" },
new VendorAssistanceViewModel { Name = "Videographer" },
new VendorAssistanceViewModel { Name = "Transportation" },
}.ToList();
}
[Required]
public string Name { get; set; }
[Required]
public string Phone { get; set; }
[Required]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
[Required]
public string Subject { get; set; }
public IEnumerable<SelectListItem> SubjectValues
{
get
{
return new[]
{
new SelectListItem { Value = "General Inquiry", Text = "General Inquiry" },
new SelectListItem { Value = "Full Wedding Package", Text = "Full Wedding Package" },
new SelectListItem { Value = "Day of Wedding", Text = "Day of Wedding" },
new SelectListItem { Value = "Hourly Consultation", Text = "Hourly Consultation" }
};
}
}
public IList<VendorAssistanceViewModel> VendorAssistances { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new ContactViewModel());
}
[HttpPost]
public ActionResult Index(ContactViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
//Send email logic
return RedirectToAction("ContactConfirm");
}
}
View:
#using (Html.BeginForm())
{
<div id="ContactContainer">
<div><span class="RequiredField">* </span>Your Name:</div>
<div>
#Html.TextBoxFor(model => model.Name)
</div>
<div><span class="RequiredField">* </span>Your Phone:</div>
<div>
#Html.TextBoxFor(model => model.Phone)
</div>
<div><span class="RequiredField">* </span>Your Email:</div>
<div>
#Html.TextBoxFor(model => model.Email)
</div>
<div>Subject:</div>
<div>
#Html.DropDownListFor(model => model.Subject, Model.SubjectValues)
</div>
<div>Vendor Assistance:</div>
<div>
#for (int i = 0; i < Model.VendorAssistances.Count; i++)
{
<div>
#Html.HiddenFor(x => x.VendorAssistances[i].Name)
#Html.CheckBoxFor(x => x.VendorAssistances[i].Checked)
#Html.LabelFor(x => x.VendorAssistances[i].Checked, Model.VendorAssistances[i].Name)
</div>
}
</div>
<div>
<input id="btnSubmit" type="submit" value="Submit" />
</div>
</div>
}
Use a string array in your view model. You can then use the helper I hacked together. if you don't want to use the helper and the enum then see the actual Html at the bottom. The binder will return a string array with only the selected string values in it. if none are selected it returns a null value for your array. You must account for that, you have been warned :)
View Model:
[Display(Name = "Which Credit Cards are Accepted:")]
public string[] CreditCards { get; set; }
Helper:
public static HtmlString CheckboxGroup<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> propertySelector, Type EnumType)
{
var groupName = GetPropertyName(propertySelector);
var modelValues = ModelMetadata.FromLambdaExpression(propertySelector, htmlHelper.ViewData).Model;//propertySelector.Compile().Invoke(htmlHelper.ViewData.Model);
StringBuilder literal = new StringBuilder();
foreach (var value in Enum.GetValues(EnumType))
{
var svalue = value.ToString();
var builder = new TagBuilder("input");
builder.GenerateId(groupName);
builder.Attributes.Add("type", "checkbox");
builder.Attributes.Add("name", groupName);
builder.Attributes.Add("value", svalue);
var contextValues = HttpContext.Current.Request.Form.GetValues(groupName);
if ((contextValues != null && contextValues.Contains(svalue)) || (modelValues != null && modelValues.ToString().Contains(svalue)))
{
builder.Attributes.Add("checked", null);
}
literal.Append(String.Format("</br>{1} <span>{0}</span>", svalue.Replace('_', ' '),builder.ToString(TagRenderMode.Normal)));
}
return (HtmlString)htmlHelper.Raw(literal.ToString());
}
private static string GetPropertyName<T, TProperty>(Expression<Func<T, TProperty>> propertySelector)
{
var body = propertySelector.Body.ToString();
var firstIndex = body.IndexOf('.') + 1;
return body.Substring(firstIndex);
}
HTML:
#Html.CheckboxGroup(m => m.CreditCards, typeof(VendorCertification.Enums.CreditCardTypes))
Use this if helper extensions scare you:
<input id="CreditCards" name="CreditCards" type="checkbox" value="Visa"
#(Model.CreditCards != null && Model.CreditCards.Contains("Visa") ? "checked=true" : string.Empty)/>
<span>Visa</span><br />
<input id="CreditCards" name="CreditCards" type="checkbox" value="MasterCard"
#(Model.CreditCards != null && Model.CreditCards.Contains("MasterCard") ? "checked=true" : string.Empty)/>
<span>MasterCard</span><br />
For me this works too, and I think this is the simplest (reading the previous answers).
The viewmodel has a string[] for the check boxes.
public string[] Set { get; set; }
The view has this code, and you can repeat the input as many times you need. name, id of the input control has to match the name of the property of the viewmodel.
<div class="col-md-3">
<div class="panel panel-default panel-srcbox">
<div class="panel-heading">
<h3 class="panel-title">Set</h3>
</div>
<div class="panel-body">
<div class="form-group-sm">
<label class="control-label col-xs-3">1</label>
<div class="col-sm-8">
<input type="checkbox" id="Set" name="Set" value="1" />
</div>
<label class="control-label col-xs-3">2</label>
<div class="col-sm-8">
<input type="checkbox" id="Set" name="Set" value="2" />
</div>
</div>
</div>
</div>
</div>
On the post method the Set variable is an array, having the checked value(s).

Resources