Get all hidden input fields in JSF dynamically - jsf-2

I have some hidden inputs that are generated dynamically using JQuery. For example :
<input type="hidden" name="name1" value="SomeValue1">
<input type="hidden" name="name2" value="SomeValue2">
The method
FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("name1")
returns the value SomeValue1 which is correct. But, at runtime I am not aware of the input names. How can I get all the hidden inputs without knowing their names ?
Thansk for help.

Give them all the same name, so that you can use getRequestParameterValuesMap() instead.
<input type="hidden" name="name" value="SomeValue1">
<input type="hidden" name="name" value="SomeValue2">
...
String[] names = externalContext.getRequestParameterValuesMap().get("name");
The ordering is guaranteed to be the same as in HTML DOM.
Alternatively, based on the incremental integer suffix as you've in the HTML DOM, you could also just get the request parameter in a loop until null is returned.
List<String> names = new ArrayList<>();
for (int i = 1; i < Integer.MAX_VALUE; i++) {
String name = requestParameterMap.get("name" + i);
if (name != null) {
names.add(name);
} else {
break;
}
}

Related

Passing route values to Html.BeginForm

I want the form to pass the values from the hidden inputs to the server and I also expected it to build the URL as
"localhost:9392/Ranking/Index/2?rankingType=SOMEVALUE&ageGroup=SOMEVALUE&week=SOMEVALUE"
but it shows like "localhost:9392/Ranking/Index/2?rankingType=rankingTypeID&ageGroup=ageGroupID&week=week"
#using (Html.BeginForm("Index", "Ranking", new { id = Model.CurrentRanking, rankingType = "rankingTypeID", ageGroup = "ageGroupID", week = "week"}, FormMethod.Post, new { id = "ageGroupForm" }))
{
<input id="ageGroupID" name="ageGroup" hidden />
<input id="rankingTypeID" name="rankingType" hidden />
<input id="week" name="week" hidden />
}
Why is that ? How do I pass the values and also have them show up as query string ?
Try this code:
#using (Html.BeginForm("Index", "Ranking"))
{
<input id="ageGroupID" name="ageGroup" hidden />
<input id="rankingTypeID" name="rankingType" hidden />
<input id="week" name="week" hidden />
}
and in your action method, decorate it like:
public ActionResult Index(string ageGroupID, string rankingTypeID, string week){}
but it shows like "localhost:9392/Ranking/Index/2?rankingType=rankingTypeID&ageGroup=ageGroupID&week=week"
That's because those are exactly the string values you're using:
new { id = Model.CurrentRanking, rankingType = "rankingTypeID", ageGroup = "ageGroupID", week = "week"}
It's not really clear why you're trying to set query string values for the exact form inputs you already have:
<input id="ageGroupID" name="ageGroup" hidden />
<input id="rankingTypeID" name="rankingType" hidden />
<input id="week" name="week" hidden />
If what you're trying to accomplish is to have those values be on the query string (as a GET request) instead of in the request body (as a POST request) then all you have to do is change your form method to a GET:
#using (Html.BeginForm("Index", "Ranking", new { id = Model.CurrentRanking }, FormMethod.Get, new { id = "ageGroupForm" }))
HTML form inputs are automatically added to the request, that's how forms work. You don't need to try to do it manually.
Though it's strange that you're doing this at all. Your inputs are hidden, which usually means you don't want to concern the user with them. But you're also showing them on the URL, which may be confusing to the user (or may even be suspicious to the user). You also aren't setting any values to those hidden inputs, unless you have something else not included here which does that?
Either way, your form will automatically include its inputs in the submitted request.

ASP.NET MVC - why ViewBag prop return "value" string instead of actual value?

