I have a modelView that contains an Object, similar to this
public class ExampleModelView
{
public T object { get; set; }
public ExampleModelView()
{
//...
}
}
To get that in my view I use
#Html.TextBoxFor(model => model.object.attribute1)
So the above line returns a html with a period, which is giving me problems, like this
<input class="form-control"
id="object_attribute1"
name="object.attribute1"
type="text" value="..."
aria-required="true"
aria-invalid="false">
When I submit the form I'm not being able to bind the field value like this
public ActionResult Cadastrar([Bind(Include = "attribute1")] ExampleModelView t)
{
//...
}
or those other ways
public ActionResult Cadastrar(ExampleModelView t)
{
//...
}
public ActionResult Cadastrar([Bind(Prefix= "object.attribute1")] ExampleModelView t)
{
//...
}
So, if I change TextBoxFor to TextBox I can bind the object, but not my whole modelView.
What should I do in this case?
EDIT
Changed above LabelFor to TextBoxFor.
Question
How do I bind the attributes with the period?
Related
I want to take only selected checkboxes. I tried this.
<input class="form-check-input" type="checkbox" name="Box" value="LDL" />
<label for="Box">LDL</label><br>
<input class="form-check-input" type="checkbox" name="Box" value="LDL" />
<label for="Box">LDL</label><br>`enter code here`
<input class="form-check-input" type="checkbox" name="Box" value="HDL" />
<label for="Box">HDL</label><br>
<div class="button-holder d-flex justify-content-center">
<button type="submit" class="btn btn-success">Send</button>
</div>
This is the Action
[HttpPost]
public IActionResult SendTest(ListParams listParams)
{
}
This is the DTO
public class CheckboxParams
{
public string Box { get; set; }
public bool IsChecked { get; set; }
}
public class ListParams
{
public ListParams()
{
this.AllCheckedParams = new List<CheckboxParams>();
}
public List<CheckboxParams> AllCheckedParams { get; set; }
}
In asp.net core, Model binding looks through the sources for the name pattern prefix.property_name. If nothing is found, it looks for just property_name without the prefix. Besides, the nested array matches the [index].property_name or property_name[index].property_name.
In your code, the name of the checkbox could not match the property in ListParams model. The correct way should be AllCheckedParams[index].box.
But for your scenario, your model design is not correct. The browser will only send the selected checkbox to the backend by default when form submit. If you choose the first two or last two(anyway,that is to say the index should be consecutive) checkbox, it works well. If you choose the first and third checkbox, it does not work any more. Because the name of them is AllCheckedParams[0].box and AllCheckedParams[2].box. They are not consecutive index of the array, so only the first checkbox could be matched and passed to the backend.
The correct way is that just change your backend received data like below:
[HttpPost]
public IActionResult SendTest(string[] box)
{
}
If you still want to use the model, you need change the model like below:
public class ListParams
{
public List<string> Box { get; set; }
}
Controller:
[HttpPost]
public IActionResult SendTest(ListParams listParams)
{
}
The HtmlHelper class provides two extension methods to generate a element in an ASP.NET MVC view. They are as follows:
CheckBox()
CheckBoxFor()
for creating form in razor page you can use
#model Student
#Html.CheckBoxFor(m => m.isActive)
The Html.CheckBox() is a loosely typed method which generates a with the specified name, isChecked boolean, and HTML attributes.
#Html.CheckBox("isActive", true)
This is just something that has been puzzling me, I'm wondering if there's a built in way for this.
Say you have a Package class
public class Package
{
public A AObject { get; set; }
public B BObject { get; set; }
}
And you have a view that uses this Package.
public ActionResult Action()
{
return View(new Package());
}
Now the view will accept this model and have 2 forms.
#model Path.To.Package
#Html.BeginForm("SubmitA", "MyController")
{
#Html.TextBoxFor(m => m.AObject.SomeProperty);
<input type="submit" />
}
#Html.BeginForm("SubmitB", "MyController")
{
#Html.TextBoxFor(m => m.BObject.AnotherProperty);
<input type="submit" />
}
If one would create two actions needed above that take Package as argument, this would work without question...
public JsonResult SubmitA(Package items) { ... }
public JsonResult SubmitB(Package items) { ... }
But at SubmitA the BObject would be null and in SubmitB AObject would be null.
My question here is whether you can submit only a part of the model? So the first form would only submit AObject and the second BObject so you could actually reach these via the following actions:
public JsonResult SubmitA (A a) { ... }
public JsonResult SubmitB (B b) { ... }
You can use the Prefix property of BindAttribute to bind to complex properties of a model. The attribute effectively removes the prefix from the submitted name/value pairs when binding to model.
Your controller methods would be
public JsonResult SubmitA([Bind(Prefix = "AObject")]A model) { ... }
public JsonResult SubmitB([Bind(Prefix = "BObject")]B model) { ... }
You should really use separate view model for each form. You can of course, use bind attribute or use specific property names in the controller action. But, that doesn't solve your real problem. You can only get either of the values and the other object will be unassigned or NULL. This is why you should have separate view model for each view / form. You can build your Package object once you have values for both objects.
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 a CreateViewModel.
public class CreateViewModel
{
public AttributesViewModel AttributesInfo { get; set; }
}
The AttributesViewModel is sent to a partial view.
public class AttributesViewModel
{
public AttributesViewModel()
{
ChosenAttributes = new List<int>();
}
public List<Attributes> Attributes { get; set; }
public List<int> ChosenAttributes { get; set; }
}
The List of Attributes is outputted in the partial view. Each one has a checkbox.
foreach (var attribute in Model.Attributes)
{
<input type="checkbox" name="ChosenAttributes" value="#attribute.ID" /> #Attribute.Name
}
When I post CreateViewModel, AttributesInfo.ChosenAttributes is always empty even though I checked some boxes. How do I properly name each checkbox so that it binds to the ChosenAttributes List?
My Solution
I took Stephen Muecke's suggestion to do the two way binding. So, I created a CheckboxInfo class that contained Value, Text, and IsChecked. I created a EditorTemplate for it:
#model Project.CheckboxInfo
#Html.HiddenFor(model => model.Text)
#Html.HiddenFor(model => model.Value)
#Html.CheckBoxFor(model => model.IsChecked) #Model.Text
One GIANT caveat. To get this to work properly, I had to create an EditorTemplate for the AttributesViewModel class. Without it, when CreateViewModel is posted, it cannot link the checkboxes to AttributesInfo.
Your naming the checkbox name="ChosenAttributes" but CreateViewModel does not contain a property named ChosenAttributes (only one named AttributesInfo). You may be able make this work using
<input type="checkbox" name="AttributesInfo.ChosenAttributes" value="#attribute.ID" /> #Attribute.Name
but the correct approach is to use a proper view model that would contain a boolean property (say) bool IsSelected and use strongly typed helpers to bind to your properties in a for loop or using a custom EditorTemplate so that your controls are correctly names and you get 2-way model binding.
I had a similar scenario, but this was how I did it. The solution is not perfect so please excuse if I have left something out, but you should be able to relate. I tried to simplify your solution as well :)
I changed the Attribute class name to CustomerAttribute, rename it to whatever you like, use a singular name, not plural. Add a property to your CustomerAttribute class, call it whatever you like, I called mine IsChange.
public class CustomerAttribute
{
public bool IsChange { get; set; }
// The rest stays the same as what you have it in your Attributes class
public string Name { get; set; } // I'm assuming you have a name property
}
Delete your AttributesViewModel class, you don't really need it, I like simplicity.
Modify your CreateViewModel class to look like this:
public class CreateViewModel
{
public CreateViewModel()
{
CustomerAttributes = new List<CustomerAttribute>();
}
public List<CustomerAttribute> CustomerAttributes { get; set; }
}
Your controller will look something like this:
public ActionResult Create()
{
CreateViewModel model = new CreateViewModel();
// Populate your customer attributes
return View(model);
}
Your post controller action method would look something like this:
[HttpPost]
public ActionResult Create(CreateViewModel model)
{
// Do whatever you need to do
}
In your view, you will have something like this:
<table>
<tbody>
#for (int i = 0; i < Model.CustomerAttributes.Count(); i++)
{
<tr>
<td>#Html.DisplayFor(x => x.CustomerAttributes[i].Name)</td>
<td>#Html.CheckBoxFor(x => x.CustomerAttributes[i].IsChange)</td>
</tr>
}
<tbody>
</table>
Create a sample app and try out the code above and see if it works for you.
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