Get Selected Value of html dropdown in razor page dotnet - c#

With the below code how can I get the value of the dropdown list using page handlers or tag helpers?
One way I've been reading is using OnChange on the dropdown, I can set the value of a hidden field with javascript then get value of hidden field from code behind. Should I even be using page handlers/tag helpers to get the dropdown value?
I have tried using Request.Form["networks"] but this just gives the ID and not the Value.
<form method="post">
#Html.DropDownList("networks",
Model.GetNetworks().Select(s => new SelectListItem()
{
Text = s.networks,
Value = s.id.ToString(),
}),
new
{
#class = "dropdown form-control",
})
<input type="hidden" id="selectedwifivalue" />
<br />
<input type="text" placeholder="enter wifi password" asp-for="Password" class="input-sm form-control" />
<br />
<button asp-page-handler="submit" class="btn btn-primary btn-sm form-control">Submit</button>
</form>
Model is
public class WifiNetworks
{
public int id { get; set; }
public string networks { get; set; }
public IEnumerable<SelectListItem> UserNetworks { get; set; }
}
Code behind in cshtml file
[BindProperty]
public string Password { get; set; }
public string networks { get; set; }
public void OnPostSubmit()
{
{
var password = Request.Form["password"];
var network = Request.Form["networks"]; <--this just gives the ID and not the value
var wifi = networks; <---this is blank
}
}

To access the form elements value, You can use Request.Form collection by passing the name of the form element.Set name property of html element and check.
Below is sample code for get form element value using name property.
View :
#using (Html.BeginForm("ReceiveValueWithRequestFormData", "ControllerAndAction"))
{
<ol>
<li>
#Html.Label("Username")
#Html.TextBox("txtUserName") : user
</li>
<li>
#Html.Label("Password")
#Html.TextBox("txtPassword") : pass
</li>
</ol>
<input type="submit" value="Login" />
}
<div id="divResult"></div>
Controller :
[HttpPost]
public ActionResult ReceiveValueWithRequestFormData()
{
string userName = Request.Form["txtUserName"];
string password = Request.Form["txtPassword"];
if (userName.Equals("user", StringComparison.CurrentCultureIgnoreCase)
&& password.Equals("pass", StringComparison.CurrentCultureIgnoreCase))
{
return Content("Login successful !");
}
else
{
return Content("Login failed !");
}
}

In the end I could have done it using ajax but using the hidden field approach it works fine like below no idea if this is a good/bad/better way to do this (I seemed to be asking those questions quite alot)
get value of dropdown using jquery "change" function (keep this outside of the
$(document).ready function as it wont fire)
$('#dnetworks').change(function () {
var selectedwifi = $('#networks option:selected').text();
$('#hiddenselectedwifivalue').val(selectedwifi); //set value of hidden field
});
Razor page
<input type="hidden" id="hiddenselectedwifivalue" asp-for="WifiNetworks.networks" />
.cshtml file
var selectedwififromdd = WifiNetworks.networks;
I used the bind for the whole class in the model page (.cshtml file)
public class WifiModel : PageModel
{
[BindProperty] public WifiNetworks WifiNetworks { get; set; }

Related

Asp.Net-Core with Razor Pages Bind hidden input

In my java script file I am setting the value of a hidden input like this:
document.getElementById("myVal").value = 11;
In my html:
<input asp-for="Value" id="myVal" />
In my Asp.Net-Core Web Application with Razor Pages I am trying to bind this property so I can take the value :
public class IndexModel : PageModel
{
[HiddenInput] //I also tried to add [BindProperty] after
public int Value { get; set; }
public void OnGet()
{
Debug.WriteLine(Value);
}
}
EDIT
function redirect() {
document.getElementById("myVal").value = 11;
}
This function is called when I click a button, and that is when the value is being set to 11.
What I want is that when this button is clicked, I can save the value myVal so that I can use it in my Razor Page.
The problem is that the value of the field will always be 0 (even when the field is not hidden). How can I set the value for the hidden field?
This work for me
i define post method
[BindProperty]
[HiddenInput]
//I also tried to add [BindProperty] after
public int Value { get; set; }
public void OnGet()
{
}
public void OnPost()
{
int a = Value;
}
*in page
<form method="post" asp-action="Index">
<input asp-for="Value" id="myVal" />
<input type="submit" value="send" />
</form>
<script>
document.getElementById("myVal").value = 11;
</script>

ASP Core Razor Syntax #for -> multiple submitbuttons with diffrent inputs?

i am trying to create a ASP Core Application with Razorpages.
Everything is working fine so far, but now i'm stuck with this problem:
I want to create a form with multiple Buttons based on a Objectarray. Each Button has its own Name and should call the same OnPost method. So far so good. I struggle with posting the correct Object with the form.
Here is some Code...
Test.cshtml :
#page
#using APP.Models
#model TestModel
#{
ViewData["Title"] = "Test";
Layout = "~/Pages/Shared/_Layout.cshtml";
var bereiche = Bereich.getBereiche();
}
<div class="grid">
<h1 class="center-text">#ViewData["Title"]</h1>
<form method="post" class="center">
<div class="form-group">
<label asp-for="Model" class="col-form-label"></label>
<div class="button-group-vertical">
#for (var i = 0; i < bereiche.Count; i++)
{
<input type="hidden" asp-for="Model" value="#bereiche[i]" />
<button class="btn btn-info btn-md button-group-vertical-item">#bereiche[i].name</button>
}
</div>
</div>
</form>
</div>
Bereich.cs in namespace Model
public enum Art
{
Mitte, Beladen, Inspection, Kette
}
public class Bereich
{
public string name { get; set; }
public Art art { get; set; }
public static List<Bereich> getBereiche()
{
var bereiche = new List<Bereich>
{
new Bereich() { name="Mitte", art = Art.Mitte},
new Bereich(){name= "Beladen",art = Art.Beladen },
new Bereich(){name = "Qualitätskontrolle",art = Art.Inspection},
new Bereich(){name ="Kette",art= Art.Kette}
};
return bereiche;
}
}
Test.cshtml.cs :
public class TestModel : PageModel
{
[DisplayName("Bereich")]
[BindProperty]
public Bereich Model { get; set; }
public void OnGet()
{
}
public IActionResult OnPost()
{
return Page(); // BREAKPOINT HERE -> MODEL always "art=Mitte", "name=null" :/
}
}
Does anyone have a clue what i am doin wrong ?
In order to post an object back, you could use one form per object, with named fields corrosponding to your object property names.
Try this instead:
#for (var i = 0; i < bereiche.Count; i++)
{
<form method="post" class="center">
<input name="art" type="hidden" value="#bereiche[i].art" />
<button name="name" value="#bereiche[i].name" class="btn btn-info">#bereiche[i].name</button>
</form>
}
And also if you want your button to hold a value, you need to put the value in a value tag like i did in the example.

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.

