Binding checkBoxList on Razor Page with Viewdata - c#

I want to bind a CheckBoxList on Razor page with Viewdata.I have the following code on my Controller Index:
public async Task<IActionResult> Index()
{
var testSections = new List<ReassignmentSectionLookup>();
testSections = await _employeeService.GetTestSections();
ViewData["testSections"] = new SelectList(testSections, "ReassignmentSectionLookupID", "Section");
return View();
}
I have the following on my razor page:
<div class="form-group row">
<div class="col-md-12">
Select Test: <br />
#{
var select = ViewData["testSections"] as SelectList;
if (select != null && select.ToList().Count > 0)
{
foreach (var item in select.ToList())
{
<input type="checkbox" name="selectedItems" value="#item.Value" #(Html.Raw(item.Selected ? "checked=\"checked\"" : "")) /> #item.Text <br />
}
}
}
</div>
When I hover over select, I can see 12 items in the select:
when I hover over select.ToList().Count I get an error saying "system.NullReferenceException". I am not sure what am I doing wrong. I can see the data in the select. below is the screen shot of the error:
If I try doing select.Items.ToList() then I am getting this error:
When using this line:
<input type="checkbox" name="selectedItems" value="#item.Value" #(Html.Raw(item.Selected ? "checked=\"checked\"" : "")) /> #item.Text <br />
I am getting this error:

As discussed in the comment, I would say SelectList is not suitable as it is used to render the items for the select/drop-down list with either tag helper (asp-items) or HtmlHelper (#Html.DropdownList()).
Create a model class for the data set.
public class ReassignmentSectionLookupOption
{
public string Text { get; set; }
public int Value { get; set; }
public bool Selected { get; set; }
}
Instead of returning the data as SelectList in ViewData, return as List<ReassignmentSectionLookupOption> type.
public async Task<IActionResult> Index()
{
var testSections = await _employeeService.GetTestSections();
ViewData["testSections"] = testSections
.Select(x => new ReassignmentSectionLookupOption { Value = x.ReassignmentSectionLookupID, Text = x.Section })
.ToList();
return View();
}
In View, cast the ViewData as List<ReassignmentSectionLookupOption>. And you can use .Any() to check whether the list is not empty.
#{
var testSections = ViewData["testSections"] as List<ReassignmentSectionLookupOption>;
if (testSections != null && testSections.Any())
{
foreach (var item in testSections)
{
<input type="checkbox" name="selectedItems" value="#item.Value" #(Html.Raw(item.Selected ? "checked=\"checked\"" : "")) /> #item.Text <br />
}
}
}

Related

Ajax helper tags in Asp.net Core

