I m working with asp.net mvc3 to create a list of radiobuttons. I need to get a list from the controller and display it in the view. For each corresponding list in the view I have YES/NO radio button and need to generate a string at the end saying for each item in the list if the radiobutton is yes then 1 else 0.
So for example if I have 10 items in the list then I need to display them in the view (default values of the items being false) and then generate a string on submit where each character in the string corresponds to the bool value of each item in the list.
Can anyone give me an idea how do I do this in mvc3 ?
Thanks for the help in advance.
UPDATE
Here is the code I am trying :
My class has two properties :
public List<Module> lstModules { get; set; } // gives the list of items
public List<bool> IsModuleActivelst { get; set; } //gives the bool values
Here in controller I need to create a list and the corresponding bool values for that. I am stuck here and not able to generate the code. Anyways I ll explain the pseudocode
public class MMController : Controller
{
[HttpGet]
public ActionResult Clients()
{
//I need to generate the list - using lstModules prop
// Assign the list with the predefined values and if not just give the default values- using IsModuleActivelst prop
}
}
Here I create the view :
#foreach (var i in Model.lstModules)
{
<div class="formlabel">
<div align="right" class="label">
#Html.LabelFor(model => model.lstModules):</div>
</div>
<div class="formelement">
<label for="radioYes" class="visible-enable" style="cursor:pointer;position:relative;">
#Html.RadioButtonFor(model => model.IsModuleActivelst, "True", new { #id = "radioYes", #style = "display:none;" })
<span>Yes</span></label>
<label for="radioNo" class="visible-disable" style="cursor:pointer;position:relative;">
#Html.RadioButtonFor(model => model.IsModuleActivelst, "False", new { #id = "radioNo", #style = "display:none;" })
<span>No</span></label>
</div>
}
I would recommend you to start by defining a view model that will represent the information that you need to be working with in the particular view. So far you have mentioned a list of modules where the user needs to set whether a module is active or not using a radio button (personally I would use a checkbox for True/False status but that's your own decision):
public class ModuleViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
}
public class MyViewModel
{
public IEnumerable<ModuleViewModel> Modules { get; set; }
}
Then you could define a controller that will populate the view model, render the form and have another action to handle the form submission:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new MyViewModel
{
// TODO: this information could come from a database or something
Modules = new[]
{
new ModuleViewModel { Id = 1, Name = "module 1", IsActive = true },
new ModuleViewModel { Id = 2, Name = "module 2", IsActive = true },
new ModuleViewModel { Id = 3, Name = "module 3", IsActive = false },
}
};
return View(model);
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return Content(
string.Format(
"Thank you for selecting the following values: {0}",
string.Join(" ", model.Modules.Select(x => string.Format("model id: {0}, active: {1}", x.Id, x.IsActive)))
)
);
}
}
The last part is to define the view (~/Views/Home/Index.cshtml):
#model MyViewModel
#using (Html.BeginForm())
{
#Html.EditorFor(x => x.Modules)
<button type="submit">OK</button>
}
And finally the corresponding editor template that will automatically be rendered for each element of the Modules collection - notice that the name and location of the template is important - ~/Views/Shared/EditorTemplates/ModuleViewModel.cshtml:
#model ModuleViewModel
<div>
#Html.HiddenFor(x => x.Id)
#Html.HiddenFor(x => x.Name)
<h2>#Html.DisplayFor(x => x.Name)</h2>
#Html.Label("IsActiveTrue", "Yes")
#Html.RadioButtonFor(x => x.IsActive, "True", new { id = Html.ViewData.TemplateInfo.GetFullHtmlFieldId("IsActiveTrue") })
<br/>
#Html.Label("IsActiveFalse", "No")
#Html.RadioButtonFor(x => x.IsActive, "False", new { id = Html.ViewData.TemplateInfo.GetFullHtmlFieldId("IsActiveFalse") })
</div>
Related
ASP.NET Core 5 MVC web app. The question is HOW it works, not why it doesn't. I don't understand the mechanism and so don't want to see it fail from some "happy-fingers" coding accident in the future...
I have a main model that the controller expects on create:
public class ProductCategory : BaseClass
{
public int ProductId { get; set; }
public virtual Product Product { get; set; }
public int CategoryId { get; set; }
public virtual Category Category { get; set; }
}
(BaseClass just has your typical record keeping fields).
I have a model for the view components; I need two, one for each dropdown, so you can easily imagine the other, with names modified to protect the innocent...:
Category:
public class CategoryList
{
public CategoryList()
{
Categories = new List<Category>();
}
public int CategoryId { get; set; }
[DisplayName("Categories")]
public List<Category> Categories { get; set; }
}
The category view component:
public class CategoryDropdownViewComponent : ViewComponent
{
private readonly ApplicationDbContext _db;
public CategoryDropdownViewComponent(ApplicationDbContext context)
{
_db = context;
}
public async Task<IViewComponentResult> InvokeAsync()
{
var items = await GetCategoriesAsync();
var TheView = "Default";
var list = new CategoryList();
if (items.Count == 0)
{
TheView = "CategoryMaintenanceLink";
}
else
{
items.Insert(0, new Category() { Id = 0, Name = "-- Please select an option --" });
list.Categories = items;
}
return View(TheView, list);
}
private Task<List<Category>> GetCategoriesAsync()
{
return _db.Category.ToListAsync();
}
}
And the default view for category (I store this and above in ~\Shared\Components\CategoryDropdown\):
#model CoolestProjectNameEver.Models.CategoryList
<p>
#Html.DropDownListFor(model => model.CategoryId, new SelectList(Model.Categories, "Id", "Name"), new { #class = "form-control" })
</p>
So, in my controller, I kick off create:
public IActionResult Create()
{
return View();
}
And in the Create view, amongst other things, I fire up the view components:
<div class="form-group">
<label asp-for="ProductId" class="control-label"></label>
#await Component.InvokeAsync("ProductDropdown")
</div>
<div class="form-group">
<label asp-for="CategoryId" class="control-label"></label>
#await Component.InvokeAsync("CategoryDropdown")
</div>
All works and the dropdown lists are filled. I can select options for both. Now the unknown part.
On to the POST method for Create:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(ProductCategory productCategory)
{
try
{
if (ModelState.IsValid) <--- breakpoint
{
_context.Add(productCategory);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
return View(productCategory);
}
The breakpoint will show the correct selected values for CategoryId and ProductId.
So the question is, did this work because of a name match in the VC model to main controller model, and it auto filled somehow?
1 if my ViewComponent model had, say SelectedValueId instead of CategoryId, then it would fail because of a mismatch?
2 How did the value from a separate model in an async ViewComponent get plugged into the main model on postback?
In fact,if you change your Create view code to:
<div class="form-group">
#await Component.InvokeAsync("ProductDropdown")
</div>
<div class="form-group">
#await Component.InvokeAsync("CategoryDropdown")
</div>
it will also successfully binding.
The model binding in asp.net core is matched according to the name. If the name matches, the corresponding attribute will be bound to the model.
In your ViewComponent, your code(model => model.CategoryId):
#Html.DropDownListFor(model => model.CategoryId, new SelectList(Model.Categories, "Id", "Name"), new { #class = "form-control" })
will be given name =CategoryId in the generated html code
Then the name CategoryId is also in your ProductCategory model, if their names match, the binding will be successful.
My question is two-fold.
I have a View that gets data on the change of a drop down selection.
The data retrieved is a List property of a ViewModel class using an Ajax call.
This data is shown as a selection of check boxes for the user to select any number of them.
If I return a Partial View from an AJAX call, this is easy enough, but from what I have experienced, this doesn't work for POST'ing back to the controller. Nothing is bound correctly.
From what I have read the correct way is to use EditorFor, So firstly, I cannot figure out how to populate the EditorFor from the AJAX call.
Secondly, If I test by sending initial data from my GET, the EditorFor displays the correct checkbox options, but when I POST, the count of the items is 0.
View:
#model EngineeringAssistantMVC.Controllers.FirmwareController.FirmwareViewModel
#using (Html.BeginForm("Upload", "Firmware", FormMethod.Post, new { #id = "uploadFirmwareForm", #class = "form-horizontal" }))
{
<!-- Device -->
<div class="form-group">
<div class="col-lg-1">
#Html.LabelFor(x => x.Device, htmlAttributes: new { #class = "control-label" })
</div>
<div class="col-lg-2">
#Html.DropDownListFor(x => x.Device, ViewBag.Devices as IEnumerable<SelectListItem>, new { #class = "form-control", #id = "Devices" })
</div>
<div class="col-lg-9">
#Html.ValidationMessageFor(x => x.Device, "", new { #class = "text-danger" })
</div>
</div>
#Html.EditorFor(x => x.SelectedModels, "SelectedModels", new { #id = "Models" })
#Html.HiddenFor(x => x.SelectedModels)
}
And the AJAX call:
function GetModels() {
$.ajax({
type: "GET",
url: '#Url.Action("GetModels", "Firmware", null)',
data: { SelectedDevice: $('#Devices').val() },
success: function (dataSet) {
//$('#Models').html(dataSet);
//$('#Models').data(dataSet);
//$('#Models').val(dataSet);
// How do I populate the EditorFor from the dataSet returned?
},
error: function (err) {
console.log("ERROR: " + err.responseText);
},
})
}
SelectedModels EditFor Template:
#model IEnumerable<EngineeringAssistantMVC.ViewModels.ModelViewModel>
#foreach (var item in Model)
{
#Html.CheckBoxFor(x => item.IsSelected)
#Html.Label(item.Description)
#Html.HiddenFor(x => item.ModelId)
#Html.HiddenFor(x => item.IsSelected)
#Html.HiddenFor(x => item.Description)
}
Controller:
[HttpPost]
public ActionResult Upload(HttpPostedFileBase uploadFile, FirmwareViewModel firmwareViewModel)
{
// firmwareViewModel.SelectedModels count is 0 here
}
ModelFirmware Class:
public class ModelFirmware
{
public int ModelFirmwareId { get; set; }
public int FirmwareId { get; set; }
public int ModelId { get; set; }
}
FirmwareViewModel:
public class FirmwareViewModel
{
public int FirmwareViewModelId { get; set; }
[Required]
public string Device { get; set; }
public ICollection<ModelViewModel> SelectedModels { get; set; }
}
I just can't get it to work correctly.
EDIT 1: - Add method that returns the models
[HttpGet]
public ActionResult GetModels(string SelectedDevice)
{
var deviceAbbreviation = _dbContext.Radios.Where(x => x.RadioName == SelectedDevice).Select(x => x.ProjectAbbreviation).FirstOrDefault();
var models = _dbContext.AnatomyModels.Where(x => x.SerialPrefix.StartsWith(deviceAbbreviation.Trim()) && x.ParentId == 0).ToList();
List<ModelViewModel> mvms = models.Select(x => new ModelViewModel()
{
ModelId = x.AnatomyModelId,
Description = x.SerialPrefix,
IsSelected = false,
}).ToList();
return Json(mvms);
}
There are numerous issues with your code.
First your not using the EditorTemplate correctly. Change its name to ModelViewModel.cshtml to match the name of the class, and locate it in the /Views/Shared/EditorTemplates (or /Views/YourControllerName/EditorTemplates) folder. The template is then based on a single object (note also the LabelFor() required to create a label associated with the checkbox, and you need to delete the hidden input for IsSelected)
#model ModelViewModel
#Html.CheckBoxFor(m => m.IsSelected)
#Html.LabelFor(m => m.IsSelected, Model.Description)
#Html.HiddenFor(m => m.ModelId)
#Html.HiddenFor(m => m.Description)
Refer also Post an HTML Table to ADO.NET DataTable to understand why your foreach loop would never have created the correct name attributes for model binding.
Then in the main view use
<div id="container">
#Html.EditorFor(m => m.SelectedModels)
</div>
and remove the hidden input for SelectedModels (but before you do, inspect the html for that element to understand why its value would never bind). The EditorFor() method will correctly generate the html for each item in your collection.
Next, change your GetModels() method to return a partial view based on FirmwareViewModel since that is what you will be posting back. Note that you could return a JsonResult, but that would mean generating a whole lot of html in the ajax call back that would not be strongly typed.
[HttpGet]
public PartialViewResult GetModels(string SelectedDevice)
{
var deviceAbbreviation = _dbContext.Radios.Where(x => x.RadioName == SelectedDevice).Select(x => x.ProjectAbbreviation).FirstOrDefault();
var models = _dbContext.AnatomyModels.Where(x => x.SerialPrefix.StartsWith(deviceAbbreviation.Trim()) && x.ParentId == 0).ToList();
List<ModelViewModel> mvms = models.Select(x => new ModelViewModel()
{
ModelId = x.AnatomyModelId,
Description = x.SerialPrefix,
IsSelected = false, // not really necessary since its the default
}).ToList();
FirmwareViewModel model = new FirmwareViewModel
{
SelectedModels = mvms
};
return PartialView(model);
}
and your GetModels.cshtml view will be
#model FirmwareViewModel
#Html.EditorFor(m => m.SelectedModels)
Then, modify your ajax call to add the partial view in the success callback
$.ajax({
type: "GET",
url: '#Url.Action("GetModels", "Firmware")', // do not need to add 3rd parameter
data: { SelectedDevice: $('#Devices').val() },
success: function (response) {
$('#container').html(response);
},
error: function (err) {
console.log("ERROR: " + err.responseText);
},
})
The .html() function will replace any elements already existing in the <div id="container"> element
Finally, since your using a view model, make use of it and do not use ViewBag. Your view model should contain a IEnumerable<SelectListItem> Devices property which you populate in the GET method (and use #Html.DropDownListFor(x => x.Device, Model.Devices, new { #class = "form-control" }) in the view (note also that the method generates id="Device"). It should also contain a HttpPostedFileBase property to avoid the additional parameter in the POST method, and allow you to add validation attributes.
I have a form that wanna to select category and tag from drop down list and bind it to a post , this is my ViewModel:
public class PostViewModel
{
public IList<Category> Category { get; set; }
public IList<Tag> Tag { get; set; }
}
and this is my get action :
public ActionResult Add()
{
ViewBag.CategoryList = new SelectList(_categoryRepository.GetAllCategory());
ViewBag.TagList = new SelectList(_tagRepository.GetAllTag());
return View();
}
now how can I get the Id dropdownlst to send it to the Post action?? :
<div>
#Html.LabelFor(post => post.Category)
#Html.DropDownListFor ????
#Html.ValidationMessageFor(post => post.Category)
</div>
I tried this one it it didn't work
<div>
#Html.LabelFor(post => post.Category)
#Html.DropDownListFor(post => post.Category, ViewBag.CategoryList as SelectList, "--- Select Category ---")
#Html.ValidationMessageFor(post => post.Category)
</div>
please give me a solution about this ,thanks
Try to avoid dynamic stuff like ViewBag and ViewData. Use strongly typed views.
ViewModel is just a POCO class which we will use to transfer data between your view and the action method. It will be specific to the view.
You have a viewmodel but you are not using it properly. Add 2 more properties to your viewmodel for getting the selected item from the dropdown. Also i changed the name of your proprties to pluralized for (Categories ,Tags) because they are for storing a collection.
public class PostViewModel
{
public List<SelectListItem> Categories{ get; set; }
public List<SelectListItem> Tags { get; set; }
public int SelectedCategory { set;get;}
public int SelectedTag { set;get;}
}
Now in your GET Action method, create an object of your view model and set the collection properties and then send that object to your view using View method.
public ActionResult Add()
{
var vm=new PostViewModel();
vm.Categories= GetAllCategories();
vm.Tags= GetAllTags();
return View(vm);
}
Assuming GetAllCategories and GetAllTags are 2 methods which returns a collection of SelectListItem for categories and tags.
public List<SelectListItem> GetAllCategories()
{
List<SelectListItem> categoryList=new List<SelectListItem>();
categoryList.Add(new SelectListItem { Text = "Sample", Value = "1" });
// TO DO : Read from your dB and fill here instead of hardcoding
return categoryList;
}
and in your view,
#model PostViewModel
#using(Html.BeginForm())
{
#Html.DropDownListFor(x => x.SelectedCategory,
new SelectList(Model.Categories,"Value","Text"), "Select")
#Html.DropDownListFor(x => x.SelectedTag,
new SelectList(Model.Tags,"Value","Text"), "Select")
<input type="submit" value="save" />
}
And in your HttpPost action method, you can get the selected items in the 2 properties we added
[HttpPost]
public ActionResult Add(PostViewModel model)
{
if(ModelState.IsValid)
{
//check model.SelectedCategory and model.SelectedTag
//save and redirect
}
//to do :reload the dropdown again.
return View(model);
}
you were close:
public class PostViewModel
{
public int CategoryId { get; set; } // <-- Altered
public int TagId { get; set; } // <-- Altered
}
<div>
#Html.LabelFor(post => post.Category)
#Html.DropDownListFor(post => post.CategoryId,
ViewBag.CategoryList as IEnumerable<SelectListItem>,
"--- Select Category ---")
#Html.ValidationMessageFor(post => post.Category)
</div>
I am having strange issue, MVC dropdown selected value is not preselected on page Load.
My Models are:
public class DocumentTypesViewModel
{
[Required(ErrorMessage = "DocumentType is required")]
public int OHDocumentTypeId { get; set; }
public string OHDocumentTypeDescription { get; set; }
}
public class ClientAdvancedSearchViewModel
{
[Display(Name = "Name")]
public string Name { get; set; }
[Display(Name = "DocumentType")]
public string DocumentTypeId { get; set; }
public IEnumerable<SelectListItem> DocumentTypes { get; set; }
}
In My Controllers I am populating the ClientAdvancedSearchViewModel like this
[HttpGet]
public ActionResult ClientAdvancedSearch()
{
ClientAdvancedSearchViewModel clientAdvancedSearchViewModel = iClientReferralRecordsRepository.GetDocumentMetadata();
//DocumentTypes Dropdown
var ddlDocumentTypes = iDocumentTypeRepository.GetDocumentTypes();
clientAdvancedSearchViewModel.DocumentTypes = new SelectList(ddlDocumentTypes, "OHDocumentTypeId", "OHDocumentTypeDescription",clientAdvancedSearchViewModel.DocumentTypeId);
return View(clientAdvancedSearchViewModel);
}
Finally in the View:
<td>
<div class="editor-label">
#Html.LabelFor(model => model.DocumentTypes)
</div>
<div class="editor-field">
#Html.DropDownListFor(x => x.DocumentTypeId, Model.DocumentTypes, "Please Select", new { #id = "ddlDocumentType" })
</div>
</td>
I believe the Name of the dropdown is same is x => x.DocumentTypeId, becuase of this I think, my value is not preselected.
This is the ViewSource for generated HTML for the Drop Down
<select id="ddlDocumentType" name="DocumentTypeId">
<option value="">Please Select</option>
<option value="20">records</option>
<option value="21"> record1</option>
..
How can I rename my dropdownlist name or How can I solve my problem?
Thank you
Updated: Added the missed line
ClientAdvancedSearchViewModel clientAdvancedSearchViewModel = iClientReferralRecordsRepository.GetDocumentMetadata();
Your code on your view is just right. You forgot to set the value for DocumentTypeId. This is your code as you posted:
[HttpGet]
public ActionResult ClientAdvancedSearch()
{
//DocumentTypes Dropdown
var ddlDocumentTypes = iDocumentTypeRepository.GetDocumentTypes();
clientAdvancedSearchViewModel.DocumentTypes = new SelectList(ddlDocumentTypes, "OHDocumentTypeId", "OHDocumentTypeDescription",clientAdvancedSearchViewModel.DocumentTypeId);
return View(clientAdvancedSearchViewModel);
}
And you missed this:
clientAdvancedSearchViewModel.DocumentTypeId = some_value;
Also, do you intend to have DocumentTypeId as an int instead of a string?
UPDATE:
You can also check that you set the id like this:
#Html.DropDownListFor(x => x.DocumentTypeId, new SelectList(Model.DocumentTypes, "Id", "Value", Model.DocumentTypeId), new { #id = "ddlDocumentType" })
Notice I used the overload with new SelectList. I don't remember all the overloads and I do it like that all the time, so you might check our the other overloads that suits your need.
An article view model
public class ArticleViewModel : ViewModelBase
{
[Required(ErrorMessage = "Required")]
public string Title { get; set; }
[Required(ErrorMessage = "Choose the language")]
public BELocale Locale { get; set; }
}
public class BELocale : BEEntityBase
{
public string OriginalName { get; set; }
public string FriendlyName { get; set; }
public string TwoLetterISOName { get; set; }
}
A view "AddLocaleForArticle"
#model Models.ArticleViewModel
#using (Html.BeginForm("VefifyAddingLocaleForArticle", "Administration"))
{
#Html.TextBoxFor(m => m.Title, new { disabled = "disabled" })
#Html.DropDownListFor(m => m.Locale,
new SelectList(ViewBag.AvalaibleLocales, "ID", "OriginalName"), "Select a language"
)
#Html.ValidationMessageFor(m => m.Locale)
<input type="submit" value="Save" />
}
An action
public ActionResult VefifyAddingLocaleForPhoto(ArticleViewModel article)
{
//article.Locale == null for some reason.
//but article.Title isn't null, it contains the data
return RedirectToAction("AddingLocaleForPhotoSuccess", "adminka");
}
Why article.Locale is equal null and how to fix it?
When the form is submitted a dropdown list sends only the selected value to the controller. So you cannot expect it to populate an entire complex object such as BELocale using a dropdownlist. The best you could is to populate its ID property and fetch the remaining of the object from your data store using this id.
So you will have to modify your dropdownlist helper so that it is bound to the id property of the locale as first argument:
#Html.DropDownListFor(
m => m.Locale.ID,
new SelectList(ViewBag.AvalaibleLocales, "ID", "OriginalName"),
"Select a language"
)
Now inside the corresponding controller action you will get the id:
public ActionResult VefifyAddingLocaleForPhoto(ArticleViewModel article)
{
// article.Locale.ID will contain the selected locale id
// so you can use this information to fetch the corresponding BELocale object
...
}
You may fill dropdown like this in your view model
public List<KeyValuePair<int, String>> locale
{
get
{
return _localerepo.Findlocals().Select(x => new KeyValuePair<int, string>(x.ID, x.OriginalName)).ToList();
}
}
In your view use this
<%:Html.DropDownListFor(x => x.ID, new SelectList(Model.locale, "key", "value"), "--Select locale--")%>
<%= Html.ValidationMessageFor(model => model.ID)%>