File Upload Causes Model Validation to Fail

I have an MVC3 form bound to a model with a file upload control. (Extra HTML removed for brevity):
#model Models.MessageModel
<script type="text/javascript">
var numAttachments = 0;
$(function () {
$(".add-attachment").click(function () {
$(".attachments").append("<div><input type=\"file\" name=\"attachments\" id=\"attachment" + numAttachments + "\" /></div>");
});
});
</script>
#using (Html.BeginForm())
{
#Html.ValidationSummary()
<div class="field-label">Subject:
#Html.EditorFor(model => model.Subject)
</div>
<div class="attachments">
</div>
<div>
Add Attachment
</div>
<div class="message-text">#Html.TextAreaFor(model => model.Text, new { cols = 107, rows = 10 })</div>
<input type="submit" value="Send Message" />
</div>
}
Users can choose to add multiple attachments by clicking the "add attachment" link, attachments are not required.
My model is as follows:
public class MessageModel
{
[Required]
public string Subject { get; set; }
[Required]
public string Text { get; set; }
public IEnumerable<HttpPostedFileBase> Attachments { get; set; }
}
(NOTE: I've also tried moving the attachments out of the model, into an argument to my action method with the same results)
My Action:
[HttpPost]
public ActionResult New(MessageModel message)
{
// this check passes if no file is uploaded
// but once a file is uploaded, this evaluates to false
// even if the model is valid
if (ModelState.IsValid)
{
// do stuff
}
}
This form works fine and validation passes when no file is selected for upload. When I choose a file for upload, ModelState.IsValid becomes false. How can I cause validation to ignore uploaded files?
You need to make sure your form is using the correct "enctype".
#using (Html.BeginForm("New", "Controller", FormMethod.Post, new { enctype = "multipart/form-data" }))
MVC 3 file upload and model binding

Is there any way to bind a checkbox list to a model in asp.net mvc

I am looking here to find a quick and easy way to bind a list of checkbox list items when the postback occurs in the model.
Apparently the common way to do it now seems to do it like this form.GetValues("checkboxList")[0].Contains("true"); It seems painfull and not exactly safe.
Is there a way to bind a list of checkbox (that are created with or without an helper in the view) or even an array of data for that matters during the UpdateModel(myViewModel, form.ToValueProvider()); phase which would populate an IList<string> or string[] inside of the model ?
You could start with a model:
public class MyViewModel
{
public int Id { get; set; }
public bool IsChecked { get; set; }
}
then a controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new[]
{
new MyViewModel { Id = 1, IsChecked = false },
new MyViewModel { Id = 2, IsChecked = true },
new MyViewModel { Id = 3, IsChecked = false },
};
return View(model);
}
[HttpPost]
public ActionResult Index(IEnumerable<MyViewModel> model)
{
// TODO: Handle the user selection here
...
}
}
a View (~/Views/Home/Index.aspx):
<% using (Html.BeginForm()) { %>
<%=Html.EditorForModel() %>
<input type="submit" value="OK" />
<% } %>
And finally a corresponding editor template:
<%# Control
Language="C#"
Inherits="System.Web.Mvc.ViewUserControl<AppName.Models.MyViewModel>" %>
<%= Html.HiddenFor(x => x.Id) %>
<%= Html.CheckBoxFor(x => x.IsChecked) %>
Now when you submit the form in the POST action you will get the list of selected values along with their id.
Here's the quick and easy way. Just set the value attribute inside your checkbox input elements and give them all the same name. If I was implementing this in a site, I would create a CheckBox helper method that takes name, value and isChecked parameters, but here's the View with just the html needed:
<% using (Html.BeginForm()) { %>
<p><input type="checkbox" name="checkboxList" value="Value A" /> Value A</p>
<p><input type="checkbox" name="checkboxList" value="Value B" /> Value B</p>
<p><input type="checkbox" name="checkboxList" value="Value C" /> Value C</p>
<p><input type="checkbox" name="checkboxList" value="Value D" /> Value D</p>
<p><input type="checkbox" name="checkboxList" value="Value E" /> Value E</p>
<p><input type="checkbox" name="checkboxList" value="Value F" /> Value F</p>
<input type="submit" value="OK" />
<% } %>
In your Controller:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(IEnumerable<string> checkboxList)
{
if (checkboxList != null)
{
ViewData["Message"] = "You selected " + checkboxList.Aggregate("", (a, b) => a + " " + b);
}
else
{
ViewData["Message"] = "You didn't select anything.";
}
return View();
}
The IEnumerable<string> parameter (you could make it IList<string> if you want) will contain only the values of the checked items. It will be null if none of the boxes are checked.
This works pretty well using a custom model binder and regular HTML...
First, your HTML form in Razor syntax:
#using (Html.BeginForm("Action", "Controller", FormMethod.Post)) {
<ol>
<li><input type="textbox" name="tBox" value="example of another form element" /></li>
<li><input type="checkbox" name="cBox" value="1" /> One</li>
<li><input type="checkbox" name="cBox" value="2" /> Two</li>
<li><input type="checkbox" name="cBox" value="3" /> Three</li>
<li><input type="checkbox" name="cBox" value="4" /> Four</li>
<li><input type="checkbox" name="cBox" value="5" /> Five</li>
<li><input type="submit" /></li>
</ol>
}
(FormMethod.Post could also be .Get, doesn't matter for this)
Then, in proper MVC sense have a model object which represents your form submission:
public class CheckboxListExampleModel {
public string TextboxValue { get; set; }
public List<int> CheckboxValues { get; set; }
}
And a custom model binder class (I like to put this inside the model being bound, so I'll repeat the model created above to show where I'd add it. Placing it inside also allows the binder to use private property setters, which is a Good Thing):
public class CheckboxListExampleModel {
public string TextboxValue { get; private set; }
public List<int> CheckboxValues { get; private set; }
public class Binder : DefaultModelBinder {
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
var model = new CheckboxListExampleModel();
model.TextboxValue = bindingContext.GetValueAsString("tBox");
string checkboxCsv = bindingContext.GetValueAsString("cBox");
// checkboxCsv will be a comma-separated list of the 'value' attributes
// of all the checkboxes with name "cBox" which were checked
model.CheckboxValues = checkboxCsv.SplitCsv<int>();
return model;
}
}
}
.GetValueAsString() is an extension method used for clarity, here it is:
public static string GetValueAsString(this ModelBindingContext context, string formValueName, bool treatWhitespaceAsNull = true) {
var providerResult = context.ValueProvider.GetValue(formValueName);
if (providerResult.IsNotNull() && !providerResult.AttemptedValue.IsNull()) {
if (treatWhitespaceAsNull && providerResult.AttemptedValue.IsNullOrWhiteSpace()) {
return null;
} else {
return providerResult.AttemptedValue.Trim();
}
}
return null;
}
.SplitCsv<T>() is also an extension method, but it's a common enough need and messy enough code that I'll leave it as an exercise for the reader.
And finally, your action to handle the form submit:
[HttpPost]
public ActionResult Action([ModelBinder(typeof(CheckboxListExampleModel.Binder))] CheckboxListExampleModel model) {
// stuff
}

Categories

Resources