Maybe weird behaviour of DOM tree updating (client side blazor, razor, C# .net ASP.net hosted) - dom

Hello there,
i have just made a weird experience with the DOM tree updating of blazor.
The code is easy:
1) the user can choose between 2 Types A or B with radiobuttons.
2) when A or B is chosen, more options appear as checkboxes (A lets you select A1 and/or A2, B lets you select B1 and/or B2).
Big question is: what happens, if you do the following:
3) select A (=> two checkboxes A1 and A2 appear)
4) check A1
5) select B (=> A1 and A2 vanish, checkboxes B1 and B2 appear)
6) look at the state of B1
I made 2 different versions:
7) one, where i define the checkboxes for A and B separately -> this works as intended (=B1 and B2 unchecked)
8) one, where i define them directly, but with the right name -> this does not work! B1 is checked!
As i understand it, when selecting B (step 5), blazor updates the DOM tree. But i dont get why the browser remembers the checkbox B1 as checked, only because i checked A1, and even though i created it completely new. This looks like a bug to me. I mean, i even give the checkbox another name.
Is there a possibility to "clear the browser memory regarding the checking-state of the checkbox" or something else?
I created this minimum-example for simplicity. In my code, the checkboxes are created in a foreach, and i also have the not-intended behaviour.
Happy to hear your opinion and/or hints.
Tim
Here is the code of my razor component:
<!-- Define 2 Radio-Buttons, User selects A or B -->
<label>
<input type="radio" name="RB_Type" #onclick="#(() => SelectType("A"))" /> Type A
</label>
<br />
<label>
<input type="radio" name="RB_Type" #onclick="#(() => SelectType("B"))" /> Type B
</label>
<br />
<!-- The following code works as intended -->
#if (accType == "A")
{
<input type="checkbox" name="A1" unchecked><label>A1</label>
<br />
<input type="checkbox" name="A2" unchecked><label>A2</label>
<br />
}
else if (accType == "B")
{
<input type="checkbox" name="B1" unchecked><label>B1</label>
<br />
<input type="checkbox" name="B2" unchecked><label>B2</label>
<br />
}
<!-- The following code does not work as intended -->
#if (accType != "")
{
<input type="checkbox" name="#(accType + "1")" unchecked><label>#(accType + "1")</label>
<br />
<input type="checkbox" name="#(accType + "2")" unchecked><label>#(accType + "2")</label>
<br />
}
#code {
private string accType = "";
private void SelectType(string type)
{
this.accType = type;
StateHasChanged();
}
}

Add some #key="" attributes like so:
#if (accType != "")
{
<input type="checkbox" #key="#(accType + "1")" name="#(accType + "1")" unchecked><label>#(accType + "1")</label>
<br />
<input type="checkbox" #key="#(accType + "2")" name="#(accType + "2")" unchecked><label>#(accType + "2")</label>
<br />
}
The blazor diff engine tries to minimize changes. So when it sees 2 checkboxes at the same place it will assume they are the same. The name property doesn't help to distinguish between A and B controls.
But you will find this 2nd version (#(accType + "1")) of your code is difficult in databinding too.

Related

Keep checkbox checked AND only check 1 checkbox per group

I'm looking to combine two things:
a) ensure that when a checkbox is checked, it remains checked after submit, AND
b) only allow one checkbox per group (e.g. the one below) be allowed to be checked at once. I can't use radio buttons unfortunately!
I can do a) & b) in isolation but unfortunately can't seem to combine them! Please help?
<form>
<input type="checkbox" name="a" value="low" <?php if(isset($_POST['a'])) echo "checked='checked'"; ?>/>
<input type="checkbox" name="b" value="mid" <?php if(isset($_POST['b'])) echo "checked='checked'"; ?>/>
<input type="checkbox" name="c" value="hi" <?php if(isset($_POST['c'])) echo "checked='checked'"; ?>/>
</form>
Using jQuery, I was able to create a function that will allow only one checkbox to be clicked at a time.
$('.check').click(function(){
if($('.check:checked').length == 1){
$('.check:not(:checked)').attr('disabled',true);
}
else if($('.check:checked').length == 0){
$('.check:not(:checked)').removeAttr('disabled');
}
});
<p>Check a box</p>
<input type="checkbox" class="check" />
<input type="checkbox" class="check" />
<input type="checkbox" class="check" />
DEMO