Pleas help. I want to click on the button to process the Ajax form,
I don't understand how working data-ajax.
Here is my HTML code
<form asp-page-handler="UpdateTable" data-ajax="true"
data-ajax-method="post"
data-ajax-update="#Tables"
data-ajax-mode="after"
data-ajaz-url="Secon">
<ul class="widget-list">``
<li> <input type="checkbox" name="name1" value="true" /> <span>sdfsdfsfsdfsf</span></li>
<li> <input type="checkbox" name="name2" value="true" /> <span>ssdfsdf</span></li>
<li> <input type="checkbox" name="name3" value="true" /> <span>sdfdsfsdewe</span></li>
<li> <input type="checkbox" name="name4" value="true" /> <span>sdfsdfsfsdfsf</span></li>
<li> <input type="checkbox" name="name5" value="true" /> <span>ssdfsdf</span></li>
<li> <input type="checkbox" name="name6" value="true" /> <span>sdfdsfsdewe</span></li>
</ul>` `
<p style="">Дата: <input type="text" style="width:180px; border-radius:5px;" name="Days" required class="datepicker-here" data-range="true" data-multiple-dates-separator=" - " data-position='top right' /></p>
<p style="margin-left:19px;">
С: <input type="text" style="width:60px; border-radius:5px;" name="TimeFrom" required class="only-time" data-position='top right' />
До: <input type="text" style="width:60px; border-radius:5px;" name="TimeTo" required class="only-time" data-position='top right' />
</p>
<input type="submit" value="Показать" class="button-style" style="margin-left:60px;" />
</form>
and div which i want to update
<div class="div-table" id="Tables">
<div class="BodyTwo" style="width:auto;">
<h3 >#Model.Name</h3>
#{
DataSet DS = Model.Data;
// = Model.Data;
}
</div>
</div>
and my c# code
public class AboutModel : PageModel
{
public string Name { get; set; }
public DataSet Data { get; set; }
public string Date { get; set; }
public string View { get; set; }
public DataTable dataTable { get; set; }
public string Razdel { get; set; }
IRepositor repositor;
public AboutModel (IRepositor repositor)
{
this.repositor = repositor;
}
public IActionResult OnGet()
{
Name = " mnnjhjbbhvbhvh";
Data = new DataSet();
string StrocRezdel = Request.Query.FirstOrDefault(p => p.Key == "Razdel").Value;
View = Request.Query.FirstOrDefault(p => p.Key == "View").Value;
if (StrocRezdel != null)
{
Data = repositor.DataSetTwo(Name);
}
Razdel = StrocRezdel;
return Page();
}
public void OnPostUpdateTable(bool name1, bool name2, bool name3, bool name4, bool name5, bool name6)
{
Name = "";
Name += name1 == true ? "dsdfsdf " : "";
Name += name2 == true ? "svwer " : "";
Name += name3 == true ? "sghjkker " : "";
Name += name4 == true ? "mjhj " : "";
Name += name5 == true ? "rffvbn " : "";
Name += name6 == true ? "ooluhj " : "";
Console.WriteLine("UEEEEEEEEEE");
}
}
But when i press button one more menu is added
Screen1
,
Screen2
why when i press the button "Show" it's happens?
Thanks.
You have a few mistakes in your form. The asp-page-handler attribute is unnecessary because the form will/should be posted to the URL specified by the data-ajax-url attribute. You have mis-spelled that (data-ajaz-url) and it doesn't appear to point to any code that you showed. You have also set the data-ajax-mode to after, which results in the content returned from the AJAX call being appended to existing content.
It's not that easy to see what result you want, so I shall point you to some generic stuff on using unobtrusive AJAX in Razor Pages instead: https://www.learnrazorpages.com/razor-pages/ajax/unobtrusive-ajax

Dynamic checkboxes - Error: Collection is read-only