I need to pass some flag into a view and get it back.
But requirements are:
this flag is not part of model
and it must not be stored in session or such.
I mean - flag visibility scope should be - current page for current user only.
I tried to put it into a ViewBag like this:
public ActionResult Product_Add()
{
ViewBag.IsNew = true;
ViewData["IsNew"] = ViewBag.IsNew;
ViewBag.RecordActionName = "Add";
return View("Product_Edit");
}
And then use this code in *.cshtml:
<input type="hidden" name="IsNew1" value="#ViewBag.IsNew" />
<input type="hidden" name="IsNew2" value='#ViewData["IsNew"]' />
But in resulting HTML I see this:
<input type="hidden" name="IsNew1" value="value" />
<input type="hidden" name="IsNew2" value='value' />
Then I tried this code in *.cshtml:
#{ string isNewRec = ViewBag.IsNew.ToString(); }
<input type="hidden" name="IsNew1" value="#isNewRec" />
And now it works:
<input type="hidden" name="IsNew1" value="True" />
Question is - why first attempt put "value" text instead of boolean value into HTML?
Also the strange thing - this code works fine:
<input type="hidden" name="ProductID" value="#ViewBag.ProductID" />
So, for integer values in ViewBug it works, returns an actual interger value, but for boolean it returns "value" text.
Another part of question.
Currently I use code like this:
[HttpPost]
public ActionResult Product_Commit(Product pProduct)
{
if (ModelState.IsValid)
{
AbcProducts ctx = new AbcProducts();
bool isNew = Convert.ToBoolean(Request.Form["IsNew"]);
if (isNew)
ctx.Products.Add(pProduct);
else
{
// [...]
}
ctx.SaveChanges();
}
return View();
}
Could you recommend something more correct from MVC point of view to pass non-model parameter into a view and get it back then with submited data?
But this parameter should not be stored anywhere where other pages can see it somehow.

Passing an array of values to Action using Ajax.ActionLink

