Loop seems to yield the same value every time - c#

I have a string array called Groups containing the following two strings
Group Alpha
Group Bravo
Controller
public ActionResult Task(TaskModel model)
{
return PartialView(model);
}
Index view which invokes my Task action
<div id="update">
#using (Ajax.BeginForm("Task", "Home", new AjaxOptions { UpdateTargetId = "update", InsertionMode = InsertionMode.Replace}))
{
<input value="Group Alpha" name="Groups"/>
<input value="Group Bravo" name="Groups"/>
<button type="submit">Submit</button>
}
</div>
My task view
#model MvcApplication4.Models.TaskModel
<div id="container">
#if (Model.Groups != null)
{
foreach (var group in Model.Groups)
{
#Html.TextBox("Groups", group, new { #id = String.Empty })
}
}
</div>
my task model
public class TaskModel : IValidatableObject
{
public Guid TaskId { get; set; }
public String Name { get; set; }
public string[] Groups { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
yield break;
}
}
This only outputs Group Alpha Group Alpha. Wheres the problem??
Sorry for not being clear before
Example project replicating the issue https://www.dropbox.com/s/s2o59cu00am3eov/MvcApplication4.zip

You POST some input fields named Groups. In the action method processing that POST, you render a partial that uses #Html.TextBox("Groups", ...).
#Html.TextBox uses the ModelState to fill the value with the previously POSTed value, based on input element name. In this case it takes the first Groups value it finds in the POST, being "Group Alpha".
Clear the ModelState (or just the Groups key) before rendering your partial view.
This:
ModelState.Clear();
ModelState.Remove("Groups");
return PartialView(model);
Works.
Next time try showing all relevant code up front, so we don't need so many comments asking you to. :-)

This is a closure problem I think. Try this code:
<div id="container">
#if (Model.Groups != null)
{
foreach (var group in Model.Groups)
{
var currentGroup = group;
#Html.TextBox("Groups", currentGroup, new {#id = String.Empty});
}
}
</div>

Related

Passing data from HttpGet to View to HttpPost in MVC [duplicate]