I'm quite new to MVC4 and my past experience with asp.net has been working with Webforms, so I think I need some careful hand holding. Thank you in advance!
I'm creating a simple web application that is similar to choosing lotto numbers. At the moment the user chooses whichever numbers they want (no limit, starting with "1") and posts it to the next page where I continue to do something with the numbers the user has chosen. The number of numbers - to choose from is dynamic and represented by checkboxes.
The problem is that when the view is submitted, I get the error page "Collection is read-only."
Model - LottoNumbers.cs
bool[] numbers is to store which numbers the user has checked.
namespace Lotto.Models
{
public class LottoNumbers
{
public string name { get; set; }
public bool[] numbers { get; set; }
public LottoNumbers() {
numbers = new bool[49];
}
public LottoNumbers(int limit)
{
numbers = new bool[limit];
}
}
}
in the HomeController.cs ...
I declared LottoNumbers ln, and
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Lotto.Models;
namespace Lotto.Controllers
{
public class HomeController : Controller
{
public LottoNumbers ln;
public ActionResult Index()
{
return View();
}
public ActionResult SetNumbers()
{
ln = new LottoNumbers(30);
// 30 checkboxes will be generated on the view
return View(ln);
}
[HttpPost]
public ViewResult SetNumbers(LottoNumbers l)
{
// get checkbox values
return View();
}
}
}
View - SetNumbers.cshtml
#model Lotto.Models.LottoNumbers
#{
ViewBag.Title = "SetNumbers";
}
<h2>SetNumbers</h2>
#using (Html.BeginForm()) {
<div>
#Html.LabelFor(m => m.name)
#Html.EditorFor(m => m.name)
</div>
<div id="numberlist">
#for (int i = 0; i < Model.numbers.Length; i++)
{
<div class="item-number" style="">
#Html.EditorFor(m => m.numbers[i])
<label class="lbl-number" for="numbers[#i]">#(i+1)</label>
</div>
}
</div>
<div style="clear:both;">
<input type="reset" value="Reset everything" />
<input type="submit" value="Submit" />
</div>
}
Change your array to a list.
public string Name { get; set; }
public List<bool> Numbers { get; set; }
public LottoNumbers()
{
Numbers = new List<bool>();
for (var i = 0; i < 40; i++)
{
Numbers.Add(false);
}
}
public LottoNumbers(int limit)
{
Numbers = new List<bool>();
for (var i = 0; i < limit; i ++)
{
Numbers.Add(false);
}
}
then your view would be
#using (Html.BeginForm())
{
<div>
#Html.LabelFor(m => m.Name)
#Html.EditorFor(m => m.Name)
</div>
<div id="numberlist">
#for (var i = 0; i < Model.Numbers.Count; i++)
{
<div class="item-number" style="">
#Html.CheckBox("Numbers[" + i.ToString() + "]")
<label class="lbl-number" for="numbers[#(i + 1)]">#(i + 1)</label>
</div>
}
</div>
<div style="clear:both;">
<input type="reset" value="Reset everything" />
<input type="submit" value="Submit" />
</div>
}
Null Reference Exception
You are most likely getting the null reference exception after submitting because you are not passing a model back to the view. You set up your page to expect a LottoNumbers model.
[HttpPost]
public ViewResult SetNumbers(LottoNumbers l)
{
// get checkbox values
return View(); //Nothing being passed to the view
}
so after post when your code gets to here
#foreach (var numberBool in Model.Numbers)
it blows up as the Model is null. You can fix this in several ways depending on how you want your app to run. If you want the user to see a blank list of numbers after submit simply do this and add some sort of success message on post back
[HttpPost]
public ViewResult SetNumbers(LottoNumbers l)
{
//process stuff
ln = new LottoNumbers(30);
return View(ln);
}
you can also simply redirect the user back to the index page. Either way the error is caused because you are not passing in a model with numbers back to the view.
If you look at the stack trace of your "Collection is read only" error, you can see that it's happening during Model Binding, which is when it tried to recreate your LottoNumbers object from the data passed back from the form post.
You're getting the error because you're initialising your object with an array size using you're constructor that takes a parameter, but when it posts back the data, it doesn't know what size the array needs to be when it recreates the object.
To solve this, it would be better to use a List instead, and have the size as a property of your LottoNumbers object.
Using a List means you don't have to pre-initialise the array with a size. And having the size as a property means you can embed the size in a hidden input variable, so that it can be passed back to the controller, and it should correctly do the model binding.

MVC 4 Bind multiple model asp razor