Foundation 5 & Abide: a custom validator for a set of checkboxes?

I would like to create a validator for abide for a set of checkboxes.
Let's consider a set of 5 checkboxes. The user is asked to check 3 max, and at least 1.
So, here is my work-in-progress code:
<div data-abide-validator='checkboxes' data-abide-validator-values='1,3'>
<input type="checkbox"/>
<input type="checkbox"/>
<input type="checkbox"/>
<input type="checkbox"/>
<input type="checkbox"/>
</div>
<script>
$(document).foundation({
validators: {
checkboxes: function(el, required, parent) {
var countC = el.find(':checked').length;
alert(countC);
}
}
});
</script>
At this point, I just try to count the checked inputs. But it seems I can't even trigger the validator... I think I could manage to code my validation stuff if only I could figure out how to trigger it.
Indeed I didn't find many examples of the custom validator, and the official doc did not help me much.
Your HTML markup is not really "correct" for abide. You should be attaching the data-abide-validator attribute to the inputs, not the parent div. Additionally, you need some better markup so abide's default error display can work (and some better use of foundation's grid system to lay it out). I would point you toward the Abide Validation Page on Zurb's site for some examples of form markup.
I've taken the liberty of restructuring your markup to be something that is more becoming of a foundation layout:
<form action="/echo/html/" method="POST" data-abide>
<div class="row">
<div class="small-12 columns checkbox-group" data-abide-validator-limit="1,3">
<label>Check some boxes</label>
<small class="error">You have checked an invalid number of boxes.</small>
<ul class="small-block-grid-3">
<li>
<label>
<input type="checkbox" data-abide-validator="checkbox_limit" value="1" /> 1
</label>
</li>
<li>
<label>
<input type="checkbox" data-abide-validator="checkbox_limit" value="2" /> 2
</label>
</li>
<li>
<label>
<input type="checkbox" data-abide-validator="checkbox_limit" value="3" /> 3
</label>
</li>
<li>
<label>
<input type="checkbox" data-abide-validator="checkbox_limit" value="4" /> 4
</label>
</li>
<li>
<label>
<input type="checkbox" data-abide-validator="checkbox_limit" value="5" /> 5
</label>
</li>
</ul>
</div>
</div>
<div class="row">
<div class="small-12 columns">
<button type="submit">Submit</button>
</div>
</div>
</form>
As to your JS code. It's not correct either. You need to address the abide -> validators namespace of the options, not just validators. I've rewritten your JS code to not only do that, but give the desired effect you wanted:
$(document).foundation({
abide: {
validators: {
checkbox_limit: function(el, required, parent) {
var group = parent.closest( '.checkbox-group' );
var limit = group.attr('data-abide-validator-limit').split(',');
var countC = group.find(':checked').length;
if( countC >= limit[0] && countC <= limit[1] ) {
group.find('small.error').hide();
//return true so abide can clear any invalid flags on this element
return true;
} else {
group.find('small.error').css({display:'block'});
//return false and let abide do its thing to make sure the form doesn't submit
return false;
}
}
}
}
});
In order to check adjacent elements when doing custom validation, you need to have something to target. The el variable in the validation function will be the DOM element of the input/field that is being validated. The required variable will tell you if the field is flagged as being required or not (boolean). The parent variable will be set to the "parent" of the field. I say "parent" because although the label tag is technically the parent of the input element, abide is smart enough to realize that the label is part of the field's element structure and skip over it to the li element instead.
From there, you need a way to identify a common parent. So I added the checkbox-group class to whatever element I decided to make the "parent" of all the checkboxes in the group. This is not a Foundation or Abide "magic" class, but rather something of my own creation for use in the validation function.
From there, you can easily trace the few lines of the validation function to see the workflow: Find the group container object, parse the limits off the container's data-abide-validator-limits attribute, count the number of checked inputs in the container, check if the number checked is between the limits, display/hide the error message and return true/false so abide knows if the field validated or not.
I've got a working Fiddle of it if you care to check it out yourself ;) Hopefully this was informative for you, and I wish you the best of luck playing with the awesome that is Foundation!

get checkbox and radio button value in lift

