Smarty / Dojo checkbox onCheck uncheck other checkbox - javascript

I am using Dojo labels and checkboxes in one of my app inside smarty file. I want to add a certain behavior to uncheck a checkbox, if any other checkbox is checked. I also check if that checkbox is originally checked, it will uncheck the same. (I do not want to use radio button)
Here is my code for one CheckBox:
<input id="form.cs"
data-dojo-type='dijit.form.CheckBox'
type="checkbox"
data-dojo-props="value:'true', type:'checkbox', name:'cs', style:'vertical-align: top'"
onChange="if(dijit.byId('form.cs"').checked)
dijit.byId('form.cs"').set('checked', false);
else
dijit.byId(' form.nl"').set('checked', false);"
/>
The problem with code is when i add curly braces, this is not rendered by smarty engine and throws error.
For example :
onChange="if(dijit.byId('form.cs"').checked) {
dijit.byId('form.cs"').set('checked', false); }
else {
dijit.byId(' form.nl"').set('checked', false);"}
The above code snippet will create a breakdown in the smarty.

I recommend writing your event handler in JavaScript. If you're going to write all your event handlers as attributes you're going to have a lot of problems like code validation, ... .
You could write a loop that actually loops over all checkboxes, setting the value to the opposite of the changed value (so if one checkbox becomes true, the other ones must become false).
To do this you could write a simple function like this:
var toggleCheckboxes = function(myNode, value) {
query("input[type=checkbox]").forEach(function(node) {
if (node !== myNode) {
registry.getEnclosingWidget(node).set("value", !value, false);
}
});
};
The dojo/query module allows you to get a list of all nodes matching the given selector. With the dijit/registry module you can retrieve the actual widget behind the DOM node and then you just set the value using:
registry.getEnclosingWidget(node).set("value", !value, false);
The third parameter (set to false) is actually very important. This parameter will prevent further event invocations. If you don't put that parameter there it will actually trigger another onChange, causing an infinite loop.
Now the only thing you need to do is bind an onChange event handler to each checkbox that calls this function, you can also to that with the dojo/query and dijit/registry module, for example:
query("input[type=checkbox]").forEach(function(node) {
registry.getEnclosingWidget(node).on("change", function(value) {
toggleCheckboxes(node, value);
});
});
A complete example can be found on JSFiddle.
But I still recommend using a radiobutton. I think you can actually say this is a bad UI design, a checkbox and a radiobutton have different goals, use them for what they're meant to.

Related

Have a each() with if statement not working

Problem
I only want the commands in the if logic to happen if the 3rd default button is visible, but it throws an error saying its parent has the css class of visibility: hidden. When you need to remove that state the 3rd default button becomes visible so I'm not sure why it isn't passing.
Desired Behaviour
I want it so for each state that is underlined to click on them to unselect them. Then the if statement is to take care of those who need to be actually removed, because they have info filled out for them, and so if the 3rd default button is visible, which is the remove button, then to execute the code inside the if statement which is to make sure the other default buttons are hidden and click the remove button.
Code
it('deletes all selected states', () => {
cy.get('span[class*="css-ddft8r-StateText"]').each($el => {
cy.wrap($el)
.filter(':has(span[class*="css-1qkbmzm-Underline css-1x6iasc4"])')
.click({ multiple: true });
if (cy.get('[data-cy=default-buttons]').eq(3).should('be.visible')) {
cy.get('[data-cy=default-buttons]')
.eq(0)
.should('be.hidden');
cy.get('[data-cy=default-buttons]')
.eq(2)
.should('be.visible');
cy.get('[data-cy=default-buttons]')
.eq(3)
.click();
}
});
});
Cypress commands return Chainer object - it is inner Cypress entity which can't be used with conditionals as object is always recognized as true within if statement.
So please give a chance for jquery:
if(Cypress.$('[data-cy=default-buttons]:contains("remove")').is(':visible')){
// do assertions
cy.get('[data-cy=default-buttons]:contains("remove")').click({force: true})
// as parent is hidden, we can ommit click visibility check by passing force: true
}
*= is checking that attribute contains part of passed string, so your selector could be simplified to:
[class*="StateText"]
Whole test seems as overengineered and not working properly with these each cycle and conditional inside. Maybe you dont even need to check buttons for each element and move assertions out of loop. Example repository would be helpful.
So the solution I ended up using was actually just using .each() instead of the if statement within it. We figured out we could utilize a ternary inside of a data-cy due to those css classes being unstable/unreliable and this took care of not having to write an if statement.
We did roughly this:
cy.get('[data-cy=example_data_cy]').each($el => {
//click on the state if it was selected to unselect it

Differ Dropdown From manually Selected and auto-Selected

I have the following logic question. I have 3 cascading dropdowns, ddl1, ddl2, ddl3, which call their functions on change events respectively.
When I select ddl1 manually, it sets ddl2, ddl3 and calls ddl1, dd2, dd3 functions on change event.
When I select ddl2 manually it sets ddl3, ddl Automatically and calls dd2,dd3 functions on change events.
Can I use some kind of logic to differentiate that ddl2 was selected manually? Or it is auto selected due to autochange event of ddl1?
I am not able to find answer for this kind of logic.
You can simply use a flag to check this.
Set a flag = false initially. On setting ddl1 set flag = true .
Now ddl2 change event you can check the value of the flag , if it's false it has been selected manually , else by ddl1.
You might need to use multiple flags to do the same for all drop downs.
Do not know if this is the most efficient way , but it can certainly work.
Here is a pseudo code
flag= false
ddl1click()
{
flag = true
do something
}
ddl2change()
{
if(flag)
do things when triggered by ddl1
flag = false
else
do things when in case of manual selection
}

jQuery 'change' method (for HTML attribute modify) working only once

I have this pair of functions affecting two inputs, and one select. These are exclusive so when inputs are filled, select must be modified to have option 3 selected, and when any option except 3 is selected, both inputs must be empty:
$('#ar_filter').on('change', '#ar_fromDate, #ar_toDate', function() {
if ($('#ar_fromDate, #ar_toDate').val!=""){
$('.lastDays').attr('readonly','readonly').find('option[value=3]').attr('selected', true);
}
});
$('#ar_filter').on('change', '#lastDays', 'select', function() {
if ($('.lastDays').val()!=3){
$('#ar_fromDate, #ar_toDate').val("");
}
});
This works, but only the first time. When I write some value on the inputs, it resets correctly select to value 3, but when I change manually selected options, after it resets and leaves inputs empty, it does not reset anymore the select, even when writing on any of those inputs.
JSFIDDLE EXAMPLE (try making 2 select resets by filling the inputs: it will only make the first one)
Based on your JSFiddle, I believe your second implementation of .on() is incorrect. The third optional argument can be passed as data to the handler function as denoted in the reference documentation.
Try changing:
$('#ar_filter').on('change', '#lastDays', 'select', function() {
to this:
$('#ar_filter').on('change', '#lastDays', function() {
Based on your comment above, I believe your selector is wrong. #lastDays is the id of the <select> element, which is where you want the change event bound. The extra select is not needed.
Updated Fiddle
Note:
The updated fiddle includes the .val() fix described by #tymeJV in his answer.
EDIT:
In addition to the .on() selector fix described above, you'll need to break out the two selectors in your .val() statement. This is because only the first input will be validated each time the change event occurs. This comes directly from the jQuery documentation for .val():
Get the current value of the first element in the set of matched elements.
The second value will not be fetched or validated.
Change this:
$('#ar_fromDate, #ar_toDate').val() != ""
to this:
$('#ar_fromDate').val() != "" || $('#ar_toDate').val() != ""
This should fix the problem. I've included an updated fiddle below. I've left the original fiddle in tact to show the progression of steps in solving this problem for the benefit of future visitors.
Complete Fiddle

How to store multiple related values in javascript?

I have a set of checkboxes in a page and each checkbox is associated with a textbox.
What I need to do is to disable the textbox if the checkbox is unchecked and put a default value inside it(each textbox has a different default value).
Right now I have written a function which accepts checkboxId, textboxId and the default value which is called onClick of the checkbox.
Is there a way to store this information(checkBoxId,textBoxId,defaultValue) statically(like a map or something) in javascript so that my function does not require any arguments and can be called onLoad of the page?
This is something I did before:
<input type="checkbox" id="sample">
<input data-requires="sample" data-default="Default">
function applyControls(){
$("input[data-requires]").each(function(){
var target = $(this);
$("#" + target.data("requires")).click(function(){
if(this.checked){
target.prop("disabled", false);
}else{
target.prop("disabled", true).val(target.data("default"));
}
});
});
}
applyControls();
Note: This code is written with jQuery. You can easily convert this to native code by substituting jQuery methods with native methods.
Demo: http://jsfiddle.net/DerekL/VnHAc/
Now this way you don't have to create any map or Object to store these relations (you don't even need ids for individual textboxes!) By using data-* attributes, you do not have to modify a "map" every single time you add connections. In fact, this is the preferred way1 of doing it in HTML5.
1 https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Using_data_attributes

Checked state not getting populated in viewmodel when set programatically

I believe this question is similar to this one but as far as could see in the rules, if there is no answer and it is not the same scenario, I'm allowed to ask.
I've simplified my real scenario with the following, basically, the checkbox is getting checked through some unaccessible code which doesn't get the view model of knockout.js to detect. Is there a work around?
HMTL:
<input id="myCheckbox" type="checkbox" data-bind="checked: myValue" />
<div data-bind="text: myValue"></div>
javascript:
var viewModel = {
myValue: ko.observable(false)
};
ko.applyBindings(viewModel);
setTimeout(function() {
$("#myCheckbox").attr("checked", "checked");
}, 1000);
When a checkbox is modified using the setAttribute function or the checked property, as jqGrid does, it doesn't trigger the click event that Knockout's checked binding uses; neither does it trigger a change event. To be able to detect these changes, you have different options depending on the browser/version: MutationObserver, DOMAttrModified, and/or onpropertychange.
But I'd suggest avoiding those solutions and using what jqGrid gives you: either the jqGridSelectRow event or the onSelectRow callback. You might want to check out the Knockout-jqGridBinding plugin that should give you a good starting point. It includes a selectedItems option that lets you bind an observable array to jqGrid's selected items (using the onSelectRow callback internally).
EDIT:
To re-iterate, I suggest you don't try to solve the problem by watching the checkboxes. But if that's the way you want to go, there's a jQuery plugin, attrchange that provides cross-browser support for this.
Resources:
Knockout-jqGridBinding: https://github.com/CraigCav/Knockout-jqGridBinding
attrchange: http://meetselva.github.io/attrchange/
The answer to which you linked explained that Knockout needs to be alerted of the change through a usual event, such as "click". Here is the idea posted there by Rustam:
function update(){
var $cb = $(':checkbox');
var cb = $cb[0];
// change value directly on element
cb.checked = !cb.checked;
// propagate changes to KO
$cb.triggerHandler('click');
}
setTimeout(update, 1000);
Of course the method more native to KO would be to change the observable on the model, like so:
var update = function() {
viewModel.myValue(!viewModel.myValue)
};
I was not able to fix this on my code in which Foundation was taking control of the checkbox. I ended up binding a click event which then checked on the checkbox to see if it was checked (we only had 1 checkbox that we were trying to keep track of). I then updated the observable from that click event based on whether it was checked or not.
No way around this that I know of.

Categories

Resources