I develop an application who manages formations of employees, I use MVC4 Asp.net with Razor.
In my model I have tow class (who are table in my database) formation and formateur (trainers).
In my application i can create a “formation” and I want to add a list of “formative”(trainers) but I don’t know what I must do.
I think the best solution it’s a list of checkbox, I succeeded to display my list of checkbox with a foreach but I have no idea how I get the result of selected checkbox to pass into my controller.
I saw many tutorials where use “CheckBoxList” and I tried to use too, but I use a ViewBag to populate it and they don't explain how to use it with a viewbag.
Now I test a Dual listBox with tow buttons (Add and Remove) but this doesn't work.
So, somebody can help me to find, and explain how I must do, the good or the best solution ?
I'm sorry for my english, I'm a french girl.
One of my solutions look like this :
My controller :
public ActionResult Create()
{
ViewBag.formateurListe = (from unFormateur in db.salarie
where unFormateur.sFormateur == true
select unFormateur).AsEnumerable()
.Select(m => new SelectListItem
{
Text = m.sNom.ToString() + " " + m.sPrenom.ToString(),
Value = m.sId.ToString()
}).ToList();
return View();
}
[HttpPost]
public ActionResult Create(formation formation, IEnumerable<SelectList> formateurList)
{
if (ModelState.IsValid)
{
db.formation.Add(formation);
foreach (var unSal in formateurList)
{
formateur f = new formateur();
f.ftIdFormation = formation.fId;
f.ftIdSalarie = (int)unSal.SelectedValue;
db.formateur.Add(f);
}
db.SaveChanges();
return RedirectToAction("Index");
}
return View(formation);
}
In my view :
#model MvcAppGestionRH.Models.formation
#using (Html.BeginForm("Create", "Formation", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.ValidationSummary(true)
#Html.Label("Libelle")
#Html.EditorFor(model => model.fLibelle)
#Html.ValidationMessageFor(model => model.fLibelle)
<label id="fFormateur">Formateur</label>
#Html.ListBox("formateurListe", ViewData["formateurListe"] as SelectListItem[], new {Multiple = "multiple"})
<input type="button" value="+" name="add" />
<select name="select" size="7" >
</select>
<input type="submit" value="Créer" />
}
With a script :
$(function () {
$("#Add").click(function () {
$("select").add($('fFormateurListe').selected);
});
});
Checkboxes can be tricky the first time - I googled that a long time, too.
My solution is a view model which looks like this:
It is intended for questions, where the crator can speciy items via checkboxes (e.g. a questions might have the answer "GOOD" and "BAD".
public class QuestionModel
{
public int QuestionID { get; set; }
public string QuestionText { get; set; }
/// <summary>
/// Gets or sets the selected items. Purely a helper List to display check boxes for the user
/// </summary>
/// <value>
/// The selected items.
/// </value>
[Display(Name = "Items", ResourceType = typeof(Domain.Resources.Question))]
public IEnumerable<SelectListItem> SelectedItems { get; set; }
/// <summary>
/// Gets or sets the selected ids. Populated by the user, when he checks / unchecks items. Later translated into QuestionItems
/// </summary>
/// <value>
/// The selected ids.
/// </value>
public int[] SelectedIds { get; set; }
}
This is populated like this in the QuestionController:
private async Task GetSelectedItems(QuestionModel sm, Item selectedItems)
{
var alreadySelected = new List<Scale>();
if (selectedScale != null)
{
alreadySelected.Add(selectedScale);
}
var itemList = (await this.uoW.ItemRepository.Get()).OrderBy(i => i.Name);
sm.SelectedItems = itemList.Select(x => new SelectListItem
{
Value = x.ScaleID.ToString(),
Text = x.NameOfScale.GetText(),
Selected = (from a in alreadySelected where a.ItemID == x.ItemID select x).Any()
});
}
What does this do? It gets a list of all avialable items in the database and populates the model with it. Furthermore, you can pass in a list of items, which are already selected - so you can edit an existing question and siplay all already checked Items.
And n the view I have used a DropDownList:
<div class="form-group">
#Html.LabelFor(model => model.SelectedItems, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<div class="checkbox">
#Html.DropDownListFor(x => x.SelectedIds, Model.SelectedItems, new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.SelectedItems, "", new { #class = "text-danger" })
</div>
</div>
</div>
If you want checkboxes, that looks like this(different controller, so dont be confused)
for (int i = 0; i < Model.SelectedItems.Count(); i++)
{
var currentElem = Model.SelectedItems[i];
//if this item is selected by the user, e.g. because he is editing the item, the item will be pre-selected
var selected = currentElem.Selected ? "checked=\"selected\"" : string.Empty;
// column for the questions. expected layout: list of all questions
<div class="col-md-6">
<div class="checkbox" id="SelectedIds">
<label>
<input type="checkbox" value="#currentElem.Value" #selected name="SelectedIds">
#Html.Encode(currentElem.Text)
</label>
</div>
</div>
}
and finally the create() method itself:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create([Bind(Include = "QuestionText,SelectedItems, SelectedIds")] QuestionModel question)
{
if (ModelState.IsValid)
{
// I need only one Item, but if you want ore more change this line
if (question.SelectedIds.Count() == 1)
{
// better use Automapper here, this is unnecessary work
var newQuestion = new Question { QuestionText = question.QuestionText};
var ItemID = question.SelectedIds.First();
newQuestion.QuestionScale = await this.uoW.ItemRepository.GetRaw().Where(i => i.ItemID == ItemD).FirstAsync();
this.uoW.QuestionRepository.Insert(newQuestion);
await this.uoW.Save();
return this.RedirectToAction("Index");
}
else
{
this.logger.Warn("User {0} tried to insert more than one Itemin question {1}", User.Identity.Name, question.QuestionID);
ModelState.AddModelError(string.Empty, xyz.Areas.QuestionManagement.Resources.QuestionRes.ErrorTooManyScales);
}
}
else
{
// the SelectedItems are empty in the model - so if you have to redisplay it, repopulate it.
await this.GetSelectedItems(question, null);
}
return this.View(question);
}
Have you tried using a viewmodel to pass your two model in the view?
For example :
ViewModel
public class CreateFormationViewModel
{
public Formation formation{ get; set; }
public List<Formative> trainers {get;set;}
}
and then use this viewmodel in your view
An easy way to use this view model :
In your controller
public ActionResult CreateFormation()
{
//Get your data (formation and trainer)
CreateFormationViewModel createFormationVM = new CreateFormationViewModel();
createFormationVM.formation = YourFormationModel;
createFormationVM.trainers = YourtrainersModelasList;
//bind data to the view
return View(createFormationVM);
}
And in your View, you have :
#model [yournamespace].CreateFormationViewModel

EditorFor not generating proper html

I have a mvc web project where I try to render a list of checkbox with the EditorFor extension method but the result just display the ids as text instead of of a list of checkbox.
Here is the code in the view:
<div id="permissions" class="tab-body">
#Html.Label("Permissions :")
#Html.EditorFor(x => Model.Permissions)
<br />
<br />
</div>
This is the property 'Permissions' of the object 'Model':
[DisplayName("Permissions")]
public List<PermissionViewModel> Permissions { get; set; }
And this is the PermissionViewModel:
public class PermissionViewModel
{
public int Id { get; set; }
public UserGroupPermissionType Name { get; set; }
public string Description { get; set; }
public bool IsDistributable { get; set; }
public bool IsGranted { get; set; }
}
And finally, this is the result in the browser:
<div id="permissions" class="tab-body" style="display: block;">
<label for="Permissions_:">Permissions :</label>
192023242526272829
<br>
<br>
</div>
Have you any idea why the html is not generated correctly? Missing dependencies? Conflict in dependencies? Web.Config configured not correctly?
Thank you very much for you help.
It looks as if you need to create an editor template for the "PermissionViewModel" class, as right now, MVC seems to be confused with how to make an editor for such a complex object.
In the folder where the view is being served from, add a folder called "EditorTemplates"
Then add a new partial view in that folder. The code should be:
#model IEnumberable<PermissionViewModel>
#foreach(var permission in Model)
#Html.EditorFor(x => x.Name)
#Html.EditorFor(x => x.Description)
#Html.EditorFor(x => x.IsDistributable)
#Html.EditorFor(x => x.IsGranted)
You will need to create an Editor Template for the Name class as well.
So now in your view you can call
<div id="permissions" class="tab-body">
#Html.Label("Permissions :")
#Html.EditorFor(x => Model.Permissions)
<br />
<br />
</div>
And MVC will know to use the editor template you just made for your permission.
A good resource for learning about editor templates is here: http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-1-introduction.html
Maybe you want to make something yourself?
public delegate object Property<T>(T property);
public static HtmlString MultiSelectListFor<TModel, TKey, TProperty>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, IEnumerable<TKey>>> forExpression,
IEnumerable<TProperty> enumeratedItems,
Func<TProperty, TKey> idExpression,
Property<TProperty> displayExpression,
Property<TProperty> titleExpression,
object htmlAttributes) where TModel : class
{
//initialize values
var metaData = ModelMetadata.FromLambdaExpression(forExpression, htmlHelper.ViewData);
var propertyName = metaData.PropertyName;
var propertyValue = htmlHelper.ViewData.Eval(propertyName).ToStringOrEmpty();
var enumeratedType = typeof(TProperty);
//check for problems
if (enumeratedItems == null) throw new ArgumentNullException("The list of items cannot be null");
//build the select tag
var returnText = string.Format("<select multiple=\"multiple\" id=\"{0}\" name=\"{0}\"", HttpUtility.HtmlEncode(propertyName));
if (htmlAttributes != null)
{
foreach (var kvp in htmlAttributes.GetType().GetProperties()
.ToDictionary(p => p.Name, p => p.GetValue(htmlAttributes, null)))
{
returnText += string.Format(" {0}=\"{1}\"", HttpUtility.HtmlEncode(kvp.Key),
HttpUtility.HtmlEncode(kvp.Value.ToStringOrEmpty()));
}
}
returnText += ">\n";
//build the options tags
foreach (TProperty listItem in enumeratedItems)
{
var idValue = idExpression(listItem).ToStringOrEmpty();
var displayValue = displayExpression(listItem).ToStringOrEmpty();
var titleValue = titleExpression(listItem).ToStringOrEmpty();
returnText += string.Format("<option value=\"{0}\" title=\"{1}\"",
HttpUtility.HtmlEncode(idValue), HttpUtility.HtmlEncode(titleValue));
if (propertyValue.Contains(idValue))
{
returnText += " selected=\"selected\"";
}
returnText += string.Format(">{0}</option>\n", HttpUtility.HtmlEncode(displayValue));
}
//close the select tag
returnText += "</select>";
return new HtmlString(returnText);
}

