Duplicate of parameters in URL caused by checkbox - c#

We're having a problem with the following:
If our checkbox is checked, our url has two of the same parameters in it:
http://localhost:63978/Failure?ShowResolvedFailures=true&ShowResolvedFailures=false
We are almost sure that is has to do with the hidden parameter from the checkbox (See: asp.net mvc: why is Html.CheckBox generating an additional hidden input)
The first "ShowResolvedFailures" would be the value which is generated because of the fact that the checkbox is checked.
The second "ShowResolvedFailures" is from the hidden property we expect
But how do we remove that parameter from our url. Of course we do not want to see the hidden parameter in our URL.
Here is our code:
cshtml:
<form asp-controller="Failure" asp-action="Index" method="GET" role="form" id="searchForm" asp-antiforgery="false">
<div class="pull-right">
<span>
Toon opgeloste storingen?
<input asp-for="#Model.ShowResolvedFailures"
type="checkbox"
class="customCheckbox"
onclick="this.form.submit();">
</span>
</div>
controller.cs:
[HttpGet]
public async Task<IActionResult> Index([FromQuery] FailureIndexViewModel requestModel)
{
var showResolvedFailures = requestModel?.ShowResolvedFailures ?? false;
var searchQuery = new FailureSearchQuery
{
CorrelationId = requestModel?.CorrelationId,
CommandId = requestModel?.CommandId,
ShowResolvedFailures = showResolvedFailures
};
var urlWithQueryString = QueryHelpers.AddQueryString(#"api/tool/failures/search", searchQuery.GetQueryParameters());
var failureOverviewModel = await GetDataAsync<FailureOverviewModel>(urlWithQueryString);
return View("Index", new FailureIndexViewModel
{
CorrelationId = requestModel?.CorrelationId,
CommandId = requestModel?.CommandId,
ShowResolvedFailures = showResolvedFailures
});
}

My personal view (may be stupid one) is this is design issue. there are two fields created for check box, one is checkbox itself and other one is hidden input. Hidden input is always false for some reason (that I don't know) and value of check box depends on whether it is checked or not. If it is not checked query string will be false because check box will not send anything if it is unchecked instead value will be of hidden input. But when check box is checked then it will send true and hidden input will send false which is your case. I would use work around for that.
Replace
var showResolvedFailures = requestModel?.ShowResolvedFailures ?? false;
with
var showResolvedFailures = Request.QueryString["ShowResolvedFailures"].Contains("True") ? true : false;

Use your own input checkbox construction instead the asp-for taghelper, This way you won't use the default template for boolean
<input name="#Html.NameFor(m => mShowResolvedFailures)"
type="checkbox"
class="customCheckbox"
onclick="this.form.submit();">

Related

.Net Core - Passing Input Controller value into ViewComponent/Controller Action

I am pretty sure if this was in a form I could simply just put
var name = this.Request.Form["txtName"];
To get the value in the action. But since its not a form I am just trying to get this value of a hidden input in my controller so I can use it in the method.
I have tried passing in the name of the input in the action parameters using a session/tempdata but that does not work because in a new tab it then uses this same value so I have to have a set value saved on the page and use that value in the controller.
I thought I could do a querystring but also I have not had luck passing this into the action.
Here is what I have.
Html
<input id="txthguid" name="txthguid" type="hidden" value="#TempData["guid"]" />
<input asp-for="TabGuid" type="hidden" value="#TempData["guid"]" />
#await Component.InvokeAsync("DataPage", Model)
View Compnent
public async Task<IViewComponentResult> InvokeAsync(string sessionID, object txthguid, LNPartVM model)
{
if (TempData != null)
{
TempData.Keep();
}
List<LNPartVM> lstData = null;
try
{
_logger.LogInformation(txthguid.ToString());
_logger.LogInformation(TempData["guid"].ToString());
var jsonResponse = "";
//string sessionID = _contextAccessor.HttpContext.Session.Id;
//string sessionID = _contextAccessor.HttpContext.Session.GetString("sGUID");
var tabGuid = Request.Query["txthguid"];
_logger.LogInformation(tabGuid);

How to pass Text instead of Value from select list to controller in Asp .Net Core

I have two <slect> list and I want to pass only Text to the controller. I don't know if its possible.
This is Html :
<form asp-action="Crea" method="post">
<select asp-for="Azienda_cliente" class="select2 form-control" id="ddlClienti" onchange="LoadSottoCliente(value)"></select>
<select asp-for="Azienda_sotto_clienti" class="select2 btn-block" id="ddlSottoClienti"></select>
<input type="submit" value="Crea" class="w-50 btn btn-success" />
</form>
And this is controller:
[HttpPost]
public async Task<IActionResult> Crea(Attiivita attivita)
{
if (ModelState.IsValid)
{
_db.Add(attivita);
await _db.SaveChangesAsync();
return RedirectToAction("Index");
}
return View();
}
Is it possible to post only Text from the select list?
Any suggestions how to do that?
Thanks in advance!
I have two <select> list and I want to pass only Text to the controller.
In your form, when you submit the form, the Value of the selected option would be posted to controller action method.
If you'd like to post Text of the selected option to controller action, you can try following two approaches:
Approach 1: populate your dropdown(s) with expected data as Value property.
<select asp-for="Azienda_cliente" asp-items="ViewBag.ClientItems" class="select2 form-control" id="ddlClienti" onchange="LoadSottoCliente(value)"></select>
In action method
ViewBag.ClientItems = new List<SelectListItem>
{
//normally we populate Value property with Id etc field
//in your code logic, you can populate it with your expected data (same as you set for Text property)
new SelectListItem { Value = "ClientName", Text = "ClientName" },
//...
//other items
};
For the second dynamic dropdown, you can set Value property with same data as Text of each <option>.
$("select#ddlSottoClienti").append(`<option value="${text_here}">${text_here}</option>`);
Approach 2: you can get text of the selected option(s), then submit data using jQuery Ajax etc on JavaScript client side.
$("input[type='submit']").click(function (ent) {
ent.preventDefault();
var Azienda_cliente = $("select#ddlCLienti option:selected").text();
var Azienda_sotto_clienti = $("select#ddlSottoClienti option:selected").text();
//make ajax request to your controller action
//...
})

Required Checkbox validation works in reverse for Html.CheckBoxFor [duplicate]

This question already has answers here:
MVC Model require true
(17 answers)
Closed 5 years ago.
I have a checkbox validation before i submit the page. But it works in reverse. It's displaying error message when i check the box instead of opposite. I don't know where im doing wrong.
My ViewModel
[Display(Name = "Terms and Conditions")]
[Range(typeof(bool), "true", "true", ErrorMessage = "Please accept Terms & Conditions")]
public bool IsTermsAccepted { get; set; }
My View
<div class="row col-lg-offset-2 top-buffer">
#Html.CheckBoxFor(model => model.IsTermsAccepted)
#Html.LabelFor(model => model.IsTermsAccepted)
<br>#Html.ValidationMessageFor(model => model.IsTermsAccepted)
</div>
Thank you for your time!
Edit1: I followed exactly like here
Edit2: I was able to resolve this by adding a simple script (as shown in the above mentioned link)
<script>
// extend range validator method to treat checkboxes differently
var defaultRangeValidator = $.validator.methods.range;
$.validator.methods.range = function(value, element, param) {
if(element.type === 'checkbox') {
// if it's a checkbox return true if it is checked
return element.checked;
} else {
// otherwise run the default validation function
return defaultRangeValidator.call(this, value, element, param);
}
}
You need to understand how CheckBoxFor works in ASP.NET MVC - and how checkboxes work in HTML (see my answer to this question: https://stackoverflow.com/a/11424091/159145 )
HTML checkboxes do not post their value in the POST request body if they are not checked.
So in order to tell the difference between "checkbox-not-checked" and "checkbox-excluded" you need to include an explicit "false" value as <input type="hidden" /> with the same name="" property value as the checkbox input's.
ASP.NET MVC does this for you: Html.CheckBoxFor() renders both an <input type="checkbox" value="true" /> and an <input type="hidden" value="false" />
If you look at the rendered HTML of your page, you'll see them both.
so when you submit a checked checkbox, your browser is actually sending two values: true and false. It's the "false" value that causes your Range validator to fail, even though it also sent the true value.
However, the RangeValidatorAttribute is not smart enough to handle this specific case for boolean ViewModel properties set using CheckBoxFor.
As far as I know, there is no built-in DataAnnotation attribute that handles this case - you will need to implement it yourself in your Controller Action, like so:
[HttpPost]
public IHttpActionResult Foo(FooViewModel model) {
if( !model.IsTermsAccepted ) {
this.ModelState.AddModelError( nameof(model.IsTermsAccepted), "you must accept the terms." );
return this.View( model );
}
}
...or you could try to implement a new validation attribute yourself, it would need to do something like this.
Note it derives from Required attribute, because you need to ensure both that the property has a value in the request body (i.e. that the checkbox was included in the response, with the hidden input) and that the true value is present.
[AttributeUsageAttribute(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
class CheckboxIsCheckedAttribute : RequiredAttribute {
public override bool IsValid(Object value) {
Boolean isRequiredValid = base.IsValid( value );
if( !isRequiredValid ) return false;
return (value as Boolean) == true;
}
}

Asp.NET MVC Value of DynamicForm posted but not passed to controller

So what I have is a HTML-Form enabling the user to register for sportsevents. The user can register different profiles (e.g. his children) and every event can potentially have so called "Additional Attributes" like textboxes for T-Shirt-size etc.
#model Models.EventRegistrationModel
#{
Layout = null;
var playerCount = Model.PlayersToRegister.Count;
}
#using (Html.BeginForm("RegisterForEvent", "Event", FormMethod.Post)){
#Html.AntiForgeryToken()
<div class="form-group">
#for (int i = 0; i < playerCount; i++)
{
<div>
<p>#Model.PlayersToRegister[i].User.FullName</p>
</div>
<div
#Html.CheckBoxFor(model => Model.PlayersToRegister[i].PlayerShallGetRegistered)
</div>
//this is the "Additional Attributes"-section for each user-profile
#Html.Raw(Model.PlayersToRegister[i].Form.RenderHtml())
}
</div>
<input type="submit" value="Confirm Registration"/>
}
Since I do not create those events, I cannot know, what these "Additional Attributes" look like, which is why they are rendered dynamically using DynamicForm.
My problem is that I cannot access the user-input for those attributes in the controller. When I check the browser's console, I see the input being posted, but checking the dynamic form's value, it always says "null".
Here's my controller:
[HttpPost, ValidateAntiForgeryToken]
public ActionResult RegisterForEvent(EventRegistrationModel model)
{
for (int i = 0; i < playerList.Count; i++)
{
var form = Session["Form" + i] as Form;
model.PlayersToRegister[i].Form = form;
//var test = form
//var testtest = test.GetResponses(false);
}
return RedirectToAction("EventOverview");
}
As you can see, I tried to use the Form's "GetResponses"-Method, but it returned null.
public List<Response> GetResponses(bool completedOnly)
{
return InputFields.OrderBy(inputField => inputField.DisplayOrder).Select(field => new Response
{
Title = field.Title, Value = field.Response
}).Where(response => !completedOnly || !String.IsNullOrEmpty(response.Value)).ToList();
}
At the moment I am trying to get the values via Session, as this worked in an older version, where you were only able to register one profile at a time. The Session-variable gets assigned in the ActionResult returning the above View.
I've been trying various solutions from various threads over the past days (e.g. ModelState.Clear), but unfortunately nothing has been successful.
If you need more information, code or whatever, please let me know.
Since your form is dynamic you may want to use a dynamic model in the post method. Try something like this:
[HttpPost, ValidateAntiForgeryToken]
public ActionResult RegisterForEvent(FormCollection collection)
{
// stuff....
}
You'll have to do a bit of work parsing out the collection that comes in but it should have everything that was posted from the form. In general I don't recommend this as it can really spiral out of control. Having a well defined view model (as you did in the original posting) is much better in general. However, sometimes you really need something dynamic and this gets the job done.

Hook javascript to dropdownlist change

¡Hola!
My current task is to have a page where, with a dropdownlist, a user can select a deck title. Once a title is selected, the page should postback with details on that deck.
Here's what I've got at the moment:
#model IEnumerable<SCATChartsMVC.Models.Charts_DeckList>
#{
ViewBag.Title = "Index";
if (IsPost) { ViewBag.Title = "We posted back!"; }
}
<h2>Index</h2>
#{ var list = ViewData.Model.Select(cl => new SelectListItem
{
Value = cl.RecNum.ToString(),
Text = cl.DeckTitle.ToString()
});
}
#using (Html.BeginForm("Details", "Charts_DeckList", FormMethod.Post))
{
#Html.DropDownList("deckTitles", list, "---------select---------")
<input type="submit" name="submit" value="Submit" />
#Html.ActionLink("Details", "Details", "Charts_DeckList", new { id = list.ElementAt(4).Text }, "")
}
<script src="~/Scripts/jquery-1.10.2.js"></script>
<script>
$("deckTitles").change(function () {
if ($("#deckTitles").val() != "") {
var test = {};
test.url = "/Charts_DeckList/Details";
test.type = "POST";
test.data = JSON.stringify($('#deckTitles').val());
test.datatype = "json";
test.contentType = "application/json";
test.success = true;
test.error = function () { alert("Error!"); };
$.ajax(test);
}
})
</script>
The input tag and ActionLink under Html.BeginForm were for my own testing purposes; the ActionLink works correctly if I specify the element. I'm hoping to be able to pass something similar back whenever a user clicks a selection, as opposed to whenever they hit the "details" button.
The submit input tag does not work. It does route properly to Charts_DeckList/Details, but the parameter in the action is always null.
I'm just getting into the whole MVC/Web rigamarole, so there's a lot I don't know that I'm not even aware I don't know. While I've seen a number of different resources on the internet suggesting different things, much of the web development jargon is lost on me at this point in time, and much of the way these things work under the hood is lost on me since VS seems to put together so much of it automagically.
Any pointers would be appreciated. Thank you.
barrick's suggestion below is correct!
I also had to move the script tags up into the BeginForm brackets, heads up.
You're not setting the ID of the DropDownList there, the first argument sets the name attribute of the dropdown (used to identify the value in the POST variable collection on server postback) - you'll need to add another argument to set the ID:
#Html.DropDownList("deckTitles", list, "---------select---------", new { #id = "deckTitles" });
You can then pick up the selected value in the jQuery as follows:
$("#deckTitles option:selected").val();

Categories

Resources