In one of my previous questions, I asked how I would approach populating a dropdown on my razor page by pulling info from the database. See my question here: Populating dropdown via foreign key
I was advised to do this by adding the following to my PageModel:
public List<Data.Vendor> Vendores { get; set; }
public void OnGet()
{
List<SelectListItem> test = new List<SelectListItem>();
//List<Data.File> files = new List<Data.File>();
Vendores = _context.Vendors.ToList();
foreach (var item in Vendores)
{
test.Add(new SelectListItem { Text = item.VendorName, Value = item.Id.ToString() });
}
ViewData["demo"] = test;
}
and on the .cshtml adding the following:
<select asp-for="Files.VendorId" asp-items="#((List<SelectListItem>)ViewData["demo"])"></select>
But when submitting the form in which I want to save the Dropdown selection, when it gets to my Postasync:
[BindProperty]
public Data.File Files { get; set; }
public Data.Vendor Vendors { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Files.Add(Files);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
The data for that field doesn't get passed through.
If you need to see the full PageModel and .cshtml files I can add them.
I've recorded a video of me going through the process and showing that Vendor Id is null which means nothing is passing through:
https://drive.google.com/file/d/1CxObYQ9czs4OOJYCyr5OMmu-64X9iThz/view?usp=sharing
After I checking the code in Create.cshtml, I find the reason why VendorId can't be bind to the model.
In your Create.cshtml, you have the code:
//......
<div class="form-group">
<label asp-for="Files.VendorId" class="control-label" style="color: white"></label>
<input asp-for="Files.VendorId" class="form-control" />
<span asp-validation-for="Files.VendorId" class="text-danger"></span>
</div>
//......
<select asp-for="Files.VendorId" asp-items="#((List<SelectListItem>)ViewData["demo"])"></select>
//........
Because they all use asp-for="Files.VendorId" to bind value, So they will generate the same name : Files.VendorId in html.
If there are multiple input attributes with the same name in the form, It will only bind the value of the first input attribute.
In your video, you don't write any value in VendorId input tag, So VendorId property in Files will not bind any value
You can just delete this input attribute and then Files.VendorId will bind the value of dropdown list successfully.
Related
I have a set of dropdown values defined in a global .cs file:
mydropdowns.cs
public class Foo_DropdownValues
{
public static List<SelectListItem> NoYes { get; } = new List<SelectListItem>
{
new SelectListItem { Value = "false", Text = "No"},
new SelectListItem { Value = "true", Text = "Yes" }
};
...
I'm using these SelectLists in different places in my .cshtml web form:
mypage.cshtml
<div class="form-group" id="PC_QU10_div">
<label>#Foo_Questions.QU10</label>
<select asp-for="Foo_Answers.QU10" asp-items="Foo_DropdownValues.NoYes"></select>
</div>
<div class="form-group" id="PC_QU20_div">
<label>#Foo_Questions.QU20</label>
<select asp-for="Foo_Answers.QU20" asp-items="Foo_DropdownValues.NoYes"></select>
...
I'd like to specify a default of "Yes" for the first item, and a default of "No" for the second?
Q: Is there any way for my to specify an explicit default in the .cshtml markup? On a per-item basis?
My app is written in C# in .Net Core 3.1, if it matters.
When a view has a model type specified, input controls for each of the properties of model object can be created using either the HTML Helper methods (#Html.TextBoxFor, #Html.CheckBoxFor, #Html.RadioButtonFor, #Html.DropdownListFor) etc. or using asp-for attributes in the input controls.
Both of these above mentioned ways, makes sure that the value entered/selected in the input control will be assigned to the model object properties when form is submitted and also the input controls are populated with the respective property values from the model object if the model object is being passed to the view from the server.
That's why, in this particular case, if the dropdown needs to have a specific value pre-selected (or by default selected), a value needs to be assigned to the dropdownlist. And it can be easily done by populating value in the property of the model object.
Consider example below:
Model class:
public class PersonData
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Gender { get; set; }
public bool IsMarried { get; set; }
public bool IsLeftHanded { get; set; }
}
Static list of items for dropdownlist.
public static class StaticValues
{
public static SelectListItem[] Genders
{
get
{
return new SelectListItem[]
{
new SelectListItem("Male", "Male"),
new SelectListItem("Female", "Female")
};
}
}
public static SelectListItem[] YesNoItems
{
get
{
return new SelectListItem[]
{
new SelectListItem("Yes", "True"),
new SelectListItem("No", "False")
};
}
}
}
View Code:
#model PersonData;
#{
ViewData["Title"] = "Person data";
}
<div>
<label asp-for="FirstName">Firstname:</label>
<input asp-for="FirstName"/>
</div>
<div>
<label asp-for="LastName">Lastname:</label>
<input asp-for="LastName" />
</div>
<div>
<label asp-for="Gender">Firstname:</label>
<select asp-for="Gender" asp-items="#StaticValues.Genders"></select>
</div>
<div>
<label asp-for="IsMarried">Married:</label>
<select asp-for="IsMarried" asp-items="#StaticValues.YesNoItems"></select>
</div>
<div>
<label asp-for="IsLeftHanded">Left handed:</label>
<select asp-for="IsLeftHanded" asp-items="#StaticValues.YesNoItems"></select>
</div>
In the following code from Controller Action method, model object is populated with a few properties with values assigned to them. And the model object is passed to the View.
public async Task<IActionResult> Index()
{
var personData = new PersonData();
// This assignment should populate the text box with John value
personData.FirstName = "John";
// Gender dropdown should have Male pre-selected
personData.Gender = "Male";
// IsMarried dropwodn should have "Yes" pre-selected.
personData.IsMarried = true;
return View(personData);
}
Following is the view rendered when application is run.
In a different use case, there can be a requirement where a specific value needs to be pre-selected by default when model property does not have value assigned to the property.
In such situations, specific SelectListItem should have Selected property set to true.
For example, in below list of SelectListItem New York has true passed as third argument in constructor. That will mark that item as selected be-default in the dropdownlist.
public static SelectListItem[] OfficeLocations
{
get
{
return new SelectListItem[]
{
new SelectListItem("London", "LON"),
new SelectListItem("New York", "NY", true),
new SelectListItem("Singapore", "SG")
};
}
}
Now I will add new property OfficeLocation to PersonData class as following.
public string OfficeLocation { get; set; }
And add following code to view.
<div>
<label asp-for="OfficeLocation">Office location:</label>
<select asp-for="OfficeLocation" asp-items="#StaticValues.OfficeLocations"></select>
</div>
Now if the model object does not have any value assigned to OfficeLocation property, the OfficeLocation dropdown will have New York selected by default.
The view will look as following.
I hope this will help you resolve your issue.
There are 2 options that I know of that can work depending on requirements:
1.) Add a disabled default option directly in the mark-up
<select ...>
<option disabled value="">Choose an option</option>
</select>
2.) Set the default selected value in your PageModel / Code behind before returning the view. If you want to continue to use a static reference for the common list, you can create a static method that takes a bool selected parameter instead of a readonly property:
var items = new List<SelectListItem>();
...
items.Insert(0, new SelectListItem("No", "No", selected: true));
//..insert other items
I'm moving from WPF development to Asp MVC and have started doing an Asp MVC app. So far I've set up my:
model
controller
and
view(with the relevant fields.)
The next step involves sending the data entered in my form to the controller on the Submit button click.
I know in WPF I can bind the control properties to a property in the viewmodel, also there would be a click event on the button which I don't see in MVC.
This is my pseudo understanding of how to do that in MVC but correct me if I' wrong (Model is of type Case):
1.Set button click event to send form data to controller.
2.Pass data into controller constructor and assign to typed Case object.
Question:
How can you pass view values on button submit to a typed object in a controller?
Code:
View -
<form action="" method="post">
<div class="form-horizontal">
<div class="col-lg-6">
<!-- SELECT STATUS STATIC-->
<div class="form-group">
<label class="col-md-3 control-label" for="Current Status">Status</label>
<div class="col-md-8">
<select id="Status" name="Status" onchange="" class=" form-control">
<option value="Down">Down</option>
<option value="Up">Up</option>
</select>
</div>
</div>
<!-- SELECT APP STATIC-->
<div class="form-group">
<label class="col-md-3 control-label" for="App">App</label>
<div class="col-md-8" >
<select id="App" name="App" onchange="" class=" form-control">
<option value="SAP">SAP</option>
<option value="JAP">JAP</option>
</select>
</div>
</div>
<asp:Button id="b1" Text="Submit" runat="server" />
</div>
</div>
</form> <!--
Controller -
public class CaseController : Controller
{
public ActionResult Index()
{
return View();
}
}
Model -
Public class Case
{
public string Status { get; set; }
public string App { get; set; }
}
I hope that I understand your scenario well? You have a form with two drop down lists and a submit button? When you click the submit button you want to extract the selected values? This is how I understand it and this is how I will try to explain my answer with examples.
I would suggest that you bind your view/page to a view model. A view model is a kind of model that will represent your data in the view, whether it be textboxes, drop down lists, textareas, radio buttons, checkboxes, etc. It can also display static text on your view. I wrote a detailed answer as to what a view model is, if you have the time please go and read it:
What is ViewModel in MVC?
Go and create your view model. It will contain two lists that will represent your two drop down lists. Each list has an id associated with it that will contain the value of the selected drop down list item:
public class CaseViewModel
{
public int StatusId { get; set; }
public List<Status> Statuses { get; set; }
public int AppId { get; set; }
public List<App> Apps { get; set; }
}
Your domain models, namely Status and App, for the above mentioned lists:
public class Status
{
public int Id { get; set; }
public string Name { get; set; }
}
public class App
{
public int Id { get; set; }
public string Name { get; set; }
}
Now that you have this setup your next step is to populate these lists in your controller's action method. Ideally you would populate it with values from a database, but in your case I guess it is ok to hard code these values:
public ActionResult Index()
{
CaseViewModel model = new CaseViewModel();
model.Statuses = new List<Status>();
model.Statuses.Add(new Status { Id = 1, Name = "Down" });
model.Statuses.Add(new Status { Id = 2, Name = "Up" });
model.Apps = new List<App>();
model.Apps.Add(new App { Id = 1, Name = "SAP" });
model.Apps.Add(new App { Id = 2, Name = "JAP" });
return View(model);
}
As soon as you have populated your two lists, you pass the view model directly to the view. The view will receive a strongly type model and will do with it what it needs to do with it. In your case, a form will be created with two drop down lists and a submit button. I have left out all your CSS for clarity (just go and add it):
#model WebApplication_Test.Models.CaseViewModel
#using (Html.BeginForm())
{
<div>
#Html.DropDownListFor(
m => m.StatusId,
new SelectList(Model.Statuses, "Id", "Name", Model.StatusId),
"-- Select --"
)
#Html.ValidationMessageFor(m => m.StatusId)
</div>
<div>
#Html.DropDownListFor(
m => m.AppId,
new SelectList(Model.Apps, "Id", "Name", Model.AppId),
"-- Select --"
)
#Html.ValidationMessageFor(m => m.AppId)
</div>
<div>
<button type="submit">Submit</button>
</div>
}
So now you have two drop down lists populated with data. Select a value in each and press the submit button. Your view is bound to a view model and will retain values on form submission. Values in lists are not kept on form submission and will need to be populated again:
[HttpPost]
public ActionResult Index(CaseViewModel model)
{
// Check form validation
if (!ModelState.IsValid)
{
// If validation fails, rebind the lists and send the current view model back
model.Statuses = new List<Status>();
model.Statuses.Add(new Status { Id = 1, Name = "Down" });
model.Statuses.Add(new Status { Id = 2, Name = "Up" });
model.Apps = new List<App>();
model.Apps.Add(new App { Id = 1, Name = "SAP" });
model.Apps.Add(new App { Id = 2, Name = "JAP" });
return View(model);
}
// Form validation succeeds, do whatever you need to do here
return RedirectToAction("Index");
}
I hope this helps.
In the view just add a button in the form like
<button id="b1" Text="Submit"/>
In the controller add an action method to handle the post.
public class CaseController : Controller
{
[HttpPost]
public ActionResult Index(Case case)
{
//Do Something
return View();
}
public ActionResult Index()
{
return View();
}
}
You may also want to look into using Razor and strongly typed views. Makes things much simpler.
another approach is to use mvc ajax call, by doing these you also can pass parameter to controller from simple parameter to a selected row in gridview.
On the view in the button control add onclick property that point to a javascript function and passing parameter. In these sample will get selected row on the gridview
<input id="GenerateData" type="button" value="GenerateData" onclick="generateData(App.grdNameOfGridview.getRowsValues({selectedOnly:true}) [0]);" />
On the view create a javascript function that use ajax to call the controller, note the paramater that can be passing from click button event to the javascript function that will use ajax to call the controller. In this sample i use extjs framework, if you want you can also use jquery as well
generateData= function (recordData) {
var jsonStringContractUnit = JSON.stringify(recordData);
Ext.Ajax.request({
url: '../ControllerName/GenerateData',
method: 'POST',
params: {
SelectedContractUnit: jsonStringContractUnit
},
On the controller the parameter will be pass from view and will be store on SelectedData
public ActionResult GenerateData(string SelectedData)
{
}
I have a list of teams on my index page.
I'm trying to pass the text of an input(type text) from the index view back to the index controller, to reload the index page, this time only displaying items in my list which have matching text. eg - bob = bob
Index Controller
public ActionResult Index(string searchString)
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
var listOfTeams = from T in db.Teams
select T;
if (!String.IsNullOrEmpty(searchString))
{
listOfTeams = listOfTeams.Where(T => T.TeamName.Contains(searchString));
}
return View(listOfTeams.ToList());
}
How i'm trying to pass the data in the Index view
I've tried
<input type="text" id="inputTeamSearch" name="searchString" class="form-control" style="width:225px;height:60px" onblur="IsTextEmpty()" oninput="CheckTeams()" placeholder="Search">
#Html.ActionLink("Search", "Index")
and
#using(Html.BeginForm("Index", "Team"))
{
<input type="text" id="inputTeamSearch" name="searchString" class="form-control" style="width:225px;height:60px" onblur="IsTextEmpty()" oninput="CheckTeams()" placeholder="Search">
<input type="submit" id="Index" value="Index" />
Html.EndForm();
}
I'm sure this is probably a duplicate of some sort, if so please just pass me in the appropriate direction. I've looked for answers, but they're either long-winded or go into more complex detail than this.
So to post data to a controller you need a seperate post action which is decorated with the HttpPost attribute. This method needs to take a model as it's parameter:
[HttpPost]
Public ActionResult Index(IndexVM model)
{
var searchTerm = model.SearchTerm;
}
The view model needs to contain the fields that you intend to post.
Public class IndexVM
{
Public String SearchTerm { get; set; }
//Other model examples
public Int32 PageNumber { get; set; }
public Int32 NumOfItemsPerPage { get; set; }
}
Then your html needs to contain a text box that has the same name as the string property in your view model.
#Html.TextBoxFor(m => m.SearchTerm)
//And at the top of your html page you will need to include the model
#model Domain.Models.IndexVM
OR
<input type="text" name="SearchTerm">
Should work.
If you are already using an entity model you can create a new View model which contains the old entity and whatever else you need. So:
public class IndexVM
{
public Team Team { get; set; }
public String SearchTerm { get; set; }
}
Then on your index GET method where you're passing your team to your view you would have:
var view = new IndexVM();
view.Team = //your team object
return View(view);
I have the view that contains the checkbox and Submit button as shown below.
#using (Html.BeginForm())
{
<fieldset>
<legend style="font-size: 100%; font-weight: normal">Delete</legend>
<p> Are you sure you want to delete?</p>
#foreach (string resource in resources)
{
if (resource != "")
{
<input type="checkbox" name="Resources" title="#resource" value="#resource" checked="checked"/>#resource
<br />
}
}
<br />
#Html.HiddenFor(m => m.AttendeeListString)
#Html.HiddenFor(m => m.ResourceListString)
<span class="desc-text">
<input type="submit" value="Yes" id="btnYes" />
</span>
<span class="desc-text">
<input type="submit" value="No" id="btnNo" />
</span>
</fieldset>
}
Below is the Controller code...
public ActionResult DeleteResource(RoomModel roomModel)
{
...
}
RoomModel contains some other data...
Now how can i access the checkbox value in controller?
Note : I have lot more information that need to be send to Controller when i clicked on submit button... Can anybody suggest some solution....
Answer :
I have added these two property to My model
public List<SelectListItem> Resources
{
get;
set;
}
public string[] **SelectedResource**
{
get;
set;
}
And My view check box i have updated as follows
#foreach (var item in Model.Resources)
{
<input type="checkbox" name="**SelectedResource**" title="#item.Text" value="#item.Value" checked="checked"/>#item.Text
<br /><br />
}
And in Controller ...
if (roomModel.SelectedResource != null)
{
foreach (string room in roomModel.**SelectedResource**)
{
resourceList.Add(room);
}
}
Note: The name of check box and Property in the model should be same. In my case it is SelectedResource
You have a few options. The easiest would be:
1) Parameter bind a view model with the Resources property. I recommend this way because it's the preferred MVC paradigm, and you can just add properties for any additional fields you need to capture (and can take advantage of validation easily by just adding attributes).
Define a new view model:
public class MyViewModel
{
public MyViewModel()
{
Resources = new List<string>();
}
public List<string> Resources { get; set; }
// add properties for any additional fields you want to display and capture
}
Create the action in your controller:
public ActionResult Submit(MyViewModel model)
{
if (ModelState.IsValid)
{
// model.Resources will contain selected values
}
return View();
}
2) Parameter bind a list of strings named resources directly in the action:
public ActionResult Submit(List<string> resources)
{
// resources will contain selected values
return View();
}
It's important to note that in the question, the view is creating checkboxes that will send the string value of all checked resources, not boolean values (as you might expect if you used the #Html.CheckBox helper) indicating if each item is checked or not. That's perfectly fine, I'm just pointing out why my answer differs.
In MVC action, have a parameter that corresponds to the name of the checkbox, something like:
bool resources
bool[] resources
use javascript or jquery to collect all the value and post to the controller
var valuesToSend='';
$('input:checked').each(function(){
valuesToSend+=$(this).val() + "$";//assuming you are passing number or replace with your logic.
});
and after submit call ajax function
$.ajax({
url:'yourController/Action',
data:valuesTosend,
dataType:'json',
success:function(data){//dosomething with returndata}
})
or else you can pass the model to controller. if you implemented Model -View-ViewModel pattern.
public class yourViewModel
{
public string Id { get; set; }
public bool Checked { get; set; }
}
Action methods
[HttpPost]
public ActionResult Index(IEnumerable<yourViewModel> items)
{
if(ModelState.IsValid)
{
//do with items. (model is passed to the action, when you submit)
}
}
I'm assuming that the resources variable is generated in the Controller or can be placed onto the ViewModel. If so, then this is how I would approach it:
Your view model would have a Resources dictionary added to it, and would look something like this:
public class RoomModel
{
public Dictionary<string,bool> Resources { get; set; }
// other values...
}
You populate the Resources Dictionary with the names of your resource items as the key (string) and set the "checked" value (bool) to a default state of false.
e.g. (in your [HttpGet] controller)
// assuming that `resource` is your original string list of resources
string [] resource = GetResources();
model.Resources = new Dictionary<string, bool>();
foreach(string resource in resources)
{
model.Resources.Add(resource, false);
}
To render in the view, do this:
#foreach (string key in Model.Resources.Keys)
{
<li>
#Html.CheckBoxFor(r => r.Resources[key])
#Html.LabelFor(r => r.Resources[key], key)
</li>
}
This will then enable the [HttpPost] controller to automatically populate the dictionary onto the Model when you post back:
public ActionResult DeleteResource(RoomModel roomModel)
{
// process checkbox values
foreach(var checkbox in roomModel.Resources)
{
// grab values
string resource = checkbox.Key;
bool isResourceChecked = checkbox.Value;
//process values...
if(isResourceChecked)
{
// delete the resource
}
// do other things...
}
}
I have added these two property to My model
public List<SelectListItem> Resources
{
get;
set;
}
public string[] **SelectedResource**
{
get;
set;
}
And My view check box i have updated as follows
#foreach (var item in Model.Resources)
{
<input type="checkbox" name="**SelectedResource**" title="#item.Text" value="#item.Value" checked="checked"/>#item.Text
<br /><br />
}
And in Controller ...
if (roomModel.SelectedResource != null)
{
foreach (string room in roomModel.**SelectedResource**)
{
resourceList.Add(room);
}
}
Note: The name of check box and Property in the model should be same. In my case it is SelectedResource
Based on Darin's answer to my question Ho to display multiple checkbox selection based on user's selection from dropdown?
I am displaying multiple checkboxes based on dropdown selection.
Now, once the user post the form (with multiple inputs) that i have on my page, i collect all the data using FormCollection. And the problem i have is how can i pull those selected checkbox values from formcollection? The number of checkbox will change on different selection from the drop-down, so i think requesting each checkbox value will not work.
Can anyone help me with this problem.
The flow is as shown below:
Properties in Model
public class Subcategory
{
public string Name { get; set; }
public int ID { get; set; }
public bool Flag { get; set; }
}
Displaying PartialView in actual view where other form inputs are there:
<div id="checkboxlist">
#if (Model.SubCategories != null)
{
#Html.Partial("SubCategories", Model.SubCategories)
}
</div>
PartialView SubCategories.cshtml
#model IEnumerable<MyProject.Entities.Subcategory>
#{
// we change the HTML field prefix so that input elements
// such as checkboxes have correct names in order to be able
// to POST the values back
ViewData.TemplateInfo.HtmlFieldPrefix = "checkboxlist";
}
<span>subcategory</span>
<div id="subcategories" style="margin-left: 130px;margin-top: -20px;" data-role="fieldcontain">
<fieldset data-role="controlgroup">
#Html.EditorForModel()
</fieldset>
</div>
EditorTemplates Subcategory.cshtml
#model MyProject.Entities.Subcategory
<div class="editor-label">
#Html.CheckBoxFor(c => c.Flag, new { type = "checkbox" })
<label for="#Model.ID">#Model.Name</label>
#Html.HiddenFor(c => c.Flag)
#Html.HiddenFor(c => c.ID)
#Html.HiddenFor(c => c.Name)
</div>
jquery to display checkboxes based on dropdown selection:
$('#Category').change(function () {
var subcategoriesUrl = $(this).data('subcategoriesurl');
var categoryId = $(this).val();
$('#checkboxlist').load(subcategoriesUrl, { category: categoryId });
});
Don't use FormCollection. That's weakly typed. Use view models. Like this:
[HttpPost]
public ActionResult Foo(MyViewModel model)
{
// model.BusinessSubCategories should contain a list of Subcategory
// where for each element you could use the Flag property to see if
// it was selected or not
...
}
Also notice that you have an inconsistency between the field prefix that you are using in your partial:
ViewData.TemplateInfo.HtmlFieldPrefix = "checkboxlist";
and the view model collection property: Model.BusinessSubCategories. So make sure you fix the prefix to use the correct property name if you want the default model binder to be able to populate this property when you post back.