i am trying to processing a form in lift frame work. my form is having one checkbox and radiobuttons. how could i check whether the checkbox is checked or not and the selected radio button.the following code i used to get other elements value.
the view:
<form class="lift:MySnippet?form=post">
From:<input type="text" name="source" /><br />
To: <input type="text" name="destination" /><br />
Age: <input type="text" name = "age"/><br />
Return: <input type="checkbox" name="needreturn" value="Return Ticket" /><br />
<input type="radio" name="sex" value="male" />Male<br />
<input type="radio" name="sex" value="male" />Female<br />
<input type="submit" value="Book Ticket"/>
</form>
and MySnippet scala code is:
object MySnippet {
def render = {
var from = ""
var to = ""
var age = 0
def process() {
S.notice("in process function")
}
"name=source" #> SHtml.onSubmit(from = _) &
"name=destination" #> SHtml.onSubmit(to = _) &
"name=age" #> SHtml.onSubmit(s => asInt(s).foreach(age = _)) &
"type=submit" #> SHtml.onSubmitUnit(process)
}
}
in this how could i process the checkbox and radio button. can anyone help me...thanx in advance.
Do you need to specify the choices in your HTML? If not, the easiest way is:
Return: <input type="checkbox" name="needreturn" /><br />
Sex: <input type="radio" name="sex" />
and the CSS Transform:
val radioChoices = List("male", "female")
var sex:Box[String] = None
var needReturn:Boolean = false
"#sex" #> SHtml.radio(radioChoices, sex, (resp) => sex = Full(resp)) &
"#needreturn" #> SHtml.checkbox(needReturn, (resp) => needReturn = resp)
You could replace SHtml.radio with SHtml.ajaxRadio and SHtml.checkbox with SHtml.ajaxCheckbox if you want your selection to be sent to the server every time the value is changed, instead of when the form is submitted
I believe you can also use the SHtml.onSubmit as you do above for the checkbox and radio, but I'd have to do some testing to figure out exactly how.
With regards to the radio button, you can find some information about changing the way the label is output here if you need to: https://groups.google.com/forum/?fromgroups=#!topic/liftweb/rowpmIDbQAE
Use SHtml.checkbox, SHtml.radio
By the way, the <input>-s should be SHtml.text, I think. So, they're not buttons -- they're inputs. Don't forget to check the resulting html in the web page with firebug. (You'd see that using the current code you have input=text deleted.)

Example of jQuery Mobile site with conditional/branching questions