ASP.NET MVC #Html.EditorFor(model => model.property) But

What about if I have a navigation property? I have a collection of another model in the model I'm trying to edit. I want to display a list of checkboxes for each object in the collection property. So far, this is what I tried...
#{
foreach (var category in ViewBag.Categories)
{
if (Model.Categories.Contains(category))
{
<input type="checkbox" name="selected-categories" value="category.CategoryId" checked="checked" />#category.Name
}
else
{
<input type="checkbox" name="selected-categories" value="#category.CategoryId" />#category.Name
}
}
}
But it fails with an EntityCommandExecutionException. In my if statement, how can I access the model.Categories like I do in something like #Html.EditorFor(model => model.Id)???
Using a strongly-typed View is the way I'd go. Create a ViewModel that contains your Model and use this ViewModel for the strongly-typed View.
Domain Model and ViewModel (simplified)
public class YourModel
{
string Category { get ; set ; }
}
public class YourViewModel
{
public List<string> PossibleCategories { get ; set ; }
public YourModel YourData { get ; set ; }
}
Then the View:
#model YourViewModel
#{
foreach (string CurrCategory in Model.PossibleCategories)
{
if (Model.YourData.Category == CurrCategory)
{
#Html.CheckBox(CurrCategory, new { #checked = "checked" })
#Html.Encode(CurrCategory) <br />
}
else
{
#Html.CheckBox(CurrCategory, false)
#Html.Encode(CurrCategory) <br />
}
}
}

Categories