I've been trying to create a drop down list with asp tag helpers and for some reason they always look strange. I'll include my code for Controller, View, and view model. Also the picture of how it looks.
Controller:
public async Task<IActionResult> Create()
{
var dalias = await _context.DeptAliases
.Select(m => new SelectListItem { Text = m.Alias, Value = m.DeptAliasPkey.ToString() }).Distinct()
.ToListAsync();
var vm = new CopyCenterCreateViewModel()
{
DAlias = dalias,
};
return View(vm);
}
View:
#model PrintShop.ViewModels.CopyCenterCreateViewModel
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<label asp-for="DAlias" class="control-label"></label>
<select asp-for="DAlias" asp-items="Model.DAlias" class="form-control"></select>
<span asp-validation-for="DAlias" class="text-danger"></span>
</form>
ViewModel:
public class CopyCenterCreateViewModel
{
public List<SelectListItem> DAlias { get; set; }
}
Result:
It looks like the select control is rendering in multi-selection mode. If the tag helper asp-for is binding to an IEnumerable then the select element is rendered in multiple mode.
From tag-helpers multiple select section of the docs:
The Select Tag Helper will automatically generate the multiple = "multiple" attribute if the property specified in the asp-for
attribute is an IEnumerable.
You only have a single property in the ViewModel which is a List, and you're binding the items to the same property as the selected value.
You could rename the list of items to something else and add a property to bind to the value of the selected item:
public class CopyCenterCreateViewModel
{
public string DAlias { get; set; }
public List<SelectListItem> Items { get; set; }
}
In the controller you'd populate the Items property of the CopyCenterCreateViewModel.
In the view bind asp-items to the view model Items property:
<select asp-for="DAlias" asp-items="Model.Items" class="form-control"></select>
Related
I want to grab all the values of a multiple selected selectlist but it keeps telling me null.
this is my viewmodel
public class ImportFromStaffListVM
{
public string StaffId { get; set; }
public SelectList StaffList {get;set;}
}
select tag is
#using (Html.BeginForm("ImportFromStaffList", "Training", FormMethod.Post))
{
<div class="card">
<div class="card-header">
<h5>Add Participants</h5>
<!--<span>Add class of <code>.form-control</code> with <code><input></code> tag</span>-->
</div>
<div class="card-body">
<div class="form-group">
<select type="text" id="selectUser" data-width="100%" class=" select2 form-control" asp-items="#Model.StaffList" asp-for="#Model.StaffId" multiple="multiple"></select>
</div>
public async Task < IActionResult> ImportFromStaffList(ImportFromStaffListVM importFromStaffListVM)
{
foreach (var staff in importFromStaffListVM.StaffList.Where(x => x.Selected).Select(y => y.Value).ToList())
{
var selectedStaff = dcx.StaffList.Where(x => x.StaffId.Contains(staff.ToString())).ToList();
foreach (var staffid in selectedStaff)
{
Participant newParticipant = new Participant()
{
DateOfBirth = staffid.DateOfBirth,
Contact = staffid.Contact,
Name = staffid.Name,
Rank = staffid.Rank,
Department = staffid.Department,
};
dcx.Participants.Add(newParticipant);
await dcx.SaveChangesAsync();
}
anytime i run the controller it tells me null. Please any help is appreciated
I think you've got a bit confused with the implementation.
Have a look at the multi select example in the online documentation.
https://learn.microsoft.com/en-us/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-6.0#the-select-tag-helper
If you compare your code with that example, it should help you resolve.
Having a quick compare myself I think the StaffId property needs to be a list in your model. Since you're selecting multiple StaffIds.
IEnumerable<string> StaffId
Then you actually loop through the StaffId list in your Action method to get the selected StaffIds.
Note: You don't need the id="selectUser" also, since the asp-for will give the generated html an id="StaffId"
Can anyone explain why my drop-down list automatically allows multiple selections?
I'm quite new to Net Core and this is the first time I add a drop-down and I'm obvisouly doing something wrong. Everything works technically, the drop-down list is populated and the Model gets the correct data when posting but for some reason the multiple attribute is automatically set for the drop-down list.
View
<select asp-for="#Model.fooForm.CountryRows"
asp-items="#(new SelectList(Model.fooForm.CountryRows,"CountryCode","CountryName"))"
class="form-control">
<option>Please select Country of use</option>
</select>
ViewModel
namespace foo.Models.ViewModels
{
[Bind(Prefix = nameof(fooIndexRootVM.fooForm))]
public class fooVM
{
public fooVM()
{
}
public fooVM(CountryVM[] countryRows)
{
CountryRows = countryRows;
}
[Display(Name = "Country of use")]
public virtual CountryVM[] CountryRows { get; set; }
}
}
Controller/Service
public async Task<fooIndexRootVM> GetCountries()
{
var countryRows = await fooContext.Country
.OrderBy(c => c.CountryCode)
.Select(c => new CountryVM
{
CountryCode = c.CountryCode
,CountryName = c.CountryName
,CountryBlocked = c.CountryBlocked
})
.ToArrayAsync();
return new fooIndexRootVM
{
fooForm = new fooVM(countryRows)
};
}
From MSDN: The select tag helper
The Select Tag Helper will automatically generate the multiple =
"multiple" attribute if the property specified in the asp-for
attribute is an IEnumerable.
So in your case the model it's looking at represents a list for asp-for="#Model.fooForm.CountryRows".
In order to fix this you can add new property for selected country to model
public virtual CountryVM SelectedCountry { get; set; }
Then asp-for should be like this:
<select asp-for="#Model.fooForm.SelectedCountry"
asp-items="#(new SelectList(Model.fooForm.CountryRows,"CountryCode","CountryName"))"
class="form-control">
<option>Please select Country of use</option>
</select>
When creating Employee entity you are supposed to select MeetingCenterfrom DropDownList. All MeetingCenters show just fine in DropDownList with their Names, but when some of them is selected and Employee is created Meeting Center is null. Im using NoSQL DocumentDB database.
Controller:
[ActionName("Create")]
public async Task<IActionResult> CreateAsync()
{
ViewBag.MeetingCentersList = await _meetingCenterReposiotry.GetItemsAsync();
return View();
}
Create View:
#model Employee
#using (Html.BeginForm("Create", "Home", FormMethod.Post, new { #class = "form-horizontal" }))
{
...
<div class="form-group">
#Html.LabelFor(MeetingCenter => Model.MeetingCenter, new { #class = "control-label" })
#Html.DropDownListFor(MeetingCenter => Model.MeetingCenter, new SelectList(ViewBag.MeetingCentersList, "MeetingCenterId", "Name"), new { #class = "form-control" })
</div>
...
}
Piece of Employee Model
public class Employee
{
...
[JsonProperty(PropertyName = "id")]
public string EmployeeId { get; set; }
[JsonProperty(PropertyName = "meetingCenter")]
public MeetingCenter MeetingCenter { get; set; }
...
}
Piece of MeetingCenter Model
public class MeetingCenter
{
...
[JsonProperty(PropertyName = "id")]
public string MeetingCenterId { get; set; }
...
}
With your current code, the DropDownListFor helper will render a SELECT element with options, which has the MeetingCenterId as the value attribute and the Name as the Text. The SELECT element's name attribute value will be MeetingCenter. So when the form is submitted the form data will look like this
MeetingCenter: 2
Assuming user selected the option with value "2".
But the MeetingCenter property of your view model(Employee) is not a numeric type, it is a complex type(MeetingCenter). So model binder cannot map this value to MeetingCenter property of your view model.
You can render the SELECT element with the name MeetingCenter.MeetingCenterId and then model binder will be able to map the posted form data as the input element name matches with the naming-structure of your view model.
So you should render something like this inside your form.
<select name="MeetingCenter.MeetingCenterId">
</select>
You can generate the above markup by using the SELECT tag helper and specifying MeetingCenter.MeetingCenterId as the asp-for property.
<select asp-for="MeetingCenter.MeetingCenterId"
asp-items="#(new SelectList(ViewBag.MeetingCentersList,
"MeetingCenterId", "Title"))">
</select>
Now when the form is submitted, it will populate the MeetingCenter property of your view model and it's MeetingCenterId property.
If you want the full MeetingCenter property to be populated (properties other than MeetingCenterId, get the full object by querying the data provided by _meetingCenterReposiotry.GetItemsAsync() using the MeetingCenterId available to you in the HttpPost action. Something like this
var id = viewModel.MeetingCenter.MeetingCenterId;
var items = await _meetingCenterReposiotry.GetItemsAsync();
var item = items.FirstOrDefault(a=>a.MeetingCenterId==id);
// User item now
// May be do somethig like : viewModel.MeetingCenter = item;
I also suggest you to use the correct types. If MeetingCenterId is numeric value, use int as type instead of string
I'm doing something horribly simple and it isn't working.
#Html.DropDownListFor(m => m.ContentDefinitionID, Model.ContentBoxesSelectList, new {#class = "form-control"})
The ContentDefinitionID is a UInt64 (although I've tried an int)
I use this same select list for 4 different controls on the page.
If my Model.ContentDefinition is set to 4, (which would be test4 in the drop down) then it SHOULD pull that selected value from the Model, NOT from the selectList right? Someone else said that it ignores the value on the SelectList when you use the m=>m.X syntax - which makes sense.
But it always selects the first one. I tried adding another parameter for what it should select, but it wants the text, not the value and I hate to have to lookup the text. (And I'm not sure this will post back correctly if I do that anyway)
I'm about to go create some JQuery code to set the default values for everything -but that is crazy. This is the obvious behavior of the DropDownListFor() method, kind of a 'duh' thing - why doesn't it work? Do I need to create a custom SelectList for every control that exists on the page?
--- Update, the model etc:
class PageModel
{
Int64 ContentDefinitionID {get;set;}
SelectList ContentBoxesSelectList {get;set;}
}
Controller init for model:
model.ContentDefinitionID = 4; // In real situation, this is an
array of N model.ContentBoxesSelectList = new SelectList( from
ContentDefinitionDocument doc in allContentBoxes
where doc.Size == size
select new {Name = doc.Name, id = doc.DefinitionID}, "Id", "Name");
Rendered Output (from it as an array):
selected value should be: 1
<select class="form-control" data-val="true" data-val-number="The field ContentDefinitionID must be a number." data-val-required="The ContentDefinitionID field is required." id="ContentBoxes_0__ContentDefinitionID" name="ContentBoxes[0].ContentDefinitionID" style="width:25%;float:left"><option value="1">Test1</option>
<option value="2">Test 2</option>
<option value="4">Test 4</option>
<option value="0">Test 0</option>
</select>
And None of them are selected.
From the html your generating (name="ContentBoxes[0].ContentDefinitionID"), you are using this in a for loop. Unfortunately DropDownListFor() does not work as expected in a for loop and you need to use a custom EditorTemplate for your model (its been reported as a bug but not yet fixed).
You have not posted your models, but assuming
public class ContentBox
{
public int ContentDefinitionID { get; set; }
....
}
public class ContentBoxesVM
{
public IEnumerable<ContentBox> ContentBoxes { get; set; }
public SelectList ContentBoxesSelectList { get; set; }
}
Side note: Its only necessary to generate one SelectList (rather that one for each object in the collection)
Controller
public ActionResult Edit()
{
ContentBoxesVM model = new ContentBoxesVM();
// populate model, for example
model.ContentBoxes = new List<ContentBox>()
{
new ContentBox { ContentDefinitionID = 4 }
};
model.ContentBoxesSelectList = new SelectList(...);
return View(model);
}
Editor Template (/Views/Shared/EditorTemplates/ContentBox.cshtml)
#model ContentBox
....
#Html.DropDownListFor(m => m.ContentDefinitionID, (SelectList)ViewData["contentBoxesList"])
...
Main view
#model ContentBoxesVM
#using(Html.BeginForm())
{
....
// pass the select list as additional view data to the editor template
#Html.EditorFor(m => m.ContentBoxes, new { contentBoxesList = Model.ContentBoxesSelectList })
....
<input type="submit" />
}
You can achieve what you want by changing your model:
class PageModel
{
Int64 ContentDefinitionID {get;set;}
List<ContentDefinitionDocument> ContentBoxesList {get;set;}
}
and in controller:
array of N model.ContentBoxesList = allContentBoxes.Where(doc => doc.Size == size).ToList();
and in View create SelectList this way:
#Html.DropDownListFor(m => m.ContentDefinitionID,
new SelectList(Model.ContentBoxesSelectList,
"DefinitionID",
"Name",
Model.ContentDefintionID),
new {#class = "form-control"})
I'm really having problems with keeping the state of my checkbox in my mvc4 application. I'm trying to send its value down to my controller logic, and refresh a list in my model based on the given value, before I send the model back up to the view with the new values. Given that my checkbox is a "show disabled elements in list" type function, I need it to be able to switch on and off. I've seen so many different solutions to this, but I can't seem to get them to work :(
Here's a part of my view:
#model MyProject.Models.HomeViewModel
<div class="row-fluid">
<div class="span12">
<div class="k-block">
<form action="~/Home/Index" name="refreshForm" method="POST">
<p>Include disabled units: #Html.CheckBoxFor(m => m.Refresh)</p>
<input type="submit" class="k-button" value="Refresh" />
#* KendoUI Grid code *#
</div>
</div>
HomeViewModel:
public class HomeViewModel
{
public List<UnitService.UnitType> UnitTypes { get; set; }
public bool Refresh { get; set; }
}
The HomeViewController will need some refactoring, but that will be a new task
[HttpPost]
public ActionResult Index(FormCollection formCollection, HomeViewModel model)
{
bool showDisabled = model.Refresh;
FilteredList = new List<UnitType>();
Model = new HomeViewModel();
var client = new UnitServiceClient();
var listOfUnitsFromService = client.GetListOfUnits(showDisabled);
if (!showDisabled)
{
FilteredList = listOfUnitsFromService.Where(unit => !unit.Disabled).ToList();
Model.UnitTypes = FilteredList;
return View(Model);
}
FilteredList = listOfUnitsFromService.ToList();
Model.UnitTypes = FilteredList;
return View(Model);
}
You return your Model to your view, so your Model properties will be populated, but your checkbox value is not part of your model! The solution is to do away with the FormCollection entirely and add the checkbox to your view model:
public class HomeViewModel
{
... // HomeViewModel's current properties go here
public bool Refresh { get; set; }
}
In your view:
#Html.CheckBoxFor(m => m.Refresh)
In your controller:
[HttpPost]
public ActionResult Index(HomeViewModel model)
{
/* Some logic here about model.Refresh */
return View(model);
}
As an aside, I can't see any reason why you'd want to add this value to the session as you do now (unless there's something that isn't evident in the code you've posted.