This question already has answers here:
Post an HTML Table to ADO.NET DataTable
(2 answers)
Closed 4 years ago.
I ran out of options checking why my HttpPost parameter is always null.My Models, View and Controllers are below. In general, I have a model with all details of medication administered to patients (Model 1) below. Then, I created another model with one property which is a list of my first model (Model 2). Model 2 is being passed to the view as shown in the HttpGet action. My HttpPost action accepts a parameter which is a list of Model 1. This list is being passed in my view in the following line which is within the View:
using (Html.BeginForm("ClientChargeInput", "Home", new { #vm = Model.patients.ToList() }, FormMethod.Post, null))
Any feedback?
Model
public class ControlledSubstancesEntity
{
public string facility { get; set; }
public string program_x_rrg_value { get; set; }
public string PATID { get; set; }
//More properties
}
Model being passed to View
public class ControlledSubsViewModel
{
public List<ControlledSubstancesEntity> patients { get; set; }
}
HttpGet Controller Action
[HttpGet]
public ActionResult ClientChargeInput(DateTime adminDate, string Facility)
{
ControlledSubsViewModel vm = new ControlledSubsViewModel();
//Some logic that populates vm
return View(vm);
}
View
#model ControlledSubstancesChargeInput.Models.ControlledSubsViewModel
#{
ViewBag.Title = "ClientChargeInput";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#if (Model != null && Model.patients.Count() > 0)
{
using (Html.BeginForm("ClientChargeInput", "Home", new { #vm = Model.patients.ToList() }, FormMethod.Post, null))
{
<input type="submit" value="Charge Input" id="btnSubmit" class="btn btn-info btn-sm" onclick="PostCharge()" />
#*Fields are shown*#
#foreach (var p in Model.patients)
{
#Html.HiddenFor(model => p.facility)
#Html.HiddenFor(model => p.program_x_rrg_code)
#Html.HiddenFor(model => p.program_x_tx_setting_code)
}
}
}
else
{
#*#Html.Action("ShowReport");*#
}
HttpPost Controller Action
[HttpPost]
public ActionResult ClientChargeInput(List<ControlledSubstancesEntity> vm)
{
//On breakpoint vm is always null
}
You can try to use ControlledSubsViewModel instead of List<ControlledSubstancesEntity> class, then you can use vm.patients to do your logic.
because you use ControlledSubsViewModel in the razor view that pass the parameter format will like ControlledSubsViewModel.
[HttpPost]
public ActionResult ClientChargeInput(ControlledSubsViewModel vm)
{
//vm.patients use this property
}
Because Model.patients is a collection, you can use for loop set input tag (hidden or text) look like this.
#if (Model != null && Model.patients.Count() > 0)
{
using (Html.BeginForm("ClientChargeInput", "Home",FormMethod.Post))
{
<input type="submit" value="Charge Input" id="btnSubmit" class="btn btn-info btn-sm" onclick="PostCharge()" />
for (int i = 0; i < Model.patients.Count; i++)
{
#Html.TextBoxFor(m => Model.patients[i].PATID);
#Html.TextBoxFor(m => Model.patients[i].facility);
#Html.TextBoxFor(m => Model.patients[i].program_x_rrg_value);
}
}
}
else
{
#*#Html.Action("ShowReport");*#
}
You still need to create ControlledSubstancesEntity as hidden-fields even if you do not want to display them on the page.
#using (Html.BeginForm("Index", "Home", FormMethod.Post, null))
{
for (int i = 0; i < Model.patients.Count; i++)
{
#Html.HiddenFor(x => Model.patients[i].facility)
#Html.HiddenFor(x => Model.patients[i].program_x_rrg_value)
#Html.HiddenFor(x => Model.patients[i].PATID)
}
<input type="submit" value="Charge Input" id="btnSubmit"
class="btn btn-info btn-sm" onclick="PostCharge()" />
}
Then use ControlledSubsViewModel as a parameter in HttpPost action method.
(click on the image to view in full screen)

ASP.NET MVC Ajax form not routing values

This is a sister question from another question I posted (here). Many thanks to #nurdyguy and all of the people who helped me previously. However, I am having an issue where I am unable to pass a list of values to a destination controller that's in another View. My desired output is to send over the complete List object but right now I am getting a value count of zero (0) when it gets to the controller.
Here is my Model:
using System.Collections.Generic;
namespace Foo.Models
{
public class FooViewModel
{
public List<Foo> FooCollection = new List<Foo>();
/*Contains two properties
string CarName {get; set;}
string Color {get; set;}
List<Features> Features = new List<Features>();
*/
}
}
My View
#model Foo.Models.FooViewModel
#{
var RedCars = Model.FooCollection.Where(c => c.Color == "Red").ToList();
... //{yellow, blue, green}
}
<div id="FooCollection">
<section class="no-padding-top no-padding-bottom">
<div class="container-fluid">
<div class="public-user-block block">
<div class="row d-flex align-items-center">
<!--Red Cars-->
#using (Ajax.BeginForm("../Bar/Index/Red", null,
new AjaxOptions
{
HttpMethod = "post",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "CarsList"
}, new { id = "RedCarsForm" }))
{
<input type="hidden" name="Cars" value="#RedCars" />
<div id="status-container" class="col-lg-3 d-flex align-items-center">
<button type="submit">#RedAlerts.Count</button>
<strong>Red Cars</strong>
</div>
}
<!-- same structure for yellow, green, blue --!>
</section>
</div>
My Controller:
public ActionResult Index()
{
foreach (var car in db.database.Db_GetCars())
{
model.FooCollection.Add(new Foo()
{
CarName = car.CarName,
Color= car.Color
});
}
return View(model);
}
Destination Controller:
namespace Foo.Controllers
{
public class BarController: Controller
{
BarViewModel model = new BarViewModel();
[HttpPost, Route("/Bar/Index/{color}")]
public ActionResult Index(List<Foo> Cars)
{
//logic goes here
return View(model);
}
}
}
I spent some time digging around the best practices to route data but most places I've went have their forms set up like mine and it seems to work (for them). I even passed (as the second parameter) new { Cars = RedCars } but I still get zero (0). Is there something I am missing? Many thanks in advance!

Dynamic MVC Model Server side validation

I have the following view:
#model dynamic
<form class="form-horizontal" id="formDynamicItem" action="/DynamicItem/SaveItem" method="post">
#Html.AntiForgeryToken()
<div class="col-xs-12 buttonBar">
<button type="submit" value="Save" name="submitButton" class="btn">Save</button>
<button type="submit" value="Cancel" name="submitButton" class="btn">Cancel</button>
</div>
<div class="col-lg-6">
<div class="ibox ">
<div class="ibox-content">
#{
foreach (var obj in Model)
{
var kvpObj = (KeyValuePair<string, object>)obj;
var entityProp = (EntityAttributeProperties)kvpObj.Value;
<div class="form-group">
#if (entityProp.IsHiddenField)
{
<input type="hidden" class="form-control" data-val="true" id="#kvpObj.Key" name="#kvpObj.Key" value="#entityProp.Value" />
}
else if (entityProp.IsFormField)
{
var isReadOnly = entityProp.IsReadonly ? "readonly" : "";
IHtmlString validationRules = Html.Raw(string.Empty);
if (entityProp.ValidationRules != null)
{
validationRules = entityProp.ValidationRules;
}
#Html.Label(entityProp.Name, new { #class = labelClass })
<div class="#controlClass">
#switch (#entityProp.Type)
{
//... many cases
default:
<input type="text" class="form-control" id="#kvpObj.Key" name="#kvpObj.Key" value="#entityProp.Value" #isReadOnly #validationRules />
break;
}
</div>
}
</div>
}
}
</div>
</div>
</div>
</form>
#section Scripts {
<script>
$("#formDynamicItem").validate();
</script>
}
And in the controller I get my values using FormCollection:
public ActionResult SaveItem(FormCollection form)
{
...
newValue = typeConverter.ConvertFromString(form[entityAttribute.Name]);
...
}
}
My question is the following:
How can I establish Server-side validation on such dynamic model? Can I use FormCollection somehow? Possibly build dynamic view model somehow? If anyone has experience in this please consider giving a suggestion(answer).
Update: Making Detail page with ViewModel insted of dynamic model
So, After much refactoring I appear to be again stuck with server-side validation:
So now I have this ViewModel:
public class DynamicItemViewModel
{
public Guid Id { get; set; }
public List<EntityAttributeProperties> Properties { get; set; }
}
this detail page:
#model ExactDistillation.Models.DynamicItem.DynamicItemViewModel
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
#using (Html.BeginForm("SaveItem", "DynamicItem", FormMethod.Post, new { #class = "form-horizontal", #id = "formDynamicItem" }))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(x => x.Id)
<div class="col-xs-12 buttonBar">
<button type="submit" value="Save" name="submitButton" class="btn btn-primary pull-right">Save</button>
<button type="submit" value="Cancel" name="submitButton" class="btn btn-default pull-right cancel">Cancel</button>
</div>
<div class="col-lg-6">
<div class="ibox float-e-margins">
<div class="ibox-title text-webwonders">
<h5>Item</h5>
</div>
<div class="ibox-content">
#{
for (int i = 0; i < Model.Properties.Count; i++)
{
#Html.EditorFor(m => Model.Properties[i], "EntityAttributeProperties", "Properties[" + i + "]")
}
}
</div>
</div>
</div>
}
</div>
</div>
And this is how I define EntityAttributeProperties page:
#model EntityAttributeProperties
<div class="form-group">
#if (Model.IsHiddenField)
{
#Html.HiddenFor(x => x.Value)
}
else if (Model.IsFormField)
{
#Html.Label(Model.Name, new { #class = "col-sm-5 col-md-4 col-lg-3" })
<div class="col-sm-7 col-md-8 col-lg-9">
#switch (Model.Type)
{
--- Many cases
default:
#Html.DynamicTextBoxFor(m => m.Value, null, Model.IsReadonly, Model.ValidationRules)
break;
}
</div>
}
</div>
EntityAttributesProperties looks the following way:
public class EntityAttributeProperties
{
public string Name { get; set; }
public string DisplayName { get; set; }
public object Value { get; set; }
public EntityAttributeDataTypeEnum Type { get; set; }
public short Order { get; set; }
public bool IsFormField { get; set; }
public bool IsReadonly { get; set; }
public bool IsHiddenField { get; set; }
public Dictionary<string,object> ValidationRules { get; set; }
}
So, I am trying to make server-side validation for Model, but I am stuck, because I don't find any elegant solution to my problem, just solutions in which I have to do a lot of hardcoding (which I don't like).
Here is how I receive the form Submit:
public ActionResult SaveItem(DynamicItemViewModel model)
{
List<EntityAttributeExtendedView> entityAttributes = GetItemEntityAttributes();
DataObject dataObject = _dbContext.DataObjectCollection.FirstOrDefault(x => x.Id == model.Id);
if (dataObject != null)
{
JObject json = JObject.Parse(dataObject.Value);
dynamic dynJson = JsonConvert.DeserializeObject(dataObject.Value);
// Possibly loop through all entity attributes and separately make a custom validation ?
// Or somehow create custom Model State for validation ?
}
return View("Detail", model);
}
I will appreciate any suggestion on how to approach the problem with server side validation.
Thanks.
FormCollection is a very raw form of data and cannot be validated easily. I'd suggest you should refactor your view to be able to use ViewModels, else you will have a hard time working with the Data.
I cannot show you the full way but give you some hints:
Create a single View Model class for the View which contains a list of Items (of type EntityAttributeProperties). Let's call it MainViewModel.
Pass this View Model to the view rather then the Dictionary
In your view, use #Html.EditoFor(x => x.Items) to generate the correct HTML. ASP.NET MVC will use the Editor Templates for EntityAttributeProperties type
This is a good moment to create a new view EntityAttributeProperties.cshtml in your View folders EditorTemplates sub-folder
within this item view, you can do all your entityProp.Type switches, but be careful with ID generation, always use #Html.IdFor(...) etc. instead of generating own IDs to keep type safe with your View Model
After some tweaks, your Post Action should be able to receive your view model of Type MainViewModel. If everything went good, the Item's will be filled, even if you used different Controls (hidden fields, text fields, drop-downs...) to populate the Values
From my perspective, only this MVC-safe approach will lead to success in this case
I'm adding another Answer because the Question changed heavily.
A good approach is to use the IValidatableObject Interface. So you add this Interface to your EntityAttributeProperties class and have to override the Method Validate. for simple validation like required fields, you use so called validation Attributes.
Your EntityAttributeProperties class would be decorated like this:
public class EntityAttributeProperties : IValidatableObject
{
public string Name { get; set; }
[Required]
public object Value { get; set; }
...
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var results = new List<ValidationResult>();
if (... /* some condition, e.g. specific EntityAttributeDataTypeEnum */)
{
// Do some validation
// some other random test
if (.../* something not right... */)
{
results.Add(new ValidationResult("your input was not valid!"));
}
}
return results;
}
}
You might need to also make your DynamicItemViewModel and IValidatableObject and loop through the Items, but sometimes MVC is smart enough to validate sub-items automatically, so you might need this:
public class DynamicItemViewModel : IValidatableObject
{
...
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
return Items.SelectMany(x => x.Validate(validationContext));
}
}
OK now in your Controller, you basically need to check your ModelState. The automatically generated ModelState Property contains all errors.

