Disallow unchecking of all checkboxes in angularJS - javascript

I'm using AngularJS directive for list of checkboxes checklist-model but I need to keep at least one check box checked at all times.
I've changed the checklist-model.js by adding an if:
watch if(current.length > 1)
before removing a check:
scope.$watch(attrs.ngModel, function(newValue, oldValue) {
if (newValue === oldValue) {
return;
}
var current = getter(scope.$parent);
if (angular.isFunction(setter)) {
if (newValue === true) {
setter(scope.$parent, add(current, value, comparator));
} else {
if(current.length > 1)
{
setter(scope.$parent, remove(current, value, comparator));
}
}
}
if (checklistChange) {
checklistChange(scope);
}
});
The problem is that the UI has already changed to unchecked while the model did not change.
Anyone has an elegant suggestion (preferably without JQuery) on how to change the checkbox back to "checked" state, or even better to catch it before it changed to checked?
JSfiddle here, try to uncheck all and see that when the check boxes are all unchecked (what I'm trying to prevent) the model stays not empty (which is good but not enough)

This probably comes from asynchronous nature of angular and the library you are using. What I could find so far is some dirty workaround like this (jsfiddle): create an event where you track the clicks on input and do the following:
$scope.onclick = function(evt) {
if ($scope.user.roles.length === 1 && prevLength === 1) {
evt.preventDefault();
}
prevLength = $scope.user.roles.length;
};
then add it it to your element
ng-click="onclick($event)"
I don't say it's perfect but it works

A colleague of mine suggested to disable the checkbox once it's the only one selected, that way you both prevent the changing of the module (therefore the directive does not even need to change) and the UX is better as the user understands he can't uncheck it:
<input type="checkbox" checklist-model="user.roles" checklist-value="role.id" ng-disabled="shouldDisable(role.id)"> {{role.text}}
$scope.shouldDisable = function(roleId) {
var isDisabled = false;
if($scope.user.roles.length === 1) {
isDisabled = $scope.user.roles.indexOf(roleId) !== -1;
}
return isDisabled;
}
See answer in this JSfiddle

At this moment, the component does not support this, but there's a discussion to implement minlength/maxlength https://github.com/vitalets/checklist-model/issues/15 and then this would be simply implemented by using checklist-minlength=1.
I also edited your proposal to implement checklistBeforeChange. I will try to handle this too in the next batch. Implemented in v0.8.0.

A nicer version to #ozba answer using the array.some function:
public atLeastOneRowEnabled(row): boolean {
//at least one row is visible except the current line
return !this.rows.some(rowIterator => rowIterator.logical_name !== row.logical_name && rowIterator.isVisible);
}

Related

JavaScript if-elseif-statement

In PHP, the different if-elseif-scenarios rule each other out, right? I am a little confused, I don't seem to figure out why this is not the case in JavaScript here. Can anybody tell me how to simplify this?
(This statement is connected to radio-buttons and is supposed to style the selected radio button differently. However, when I do not include all the remove-parts, clicking one and then another one leaves me with both of them styled as "selected")
$("#item-upload-form input").on("change", function() {
var hello = $("input[name='category_select']:checked", "#item-upload-form").val();
if(hello == 1){
$("#handy").addClass("selected");
$("#pc").removeClass("selected");
$("#notebook").removeClass("selected");
} else if (hello == 2){
$("#pc").addClass("selected");
$("#handy").removeClass("selected");
$("#notebook").removeClass("selected");
} else if (hello == 3){
$("#notebook").addClass("selected");
$("#pc").removeClass("selected");
$("#handy").removeClass("selected");
}
});
I think #Katana314 had the right answer to the question you're asking. Javascript isn't refreshing the page on each call so the class will stay on the element until you remove it. Might be a little cleaner this way...
$("#item-upload-form input").on("change", function() {
var hello = $("input[name='category_select']:checked", "#item-upload-form").val();
// find any element that has the selected class and remove it
$('.selected').removeClass('selected');
// then add it to which ever element needs it.
if(hello == 1){
$("#handy").addClass("selected");
} else if (hello == 2){
$("#pc").addClass("selected");
} else if (hello == 3){
$("#notebook").addClass("selected");
}
});
Because you're using two selectors and checking hello with the value will only work for those whose both value returns the same value. If both selector value results in different values then your condition never match.
So, it will only match if both values are the same.
Let's keep only calls to addClass(). The code would look like this:
if(hello == 1){
$("#handy").addClass("selected");
} else if (hello == 2){
$("#pc").addClass("selected");
} else if (hello == 3){
$("#notebook").addClass("selected");
}
What happens when you click on radio buttons?
R: Each time it will run only ONE branch. Successive clicks will only addClass to current element, and maintain classes of previous elements.
Why not let jQuery do all of the work for you - this way you can add/remove selections without updating your code:
$("#item-upload-form input").on("change", function (ele) {
$("#item-upload-form input").each(function( ) {
if ($(this).prop('checked')){
$(this).addClass("selected")
} else{
$(this).removeClass("selected") ;
}
});
});
To answer your question, yes, if-else-blocks will stop evaluating if a match is found.
Suggestion 1:
Try using === instead of ==.
=== checks both if the value and type are the same.
== 1 will pass as true for many things, including '1' == 1 and true == 1. I don't know what hello actually is, but you might be getting a false positive.
Suggestion 2:
Here is a revised code suggestion (instead of if-else blocks)
$("#handy").toggleClass("selected", (hello === 1));
$("#pc").toggleClass("selected", (hello === 2));
$("#notebook").toggleClass("selected", (hello === 3));

If select input equals value do this, if it is changed remove those changes

I have a select box called "requestHistoryRequestType". I'm trying to write some jQuery so that when the value of that select box is changed I call a function that adds a class and attribute to a field and appends a span to the field that I pass in as a parameter.
The problem is if a user chooses EXPAPP or EXPDEN but then changes their selection to NA it should remove the added stuff from the previous fields and add the same stuff to a different field. Kinda hard to explain, but ask questions away! I'm kinda new to writing complex jQuery like this.
The function that does the adding classes and such:
function requiredField(requiredField) {
$(requiredField).parent().addClass('has-error');
$(requiredField).attr('data-rule-required', true);
$("label[for='" + requiredField.replace('#', '') + "']").append("<span style='color:#b94a48;' class='has-error has-tooltip' data-placement='right' title='Required Field'>*</span>");
}
The actual on change listener:
//Validations for EXPAPP, EXPDEN, and NA
$("#requestHistoryRequestType").on("change", function() {
if ($("#requestHistoryRequestType").val() === "EXPAPP" || $("#requestHistoryRequestType").val() === "EXPDEN"){
requiredField("#requestHistoryVerbalDateTime");
requiredField("#requestHistoryWrittenDateTime");
} else if ($("#requestHistoryRequestType").val() === "NA") {
requiredField("#requestHistoryComments");
}
});
Thanks Stack!
Create a function that would remove the added stuff from all fields and call it before requiredField() calls:
function removeRequiredFields()
{
var $fields = $("#requestHistoryVerbalDateTime, #requestHistoryWrittenDateTime, #requestHistoryComments");
$fields.parent().removeClass('has-error');
$fields.attr('data-rule-required', false);
$fields.each(function() {
$("label[for='"+$(this).attr('id')+"']").find("[title='Required Field']").remove();
});
}
Or you can pass $fields from the event handler to removeRequiredFields() instead of hardcoding it there, for added flexibility.
I would just have a separate function for when you select a "NA" rather then trying to build that functionality into the same function.
I'll rewrite your event handler to make it a bit cleaner as well (IMO).
//Validations for EXPAPP, EXPDEN, and NA
$("#requestHistoryRequestType").on("change", function() {
var selectedVal = $(this).val();
if (selectedVal === "EXPAPP" || selectedVal === "EXPDEN"){
requiredField("#requestHistoryVerbalDateTime");
requiredField("#requestHistoryWrittenDateTime");
} else if (selectedVal === "NA") {
requiredField("#requestHistoryComments");
}
});
This way you are not hitting the DOM a potential 3 time to test your conditions every time an event is triggered. A minor change but probably a useful one as you get into more complex and larger jQuery selectors.
Edit: If you feel you MUST do it in one function then you can call the function with both elements you want to append
function requiredField(requiredField1, requiredField2) {
if (requiredField2 != null){
$(requiredField1,requiredField1).parent().addClass('has-error');
$(requiredField1,requiredField1).attr('data-rule-required', true);
var requiredLabel = "<span style='color:#b94a48;' class='has-error has-tooltip' data-placement='right' title='Required Field'>*</span>"
$("label[for='" + requiredField1.replace('#', '') + "']").append(requiredLabel);
$("label[for='" + requiredField2.replace('#', '') + "']").append(requiredLabel);
}
else {
//remove multiple element classes and add it to the single one representing the "NA"
}
}
This is based on you only ever having one case where you would be passing a single "requiredField" on a case of a "NA"

Radio button to Prevent to be checked after on click

I am trying to prevent a radio button not to be checked if a false is returned from the onclick.
Following is the jsbin link for the code I am using.
http://jsbin.com/oruliz/2/
Is there anything I am missing; BTW, I am trying to use JS with no framework.
However, if pure js has this issue is there a workaround for prototyoe.js ?
Try this
function propertyDamageType_click(elem) {
if(yourconditionfails){ // or if(yourconditionfails && !elem.checked)
elem.checked = false;
alert('Please select an incident type');
}
}
Demo
You should user return propertyDamageType_click() .
See the http://jsbin.com/uvopek/1/edit
You can use a flag and return false whenever the pre codition is not met....
var IsIncidenntTypeSelected = 0; // flag - sets to 1 when pre condition is met
$("input[type='radio']").click(function()
{
if(IsIncidenntTypeSelected == 0)
return false;
}

Phonegap - Determine exact element active

I need to change the back button functionality of my phonegap project, which I've succeeded in doing without any problem. The only issue now, is that I need to further change the functionality based on if the user has a certain field selected.
Basically, if the user has clicked in a field with the id of "date-selector1", I need to completely disable the back button.
I was attempting to use document.activeElement, but it only returns the type of the element (input in this case), but I still want the functionality to work when they are in a general input, but not when they are in an input of a specific id.
EDIT
I tried all of the suggestions below, and have ended up with the following code, but still no success.
function pluginDeviceReady() {
document.addEventListener("backbutton", onBackKeyDown, false);
}
function onBackKeyDown() {
var sElement = document.activeElement;
var isBadElement = false;
var eList = ['procedure-date', 'immunization-date', 'lab-test-done', 'condition-onset', 'condition-resolution', 'medication-start-date', 'medication-stop-date', 'reaction-date'];
console.log("[[ACTIVE ELEMENT: --> " + document.activeElement + "]]");
for (var i = 0;i < eList.length - 1;i++) {
if (sElement == $(eList[i])[0]) {
isBadElement = true;
}
}
if (isBadElement) {
console.log('Back button not allowed here');
} else if ($.mobile.activePage.is('#main') || $.mobile.activePage.is('#family') || $.mobile.activePage.is('#login')) {
navigator.app.exitApp();
} else {
navigator.app.backHistory();
}
}
if you're listening for the back button you can add this if statement:
if (document.activeElement == $("#date-selector1")[0]) {
/*disable button here, return false etc...*/
}
or even better (Thanks to Jonathan Sampson)
if (document.activeElement.id === "date-selector1") {
/*disable button here, return false etc...*/
}
You can have a flag set when a user clicks on a field or you can have a click event (or any other type of event) when a user clicks on the field that should disable the back button.
From the documentation it looks like for the specific page that the backbuton is conditional on you can drop back-btn=true removing that back button.
http://jquerymobile.com/test/docs/toolbars/docs-headers.html
If you need complex conditional functionality you can just create your own button in the header or footer, style it using jquery-mobile widgets and implement your own click functionality.

Javascript check/uncheck all function. IE doesn't update

function checkUncheckAll(theElement) {
var theForm = theElement.form, z = 0;
while (theForm[z].type == 'checkbox' && theForm[z].name != 'checkall') {
theForm[z].checked = theElement.checked;
z++;
}
}
theElement is the checkall checkbox at the bottom of a list of checkboxes. when clicked it calls this function to set all checkboxes in the same form to the value of checkall.
It works across all browsers aside from one glitch in IE. After clicking the checkall box it seems like the checkboxes are updated, but you don't see it. If you click anywhere on the page the checkboxes are then updated to their proper status. This happens for both checking and unchecking.
This is easy without jQuery, which is unnecessary here.
function checkUncheckAll(theElement) {
var formElements = theElement.form.elements;
for (var i = 0, len = formElements.length, el; i < len; ++i) {
el = formElements[i];
if (el.type == "checkbox" && el != theElement) {
el.checked = theElement.checked;
}
}
}
Update
It turned out the main problem was that the checkUncheckAll() function was being called from a change event handler on a checkbox, which doesn't fire in IE until the checkbox loses focus, so the fix was simply a matter of changing it to use a click event handler instead.
The best way to do this is to use jQuery. It does require including a library, but it's well worth it! jQuery handles cross-browser js/DOM issues, and provides an excellent selector syntax and modification methods.
Additional examples similar to yours are hilighted in this Blog Post:
http://www.iknowkungfoo.com/blog/index.cfm/2008/7/9/Check-All-Checkboxes-with-JQuery
function checkUncheckAll(theElement){
newvalue = $(theElement).is(':checked');
$("INPUT[type='checkbox']", theElement.form).attr('checked', newvalue);
}

Categories

Resources