I'm trying to create a JQM survey with branching questions--i.e. in a survey with questions 1-3, if you choose a particular answer on question 1, a question is dynamically added between questions 1 and 2.
UPDATE: I made an attempt ( https://dl.dropbox.com/u/17841063/site2/index-c1.html#page2 ) that works by matching the value of a radio button to the name of a hidden div--if there's a match, it unhides the div. The problem right now is that if you change your answer back to an option that wouldn't trigger the conditional question, it doesn't re-hide. For example, clicking No or Unsure in question A1 causes question A2 to appear, but if you then click Yes in A1, A2 still remains...
<script type="text/javascript">
// Place in this array the ID of the element you want to hide
var hide=['A2','A4'];
function setOpt()
{
resetOpt(); // Call the resetOpt function. Hide some elements in the "hide" array.
for(var i=0,sel=document.getElementsByTagName('input');i<sel.length;i++)
{
sel[i].onchange=function()
{
if(this.parentNode.tagName.toLowerCase()!='div')
resetOpt(); // Hides the elements in "hide" array when the first select element is choosen
try
{
document.getElementById(this.value).style.display='';
}
catch(e){} ; // When the value of the element is not an element ID
}
}
}
window.addEventListener?window.addEventListener('load',setOpt,false):
window.attachEvent('onload',setOpt);
function resetOpt()
{
for(var i=0;i<hide.length;i++)
document.getElementById(hide[i]).style.display='none'; // Hide the elements in "hide" array
}
</script>
Here's are the radio buttons that use the script above:
<div data-role="fieldcontain">
<fieldset data-role="controlgroup" data-type="horizontal">
<legend>(Question A1) A prominent accident smokes on top of the blessed reactionary?</legend>
<input type="radio" name="aaa" id="aaa_0" value="notA2" />
<label for="aaa_0">Yes</label>
<input type="radio" name="aaa" id="aaa_1" value="A2" />
<label for="aaa_1">No</label>
<input type="radio" name="aaa" id="aaa_2" value="A2" />
<label for="aaa_2">Unsure</label>
</fieldset>
</div>
<div id="A2" data-role="fieldcontain">
<fieldset data-role="controlgroup" data-type="horizontal">
<legend>(Question A2) Does a married composite remainder the shallow whistle??</legend>
<input type="radio" name="bbb" id="bbb_0" value="" />
<label for="bbb_0">Yes</label>
<input type="radio" name="bbb" id="bbb_1" value="" />
<label for="bbb_1">No</label>
<input type="radio" name="bbb" id="bbb_2" value="" />
<label for="bbb_2">Unsure</label>
</fieldset>
</div>
If anyone has ideas about fixing this, or examples of other ways to do branching forms, I'd be very grateful!
Thanks,
Patrick
I played around a little bit with your example, removed all your plain JavaScript and added some jQuery Mobile style script, see working example here
<script>
$("input[type='radio']").bind( "change", function(event, ui) {
var mySelection = $('input[name=aaa]:checked').val();
//alert(mySelection);
if (mySelection == "A2") {
$('#A2').removeClass('ui-hidden-accessible');
} else {
$('#A2').addClass('ui-hidden-accessible');
};
});
</script>

Required attribute on multiple checkboxes with the same name? [duplicate]

This question already has answers here:
Using the HTML5 "required" attribute for a group of checkboxes?
(16 answers)
Closed 6 years ago.
I have a list of checkboxes with the same name attribute, and I need to validate that at least one of them has been selected.
But when I use the html5 attribute "required" on all of them, the browser (chrome & ff) doesn't allow me to submit the form unless all of them are checked.
sample code:
<label for="a-0">a-0</label>
<input type="checkbox" name="q-8" id="a-0" required />
<label for="a-1">a-1</label>
<input type="checkbox" name="q-8" id="a-1" required />
<label for="a-2">a-2</label>
<input type="checkbox" name="q-8" id="a-2" required />
When using the same with radio inputs, the form works as expected (if one of the options is selected the form validates)
According to Joe Hopfgartner (who claims to quote the html5 specs), the supposed behaviour is:
For checkboxes, the required attribute shall only be satisfied when one or more of the checkboxes with that name in that form are checked.
For radio buttons, the required attribute shall only be satisfied when exactly one of the radio buttons in that radio group is checked.
am i doing something wrong, or is this a browser bug (on both chrome & ff) ??
You can make it with jQuery a less lines:
$(function(){
var requiredCheckboxes = $(':checkbox[required]');
requiredCheckboxes.change(function(){
if(requiredCheckboxes.is(':checked')) {
requiredCheckboxes.removeAttr('required');
}
else {
requiredCheckboxes.attr('required', 'required');
}
});
});
With $(':checkbox[required]') you select all checkboxes with the attribute required, then, with the .change method applied to this group of checkboxes, you can execute the function you want when any item of this group changes. In this case, if any of the checkboxes is checked, I remove the required attribute for all of the checkboxes that are part of the selected group.
I hope this helps.
Farewell.
Sorry, now I've read what you expected better, so I'm updating the answer.
Based on the HTML5 Specs from W3C, nothing is wrong. I created this JSFiddle test and it's behaving correctly based on the specs (for those browsers based on the specs, like Chrome 11 and Firefox 4):
<form>
<input type="checkbox" name="q" id="a-0" required autofocus>
<label for="a-0">a-1</label>
<br>
<input type="checkbox" name="q" id="a-1" required>
<label for="a-1">a-2</label>
<br>
<input type="checkbox" name="q" id="a-2" required>
<label for="a-2">a-3</label>
<br>
<input type="submit">
</form>
I agree that it isn't very usable (in fact many people have complained about it in the W3C's mailing lists).
But browsers are just following the standard's recommendations, which is correct. The standard is a little misleading, but we can't do anything about it in practice. You can always use JavaScript for form validation, though, like some great jQuery validation plugin.
Another approach would be choosing a polyfill that can make (almost) all browsers interpret form validation rightly.
To provide another approach similar to the answer by #IvanCollantes.
It works by additionally filtering the required checkboxes by name. I also simplified the code a bit and checks for a default checked checkbox.
jQuery(function($) {
var requiredCheckboxes = $(':checkbox[required]');
requiredCheckboxes.on('change', function(e) {
var checkboxGroup = requiredCheckboxes.filter('[name="' + $(this).attr('name') + '"]');
var isChecked = checkboxGroup.is(':checked');
checkboxGroup.prop('required', !isChecked);
});
requiredCheckboxes.trigger('change');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<form target="_blank">
<p>
At least one checkbox from each group is required...
</p>
<fieldset>
<legend>Checkboxes Group test</legend>
<label>
<input type="checkbox" name="test[]" value="1" checked="checked" required="required">test-1
</label>
<label>
<input type="checkbox" name="test[]" value="2" required="required">test-2
</label>
<label>
<input type="checkbox" name="test[]" value="3" required="required">test-3
</label>
</fieldset>
<br>
<fieldset>
<legend>Checkboxes Group test2</legend>
<label>
<input type="checkbox" name="test2[]" value="1" required="required">test2-1
</label>
<label>
<input type="checkbox" name="test2[]" value="2" required="required">test2-2
</label>
<label>
<input type="checkbox" name="test2[]" value="3" required="required">test2-3
</label>
</fieldset>
<hr>
<button type="submit" value="submit">Submit</button>
</form>
i had the same problem, my solution was apply the required attribute to all elements
<input type="checkbox" name="checkin_days[]" required="required" value="0" /><span class="w">S</span>
<input type="checkbox" name="checkin_days[]" required="required" value="1" /><span class="w">M</span>
<input type="checkbox" name="checkin_days[]" required="required" value="2" /><span class="w">T</span>
<input type="checkbox" name="checkin_days[]" required="required" value="3" /><span class="w">W</span>
<input type="checkbox" name="checkin_days[]" required="required" value="4" /><span class="w">T</span>
<input type="checkbox" name="checkin_days[]" required="required" value="5" /><span class="w">F</span>
<input type="checkbox" name="checkin_days[]" required="required" value="6" /><span class="w">S</span>
when the user check one of the elements i remove the required attribute from all elements:
var $checkedCheckboxes = $('#recurrent_checkin :checkbox[name="checkin_days[]"]:checked'),
$checkboxes = $('#recurrent_checkin :checkbox[name="checkin_days[]"]');
$checkboxes.click(function() {
if($checkedCheckboxes.length) {
$checkboxes.removeAttr('required');
} else {
$checkboxes.attr('required', 'required');
}
});
Here is improvement for icova's answer. It also groups inputs by name.
$(function(){
var allRequiredCheckboxes = $(':checkbox[required]');
var checkboxNames = [];
for (var i = 0; i < allRequiredCheckboxes.length; ++i){
var name = allRequiredCheckboxes[i].name;
checkboxNames.push(name);
}
checkboxNames = checkboxNames.reduce(function(p, c) {
if (p.indexOf(c) < 0) p.push(c);
return p;
}, []);
for (var i in checkboxNames){
!function(){
var name = checkboxNames[i];
var checkboxes = $('input[name="' + name + '"]');
checkboxes.change(function(){
if(checkboxes.is(':checked')) {
checkboxes.removeAttr('required');
} else {
checkboxes.attr('required', 'required');
}
});
}();
}
});
A little jQuery fix:
$(function(){
var chbxs = $(':checkbox[required]');
var namedChbxs = {};
chbxs.each(function(){
var name = $(this).attr('name');
namedChbxs[name] = (namedChbxs[name] || $()).add(this);
});
chbxs.change(function(){
var name = $(this).attr('name');
var cbx = namedChbxs[name];
if(cbx.filter(':checked').length>0){
cbx.removeAttr('required');
}else{
cbx.attr('required','required');
}
});
});
Building on icova's answer, here's the code so you can use a custom HTML5 validation message:
$(function() {
var requiredCheckboxes = $(':checkbox[required]');
requiredCheckboxes.change(function() {
if (requiredCheckboxes.is(':checked')) {requiredCheckboxes.removeAttr('required');}
else {requiredCheckboxes.attr('required', 'required');}
});
$("input").each(function() {
$(this).on('invalid', function(e) {
e.target.setCustomValidity('');
if (!e.target.validity.valid) {
e.target.setCustomValidity('Please, select at least one of these options');
}
}).on('input, click', function(e) {e.target.setCustomValidity('');});
});
});
var verifyPaymentType = function () {
//coloque os checkbox dentro de uma div com a class checkbox
var inputs = window.jQuery('.checkbox').find('input');
var first = inputs.first()[0];
inputs.on('change', function () {
this.setCustomValidity('');
});
first.setCustomValidity( window.jQuery('.checkbox').find('input:checked').length === 0 ? 'Choose one' : '');
}
window.jQuery('#submit').click(verifyPaymentType);
}