How to pass the ID of an object from a form to an action in ASP.NET MVC

I have an ASP.NET MVC project with entities based on EF6 (model first). So my entities are all auto-generated for me. I have an entity, Site and I just want the user to select a Site before proceeding. I have tried a couple of ways, all of them work, but they seem very messy and unnecessary.
I was curious about the cleanest way to create a DropdownList of Sites, then get the selected site when the form is submitted (by Id or whatever other mechanism is better).
Currently I have:
The index where the user is asked to select a site:
public ActionResult Index()
{
ViewBag.Sites = new SelectList(db.Sites.ToList(), "Id", "Name");
return View();
}
The view:
#using (Html.BeginForm("SetSite", "Home"))
{
#Html.Label("sites", "Site:");
#Html.DropDownList("Sites", null, new { #class = "selectpicker" });
<div style="width:100%;height:25px"></div>
<button class="btn btn-default" style="width:100%">Go</button>
}
And the SetSite action, where the form is submitted
[HttpPost]
public ActionResult SetSite()
{
if (Request.Form["Sites"] != null)
{
Session["Site"] = db.Sites.Find(Request.Form["Sites"]);
return RedirectToAction("Home");
}
else
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
}
A few problems arise from this method. First, I really wanted to take advantage of the #model functionality in razor and point it towards my Site class, but since it's auto-generated, I don't want to go poking around and adding a whole bunch of view properties. (beggars can't be choosers?)
Second, the Request.Form['Sites'] returns a string, and converting it to and int is ugly as hell.
As I mentioned, I'd like to use the #model functionality with Html.DropDownListFor. Is that possible when working with a list of Sites from the DB?
How can I get this done cleanly?
Solution 1:-
Controller:-
private List<Country> GetCountries()
{
var list = new Entity.Result<List<Entity.Country>>();
list = _context.Countries.Select(tc => new Entity.Country
{
Id = tc.Id,
Name = tc.Name,
IsdCode = tc.Code,
}).ToList();
return list.Data.Select(x => new Country
{
id = x.Id,
Name = x.Name,
}).ToList();
}
HttpGet Method:-
public ActionResult Add(int id)
{
try
{
}
catch (Exception ex)
{
}
finally
{
ViewBag.countryList = GetCountries();
}
return View()
}
View Method:-
#Html.DropDownListFor(x => x.countryId, new SelectList(ViewBag.countryList, "id", "Name"), KahandasDhanji.Common.Localization.Application.Resources.ddlCountry,
new { #id = "ddlCountry", #rows = 1 })
In Http Post Form Submitimg u handle that model value in HTTPPOST Method.
Solution 2:-
FormCollection class we can capture the form's values within the controller.
[HttpPost]
public ActionResult Add(FormCollection form)
{
string strDDLValue = form["Sites"].ToString();
return View(MV);
}
Hope Its Work !!!
You can use a ViewModel to avoid converting the string value from Request.Form. Below is how your ViewModel class should look like
public class MyViewModel
{
public MyViewModel()
{
this.DropdownItems = new List<SelectListItem>();
}
public int SelectedSiteId { get; set; }
public List<SelectListItem> DropdownItems { get; set; }
}
Change the get action method in your controller as below
public ActionResult Index()
{
List<Site> sites = db.Sites.ToList();
MyViewModel model = new MyViewModel();
foreach(var site in sites)
{
model.DropdownItems.Add(new SelectListItem() { Text = site.Name, Value = site.ID.ToString() });
}
return View(model);
}
Add #model MyViewModel at the first line in your view code and use Html.DropDownListFor method to generate the dropdownlist
#model MyViewModel
#using (Html.BeginForm("SetSite", "Home"))
{
#Html.Label("SelectedSiteId", "Site:");
#Html.DropDownListFor(m => m.SelectedSiteId, Model.DropdownItems, new { #class = "selectpicker" })
<div style="width:100%;height:25px"></div>
<button class="btn btn-default" style="width:100%">Go</button>
}
The post action method in your controller should look like below. model.SelectedSiteId will be the selected value of the dropdownlist and the type is int so you won't have to do any conversion such as Convert.ToInt32(Request.Form['Sites']).
[HttpPost]
public ActionResult SetSite(MyViewModel model)
{
Session["Site"] = model.SelectedSiteId;
return RedirectToAction("Home");
}

In MVC, posting of form return empty model

I have the following viewModel
public class ExerciceViewModel
{
public string Code { get; set; }
public string Titre { get; set; }
public int QuestionCourante { get; set; }
}
the following view
#model MonEcoleVirtuelle.ViewModel.ExerciceViewModel
#{
ViewBag.Title = "Test";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Test</h2>
#using (Html.BeginForm("test", "exercice", FormMethod.Post))
{
#Model.Code <br />
#Model.Titre<br />
#Model.QuestionCourante<br />
<br />
<br />
Model.Code = "Code Modifie";
<input type="submit" value="Post Moi Ca!" name="fini" />
}
and the following controller methods
[HttpPost]
public ActionResult Test(ViewModel.ExerciceViewModel model)
{
if (ModelState.IsValid)
{
return Content(model.Code);
}
return View(model);
}
[HttpGet]
public ActionResult Test()
{
var vm = new ViewModel.ExerciceViewModel { Code = "code1", Titre = "Mon titre", QuestionCourante = 1 };
return View(vm);
}
When I submit the form, the model passed is empty, all properties are reset, not keeping the original values. What am I missing.
thanks
well, instead of #Model.Code which just display the values, you need some inputs.
So #Html.TextBoxFor(m => m.Code) for example
To manage a collection, you can do something like that :
#for (var i = 0; i < Model.Collection.Count; i++) {
Html.TextBoxFor(m => Model.Collection[i].Property)
}
You have not included any input fields in your view.
The #Model.Code etc only output the value of the field. To be able to post back elements they need to be form elements, like inputs. Use something like
#Html.TextBoxFor(p=>p.Code)
to create input fields that can then be posted back.
For a more complete guide see MSDN at http://msdn.microsoft.com/en-us/library/dd410596(v=vs.100).aspx

Categories

Resources