I would like to provide my Action with an array of integer values (based on selected checkbox values on a form). I am trying to use Ajax.ActionLink as follows...
<%= Ajax.ActionLink("Submit", "PrintPinLetters", "EPOC", new { selectedItemsToPrint }, new AjaxOptions { HttpMethod="POST", UpdateTargetId = "PrintConfirmation", LoadingElementId = "resultLoadingDiv", OnFailure="handleError"}, new { id = "btnPrintPinLetter" }) %>
but am not sure what to pass into the routeValue section. My Action in the controller is defined as...
[HttpPost]
public ActionResult PrintPinLetters(Int64[] selectedItemsToPrint)
{
Basically I am looking to pass an array (or comma separated list of ID values) in the 'selectedItemsToPrint'. This list would be build using the values defined by checkboxes (all named the same) in multiple rows of a table.
I have used Ajax.BeginForm but as this results in a nested form I was having unpredictable results when using older browsers (IE 7 and 8).
normally i would do the ajax myself with a jQuery call. You would set it up like this
instead of using Ajax.ActionLink(), use Html.ActionLink(.... {id = "myid"}). Dont forget to give your link an id
create an onready function
$(document).ready(function () {
$('#myid').click(function() {
var allElements = $('#container').find('input').serialize();
$.post(action, allElements, function (data) {
// add your code here to process the data returned from the post.
});
return false; // dont post the form
});
});
dont forget to set the name attribute of all of the input elements to selectedItemsToPrint so the binding will work correctly with your actionresult
The Html should look something like this
<div id="container">
<input type="checkbox" name="selectedItemsToPrint" value="somevalue0" />
<input type="checkbox" name="selectedItemsToPrint" value="somevalue1" />
<input type="checkbox" name="selectedItemsToPrint" value="somevalue2" />
<input type="checkbox" name="selectedItemsToPrint" value="somevalue3" />
<input type="checkbox" name="selectedItemsToPrint" value="somevalue4" />
<input type="checkbox" name="selectedItemsToPrint" value="somevalue5" />
</div>

unobtrusive MVC3 validating group of checkboxes

I need to validate a group of checkboxes using MVC3 unobtrusive validation. how would i do it? I found this and tried it, but it doesn't work.
$(function(){
$.validator.addMethod('cb_selectone', function(value,element){
if(element.length>0){
for(var i=0;i<element.length;i++){
if($(element[i]).val('checked')) return true;
}
return false;
}
return false;
}, 'Please select at least one option');
$('#main').validate({rules:{Services:"cb_selectone"}});
...
My Html:
<input type="checkbox" class="checkbox" name="Services" value="1" />
<input type="checkbox" class="checkbox" name="Services" value="2" />
<input type="checkbox" class="checkbox" name="Services" value="3" />
It would be best if someone provided a complete solution with server side + client side validation (of course using MVC3 unobtrusive validation).
Thanks
Ok, figured it out:
for server validation:
using data annotations (required will do it)
like so in my view model:
[Required(ErrorMessageResourceName = "fld_Service_val_Required_lbl", ErrorMessageResourceType = typeof(Resources.Service.Controllers.Firm))]
public ICollection<int> Services { get; set; }
for client validation in my html i added a class to my input checkboxes:
<input type="checkbox" class="checkbox required-checkbox" name="Services" value="1" />
<input type="checkbox" class="checkbox required-checkbox" name="Services" value="2" />
<input type="checkbox" class="checkbox required-checkbox" name="Services" value="3" />
and also:
$(function(){
$.validator.addMethod('required_group', function(value, element) {
var $module = $(element).parents('form');
return $module.find('input.checkbox:checked').length;
}, 'Select at least one Service please');
$.validator.addClassRules('required-checkbox', { 'required_group' : true });
..
not sure if this is the best solution but it works :). if someone knows a better please post.
This works - validates appropriately on submit, and hides/displays the validation message if a checkbox is subsequently checked or unchecked with minimal overhead (only gets hit once per validation cycle).
JavaScript:
(function ($) {
//functions from unobtrusive:
function setValidationValues(options, ruleName, value) {
options.rules[ruleName] = value;
if (options.message) {
options.messages[ruleName] = options.message;
}
}
var formEls;
function getUniqueFormId(form) {
if (typeof(formEls==='undefined')) {
formEls = document.getElementsByTagName('form');
}
return 'form' + Array.prototype.indexOf.call(formEls, form);
}
//from jQuery validation
function findByName(name, form) {
// select by name and filter by form for performance over form.find("[name=...]")
return $(document.getElementsByName(name)).map(function (index, element) {
return element.form == form && element.name == name && element || null;
});
}
//-------------------------
$.validator.addMethod('requiredgroup', function (value, element, params) {
for (var i = 0; i < params.length; i++) {
if (params[i].checked) { return true; }
}
return false;
});
valGroups = [];
$.validator.unobtrusive.adapters.add('requiredgroup', function (options) {
var groupName = options.element.name,
uniqueGroup = getUniqueFormId(options.form) + groupName;
if (!valGroups[uniqueGroup]) {
valGroups[uniqueGroup] = findByName(groupName, options.form);
}
//jQuery Validation Plugin 1.9.0 only ever validates first chekcbox in a list
//could probably work around setting this for every element:
setValidationValues(options, 'requiredgroup', valGroups[uniqueGroup]);
});
} (jQuery));
of course the elements must have a data-val-requiredgroup attribute. The following MVC code (as part of a helper) will add the appropriate annotations:
var metaData = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
string name = ExpressionHelper.GetExpressionText(expression);
var baseAttr = htmlHelper.GetUnobtrusiveValidationAttributes(name, metaData);
baseAttr.Add("name", name);
baseAttr.Add("type", "checkbox");
if (metaData.IsRequired)
{
baseAttr.Add("data-val-requiredgroup", baseAttr["data-val-required"]);
baseAttr.Remove("data-val-required");
Because it looks for the Required attribute, server side validation is handled by the existing framework.

MVC Html.CheckBox and form submit issue

Crazy issue with submitting of values in Html.Checkbox in ASP.NET MVC RC
Some of the values are just not come to Request.Params
At my form I have this line inside the cycle:
<%=Html.CheckBox("cb" + p.Option.Id, p.Option.IsAllowed, new { value = 6 })%>
and it renders to next:
<input checked="checked" id="cb17" name="cb17" type="checkbox" value="6" />
<input name="cb17" type="hidden" value="false" />
<input checked="checked" id="cb18" name="cb18" type="checkbox" value="6" />
<input name="cb18" type="hidden" value="false" />
<input id="cb19" name="cb19" type="checkbox" value="6" />
<input name="cb19" type="hidden" value="false" />
<input id="cb20" name="cb20" type="checkbox" value="6" />
<input name="cb20" type="hidden" value="false" />
<input checked="checked" id="cb21" name="cb21" type="checkbox" value="6" />
<input name="cb21" type="hidden" value="false" />
After submitting the Form I'm get something like:
Form.Params["cb17"] = {6, "false"}
Form.Params["cb18"] = {6, "false"}
Form.Params["cb19"] = {"false"}
Form.Params["cb20"] = {"6,false"}
Form.Params["cb21"] = {"false"}
In the request string Some of the params are displayed twice (normal situation) and some only ONE TIME (only value of hidden field).
It seems that it doesn't rely on whether checkbox was checked or not, whether value has changed or so...
Does anybody faced with such a situation? How can I work around?
<% using(Html.BeginForm("Retrieve", "Home")) %>//Retrieve is the name of the action while Home is the name of the controller
<% { %>
<%foreach (var app in newApps) { %>
<tr>
<td><%=Html.CheckBox(""+app.ApplicationId )%></td>
</tr>
<%} %>
<input type"submit"/>
<% } %>
and in your controller
List<app>=newApps; //Database bind
for(int i=0; i<app.Count;i++)
{
var checkbox=Request.Form[""+app[i].ApplicationId];
if(checkbox!="false")// if not false then true,false is returned
}
the reason you check for false because the Html Checkbox helper does some kind of freaky thing for value true
True returns as:
it makes the string read "true, false"
so you may have thought it was two values but its just one and means true
False returns as:
it makes the string read "false"
This is actually the way it should work according to specifications.
It has nothing to do with ASP.NET MVC, but when a check box is left unchecked it is not included in the POST collection.
You get two values because you have both a checkbox and an input with the same name (and the ones you have two values for are most likely the ones with checkboxes checked).
Edit: specifications from W3C
Without the need to ask database about the ids after form submitting/before saving (Stateless mode) I've produced such code:
foreach (string key in Request.Form)
{
var checkbox = String.Empty;
if (key.StartsWith("cb"))
{
checkbox = Request.Form["" + key];
if (checkbox != "false")
{
int id = Convert.ToInt32(key.Remove(0, 2));
}
}
}
Thanks you guys to help me work around this issue!
I use this:
public struct EditedCheckboxValue
{
public bool Current { get; private set; }
public bool Previous { get; private set; }
public bool Changed { get; private set; }
public EditedCheckboxValue(System.Web.Mvc.FormCollection collection, string checkboxID) : this()
{
string[] values = collection[checkboxID].Split(new char[] { ',' });
if (values.Length == 2)
{ // checkbox value changed, Format: current,old
Current = bool.Parse(values[0]);
Previous = bool.Parse(values[1]);
Changed = (Current != Previous);
}
else if (values.Length == 1)
{
Current = bool.Parse(values[0]);
Previous = Current;
Changed = false;
}
else
throw new FormatException("invalid format for edited checkbox value in FormCollection");
}
}
and then call it like this:
EditedCheckboxValue issomething = new EditedCheckboxValue(collection, "FieldName");
instance.IsSomething = issomething.Current;

Resources