knockout event handling - javascript

I have a code (you can play it at http://learn.knockoutjs.com/#/?tutorial=intro , click Run in output window before playing ):
HTML:
<div class="btn" style="margin-left: 15px;" data-bind="click: includeMyNumber">
<input data-bind="checked: isIncludeMyNumber" data-val="true" id="IncludeMe" name="IncludeMe" style="margin: 0" type="checkbox" value="true" />
Include my number (+<span>11111111111</span>)
</div>
Javascript:
// This is a simple *viewmodel* - JavaScript that defines the data and behavior of your UI
function AppViewModel() {
this.isIncludeMyNumber = ko.observable(false);
this.includeMyNumber = function(){
this.isIncludeMyNumber(!this.isIncludeMyNumber());
}
}
// Activates knockout.js
ko.applyBindings(new AppViewModel());
The problem is that checkbox click event handling does not work properly. When I click the space inside [div class="btn"...] ...[/div] area, checkbox behaviour is OK, but when I click the checkbox itself, it is not checked. How can I make it checkable in any case?
Thank you.

You´re use case is to make the checkbox checked when clicking the text?
I made a binding for this in my custom binding collection
https://github.com/AndersMalmgren/Knockout.Bindings
http://jsfiddle.net/5nqw4/
<input data-bind="checked: checked, label: { caption: 'Label with reference to input' }" type="checkbox" />
edit: You can also fix this by using the standard hack of wrapping the checkbox in a label element like http://jsfiddle.net/7dTfM/

Related

ng-change not firing after click on each radio button in a list

I am creating a small application for my college project, I have a scenario where when the user clicks on a radio button an event should be fired.
My Angular code block:
<div ng-repeat="q in questionList " ng-if="ExamOver == false">
<h2>{{count+1}} .{{q.questionText}}</h2>
<ul>
<li ng-repeat="d in q.Choices">
<input type="radio" name="isCorrect" ng-model="correctAnswer.isworking" ng-change="getDetails($index,d,correctAnswer.isCorrect);" value="Yes" />
{{d.choiceText}}
</li>
</ul>
</div>
In my controller, I have this code:
$scope.correctAnswer = {isCorrect : false};
$scope.getDetails = function (index, choiceList, isCorrect) {
/* some logic... */
}
The events are firing only once per button, I am trying to work around this for past few hours without any progress, can someone please guide me what I am doing wrong here?
ng-change will be fired when the value of the variable you binded (with ng-model) changed. Here, all your radio buttons have the same value="Yes". That's is why ng-change is not triggered. From docs:
Evaluate the given expression when the user changes the input. The
expression is evaluated immediately, unlike the JavaScript onchange
event which only triggers at the end of a change (usually, when the
user leaves the form element or presses the return key).
The good solution depends of your needs, but where are some ideas:
Solution 1
Set different values for your inputs:
<li ng-repeat="d in q.Choices">
<input type="radio"
name="isCorrect"
ng-model="correctAnswer.isworking"
ng-change="getDetails($index, d, correctAnswer.isCorrect)"
ng-value="$index" />
{{d.choiceText}}
</li>
Solution 2
Fire your function whenever a radio is clicked:
ng-click="getDetails($index, d, correctAnswer.isCorrect)"
ngChange gets triggered if the value of ngModel changes.
Your radio buttons all have the same value namely "Yes"
<input type="radio" name="isCorrect" ng-model="..." ng-change="..." value="Yes" >

KnockoutJS - Correct Way of Writing KO BindingHandlers for Input Type Radio

I have set a custom binding for input type radio, which is like this:
"radio-yesno": function(bindingContext, dataClasses){
return {
click: this,
}
},
The Html Input Type Radio binds to this is written like this:
<label class="radio inline">
<input type="radio" name="RadioTest" id="RadioYes" data-class="radio-yesno"/>
Yes
</label>
<label class="radio inline">
<input type="radio" name="RadioTest" id="RadioNo" data-class="radio-yesno"/>
No
</label>
(You may notice that I am using data-class here to bind the element, it is because I am using the ClassBindingProvider plugin)
The problem now is when the radio button is clicked, the function which contains the associated observable of this element is actually triggered and during debugging it can be seen that the value of
$('#RadioNo').is(':checked')
is actually true when the No Radio Button is clicked and is false when the Yes Radio Button is clicked. BUT, the page itself doesn't show the Radio Button to be selected (checked).
Currently the ko BindingHandlers that I am using here is "click" only.
I have tried to add more bindingHandler which is "checked: this" and put it below the "click: this" line, but still doesn't work.
Could someone please help me to identify what is the problem here and how should it be fixed?
Thanks.
It's not clear from your question exactly what you want the behavior to be, but I've put together a snippet with typical radio button behavior. The most important change is that I added a value attribute to each radio button element. Without them, there's no value change on selecting a different radio button.
I have written but commented out a click binding here, and am using a checked binding, which is usually the better way to bind a radio group. I subscribe to the bound variable just to output information when it changes.
let bindings = {
"radio-yesno": function(bindingContext, dataClasses) {
return {
// click: this.clickHandler,
checked: this.checkedVariable
}
}
};
ko.bindingProvider.instance = new ko.classBindingProvider(bindings);
let vm = {
clickHandler: function(data, event) {
console.debug("Click:", data, event);
true;
},
checkedVariable: ko.observable('on')
};
vm.checkedVariable.subscribe((newValue) => {
console.debug('New value:', newValue);
});
ko.applyBindings(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="//rawgit.com/rniemeyer/knockout-classBindingProvider/master/src/knockout-classBindingProvider.js"></script>
<label class="radio inline">
<input type="radio" name="RadioTest" value="on" data-class="radio-yesno" />Yes
</label>
<label class="radio inline">
<input type="radio" name="RadioTest" value="off" data-class="radio-yesno" />No
</label>

ng-change not fired from checkbox inside label

I have some HTML like this:
<input ng-controller="cboxCtrl" type="checkbox"
ng-model="hideCompleted" ng-change="hideChanged()"/>
and a controller like this:
angular.module('simple-todos').controller('cboxCtrl', ['$scope',
function ($scope) {
console.log("starting");
$scope.hideChanged = function () {
console.log("in hideChanged() ");
};
}]); // end controller
It works fine and I see the message on the console when I click the checkbox. However, if I add a label around the checkbox:
<label>
<input ng-controller="cboxCtrl" type="checkbox"
ng-model="hideCompleted" ng-change="hideChanged()"/>
Some text to explain the checkbox
</label>
The ng-change function does not run when I click the checkbox. I expect it has something to do with scoping but I cannot figure out what. If I replace labels with divs (which of course does not give a "nice" laýout), the ng-change function is again executed as expected.
I just created a jsfiddle with your code and it works for me.
https://jsfiddle.net/jfplataroti/hphb8c4v/5/
angular.module('simple-todos', []).controller('cboxCtrl', ['$scope', cboxCtrl]);
function cboxCtrl($scope) {
console.log("starting");
$scope.hideChanged = function() {
console.log("in hideChanged() ");
};
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="simple-todos">
<label>
<input ng-controller="cboxCtrl" type="checkbox" ng-model="hideCompleted" ng-change="hideChanged()" />some text for the label
</label>
</div>
would you mind sharing your complete code?
clicking on label also triggers ng-change, you can click the label of a checkbox/radio to check/uncheck It should trigger the scoped function.
Please check for the browser you are trying to run this code. Also do check it on other browsers you have installed. AngularJS no longer supports IE8 or earlier.
link here: https://docs.angularjs.org/guide/ie
You need to move your ng-change on the checkbox to the surrounding label's ng-click
<label ng-click="hideChanged()">
<input ng-controller="cboxCtrl" type="checkbox" ng-model="hideCompleted" />
Some text to explain the checkbox
</label>

How to prevent checking checkbox when added dynamically to a div?

so I've been struggling with this issue.
I want to add a checkbox into a div dynamically by clicking a button. Let's say I already have 2 checkboxes in the div, then I uncheck those 2. When I click the button, the checkboxes become 3 (which is what I want), but all those 3 will be checked. What I want is when I add a checkbox, the other checkbox(s)' checked state remain the same as before.
Here is my code (http://jsfiddle.net/gr2o47wt/4/):
HTML:
<div id="chkbox_container">
<input type="checkbox" checked>Check<br />
</div>
<input type="button" value="Add CheckBox" onClick="addCheckBox();">
JavaScript:
function addCheckBox() {
txt = "<input type=\"checkbox\" checked>Check<br />";
document.getElementById('chkbox_container').innerHTML += txt;
}
Thanks in advance for your answers! :)
You can use insertAdjacentHTML() rather than manipulating the innerHTML:
<input type="button" value="Add CheckBox"
onClick="document.getElementById('chkbox_container').insertAdjacentHTML('beforeend','<input type=\'checkbox\' checked=\'checked\' />Check<br />');">
JS Fiddle demo.
The problem you had was that the original HTML (as returned by innerHTML) is from the source of the page, not the DOM; and therefore the checked/unchecked nature of the checkbox originally in place was restored.
insertAdjacentHTML() simply adds the HTML string in the specified place ('beforeend' in this case).
More or less as an aside, it's worth trying, where possible, to keep your event-handling outside of your HTML elements; and binding those event-handlers in the JavaScript itself. This makes for somewhat easier maintainability, and would lead to code like the following:
// note that I gave the button an 'id' for simplicity:
var button = document.getElementById('addCheckboxes');
button.addEventListener('click', function () {
document.getElementById('chkbox_container').insertAdjacentHTML('beforeend', '<input type=\'checkbox\' checked=\'checked\' />Check<br />');
});
JS Fiddle demo.
Finally, some of your HTML is invalid (or at least erroneous), an <input /> is a void element, it can have no descendants; therefore it either has no closing tag (just: <input>) or self-closes (<input />).
Further, the text beside the checkboxes is a little misleading, usually with an HTML form the text beside the <input /> will focus that input; that's achieved by using a <label> element to associate the text with the control, for example:
<label><input type="checkbox" /> click</label>
JS Fiddle demo.
Or:
<input type="checkbox" id="inputElementID" />
<label for="inputElementID">click</label>
But this latter form does require the dynamic generation of ids (which is a little beyond the scope of this question).
References:
insertAdjacentHTML().

Combining checked binding with click binding on containing element [duplicate]

This question already has an answer here:
Prevent event bubbling when using the checked binding in knockoutjs
(1 answer)
Closed 8 years ago.
In my view model I have a boolean property that's visualized by a checkbox in the view. I want the user to be able to click on the containing element as well to toggle the property, but that poses problems when the user clicks the checkbox: the change is not registered then.
Consider this view:
<div class="my-option" data-bind="click: toggleOption1">
<input type="checkbox" data-bind="checked: isOption1Checked" />
Entire div is clickable to select this option.
</div>
With this view model:
var ViewModel = function() {
var self = this;
self.isOption1Checked = ko.observable(false);
self.toggleOption1 = function(){
self.isOption1Checked(!self.isOption1Checked());
};
};
As you can see in this corresponding jsfiddle, this will not allow you to click on the checkbox to change the boolean observable. This kinda makes sense to me, probably the click handler changes the value, but the checked binding also handles the change and reverts it.
The general solution I felt I needed was a one way checked binding or something of the sort, so I tried using the attr binding:
<div class="my-option" data-bind="click: toggleOption1">
<input type="checkbox"
data-bind="attr: { checked: isOption1Checked() ? 'checked' : '???'" />
Entire div is clickable to select this option.
</div>
However, this will not work: there is no checked="false" option in html. You just omit the checked attribute altogether. I don't think the attr binding can do that however.
One other workaround I thought of was to create my own binding (possibly based on / delegating the read bit to the default checked binding), but it feels like overkill. Am I missing an obvious, elegant Knockout solution?
My current workaround (trying to evade creating such a custom binding) involves some elaborate view logic:
<div class="my-option" data-bind="click: toggleOption1">
<!-- ko if: isOption1Checked -->
<input type="checkbox" checked="checked" />
<!-- /ko -->
<!-- ko if: !isOption1Checked() -->
<input type="checkbox" />
<!-- /ko -->
Entire div is clickable to select this option.
</div>
This works, but is very verbose to my taste.
Any other elegant, concise way to handle this?
Right, just after posting and searching some more I found the answer in another question. Here's the specifics for the scenario from my question:
<div class="my-option" data-bind="click: toggleOption1">
<input type="checkbox"
data-bind="checked: isOption1Checked,
click: function() { return true; },
clickBubble: false" />
Entire div is clickable to select this option.
</div>
I'll leave this answer and question here as a duplicate, should it help a random Googling internet user that words his question similar to mine as opposed to similar to the other one.

Categories