Radio Button List with Knockout.js - javascript

I'm trying to build a list of radio buttons with labels so that you can click the label to check the radio item. What I have works fine in Chrome, but not IE7. The HTML that get's spit out seems like it is correct, but when I click on the label, the corresponding radio button doesn't get selected.
JavaScript
function ReuqestType(id, name, billable) {
this.id = id;
this.name = name;
this.billable = billable;
}
function RequestViewModel() {
var self = this;
self.availableRequestTypes = [
new ReuqestType(1, "Travel", true),
new ReuqestType(2, "Bill Only", false),
new ReuqestType(3, "Both", true)
];
self.selectedRequestType = ko.observable();
}
HTML
Request Type
<br />
<!-- ko foreach: availableRequestTypes -->
<input type="radio" name="requestType" data-bind="value:id, attr: {'id': 'rt'+ id}" />
<label data-bind="text: name, attr:{'for':'rt'+id}">
</label>
<!-- /ko -->
What is the preferred way to do this?

This seems to be working correctly now as of the newest version of Knockout (2.1.0).

I'm unfamiliar with knockout.js, but you can normally bind label's to inputs just by making the label wrap around the input box too.
http://jsfiddle.net/QD2qC/

It looks like your HTML is fine. It could be a quirk of IE7 not being able to acknowledge clicks of labels that have been generated dynamically.
If you can't find a proper answer, you could always use this workaround:
$('label').on('click', function() {
var labelId = $(this).attr('for');
$('#' + labelId ).trigger('click');
});
I have changed it slightly by using on() instead of click() to account for these labels being generated dynamically.

Related

Hide field based on checkbox using javascript in odoo 12

I have two fields, field1 is a checkbox and field2 is a normal text field. I want to hide a field2 if a field1 is not checked and if
field1 is checked then show field2. For that, I am creating the following code:
odoo.define('survey_inherit.FormView', function (require) {
"use strict";
var FormView = require('web.FormView');
var core = require('web.core');
var QWeb = core.qweb;
var FormView = FormView.extend({
_checkField: function(){
var $checkbox = $('.custom-control-input').val();
if ($checkbox.is(':checked')) {
$('.o_form_label').show();
$('.mandatory_msg_class').show();
}else{
$('.mandatory_msg_class').hide();
}//close else
},
});//close FormController
return FormView;
});
But field2 is not hidden if field1 is not checked and also field2 is not shown if field1 is checked.
Update
My requirement is that I have one form which contains a one2many field with widget many2many_tags and other fields. After clicking on tags of many2many_tags, I want to display the complete records in other fields. I am able to get complete records after a click on tags and also these records are able to put in other fields. After using attrs and opening form view for creating records field2 will never display. But open form view in edit mode after creating a record and click on many2many_tags
field2 is not displayed because of attrs.
Removing attrs and opening form view for creating records field will display(but don't want to display because field1 is not checked) and open form view in edit mode after creating a record, click on many2many_tags work fine as expected.
Attrs condition: attrs="{'invisible':[('constr_mandatory','!=',True)]}"
constr_ mandatory: checkbox field
This is the reason I am not using attrs and trying to achieve with the help of javascript. I hope the provided information is understood. Also, I have updated the question added screenshots for better understanding.
Using attrs:
Using attrs click on many2many_tags
Without using attrs:
You can add a new widget and override the click method.
I did this with a BooleanToggle field.
var basic_fields = require('web.basic_fields');
var Toggle = basic_fields.BooleanToggle.extend({
_onClick: function (event) {
var self = this;
this._super(event);
var node = event.view.$('.custom-control-input');
if(this.value) {
node.show();
} else {
node.hide();
}
},
});
fieldRegistry.add('toggle', Toggle);
You need to add the widget attribute:
field name="field1" widget="toggle"/>
Edit
You need to hide the fields after the form loaded, I suggest to you to override the autofocus function of the FormRenderer.
var FormRenderer = require('web.FormRenderer');
FormRenderer.include({
autofocus: function () {
var self = this;
// In my test I used fields values available in "self.state.data"
if(self.state.model === 'sale.order' && field_value){
var nodes = window.$('.custom-control-input');
nodes.hide();
}
return this._super();
},
});
var $checkbox = $('.custom-control-input').val();
This line set $checkbox to the checkbox's value. May be you just want the checkbox element itself:
var $checkbox = $('.custom-control-input');
Try jQuery
$(function() {
$("#selpoNO").click(function() {
if ($(this).is(":checked")) {
$(".if_pucEntry").show();
} else {
$(".if_pucEntry").hide();
}
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!-- checkbox -->
<label class="led-label"><input type="checkbox" class="led-btn" name="selpoNO" id="selpoNO" style="width: auto !important;"><strong>Checkbox</strong></label>
<!-- text box -->
<input type="text" class="form-control if_pucEntry" id="" name="" style="display: none;">

ASP.NET MVC 4 Having trouble selecting the correct input element using js/jquery

I'm working with a form that I've built using HTML helpers, like so:
#using (Html.BeginForm("CreateNewRoom", "Admin", FormMethod.Post, new { #enctype = "multipart/form-data", #class = "form-vertical", style = "margin-top:-30px" }))
{
<fieldset>
<legend>Create a new Room</legend>
<div class="form-toolbar sepa">
<label class="darkBlue toolbar-label">Room name:</label>
<div>
#Html.TextBoxFor(x => x.name, new { #id = "room", #class = "form-control-static", #style = "margin-bottom:5px;", #autofocus = "true" })
</div>
</div>
...
<div class="form-toolbar sepa">
<label class="darkBlue toolbar-label">Display:</label>
<div>
#Html.RadioButtonFor(x => x.display, "True") Yes&nbsp
#Html.RadioButtonFor(x => x.display, "False") No&nbsp
</div>
...
Just in case it helps, Chrome DevTools informs me that this is the HTML being displayed by my radio button helper:
<input id="display" name="display" type="radio" value="True">
" Yes "
<input checked="checked" id="display" name="display" type="radio" value="False">
" No "
I have a Javascript file which dynamically plays with the elements on the page. I would like for js to add a class to an element on the page, to simply change its opacity to 0.5, if it finds that the "No" radio button is selected.
if ($('input[name="display"]').val() === "False") {
if (($('#selectedRoomName')) && ($('#selectedRoomArrow')))
$('#selectedRoomName').addClass('obscured');
$('#selectedRoomArrow').addClass('obscured');
} else {
//sad
}
I've used this block in $(document).ready and in $(input[name="display"]).change, to make js add the class if display is 'False' when the page loads, and also to add it if the 'False' radio button is selected.
However, I found that js had a problem reading the value of the input, and thinks that it is true almost all the time, even if the false button is selected. I'm assuming it must be something to do with the fact that the two inputs have the same name?
My question is, how do I ensure that js always picks the input that is currently selected, and will this make sure that it reads the correct value?
UPDATE: I have changed $('input[name="display"] to $('input[name="display"][checked="checked"]'), in an effort to make js home in on whichever radio button is checked. This solves the problem where js did not know which button was checked on page load. However, now it always targets whichever button was checked on page load, instead of the button that's actually checked currently.
Apologies if I seem like a complete novice, but I am, so...
Any and all suggestions on how to better organise my code are welcome; I'm very unfamiliar with best practises in web dev.
Thanks!
Just change the following line in your change event from :
if ($('input[name="display"]').val() === "False") {
to:
if ($(this).val() === "False") {
This will check for the value of the radio button that is current clicked.
I used a combination of #Ehsan's answer, and a partial solution that I included in an update to the the question.
I decided to place the js block in a function, which took the element whose value I was checking as a parameter:
function checkdisplay(dispradio) {
var a = document.getElementById("selectedRoomName");
var b = document.getElementById("selectedRoomArrow");
if ((a) && (b)) {
if (dispradio.val() === "False") {
a.className += " obscured";
b.className += " obscured";
} else {
a.className = a.className.replace(/(?:^|\s)obscured(?!\S)/g, '');
b.className = b.className.replace(/(?:^|\s)obscured(?!\S)/g, '');
}
}
}
Then I called the function in two places like so:
//first for document load
$(document).ready(function () {
checkdisplay($('input[name="display"][checked="checked"]'));
});
and
//and then for change events
$('input[name="display"]').change(function () {
checkdisplay($(this));
});
Seems like [checked="checked"] was needed to let js know which button was checked on page load, but that then needed to be removed for onchange events, or js would target whichever button was checked on page load, rather than the button that was actually checked!
Many thanks for your partial solution #Ehsan, it helped me come up with this one!

Changing input box also need to find check box checked or not in JS

I have input box along with checkbox in table <td> like below,
<td>
<input class="Comment" type="text" data-db="comment" data-id="{{uid}}"/>
<input type="checkbox" id="summary" title="Check to set as Summary" />
</td>
Based on check box only the content of input box will be stored in DB.
In the JS file, I tried like
var updateComment = function( eventData )
{
var target = eventData.target;
var dbColumn = $(target).attr('data-db');
var api = $('#api').val();
var newValue = $(target).val();
var rowID = $(target).attr('data-id');
var summary = $('#summary').is(':checked');
params = { "function":"updatecomments", "id": rowID, "summary": summary };
params[dbColumn] = newValue;
jQuery.post( api, params);
};
$('.Comment').change(updateComment);
But the var summary always returning false.
I tried so many ways prop('checked'),(#summary:checked).val() all are returning false only.
How to solve this problem?
Looks like you have multiple rows of checkboxes + input fields in your table. So doing $('#summary').is(':checked') will return the value of first matching element since id in a DOM should be unique.
So, modify your code like this:
<td>
<input class="Comment" type="text" data-db="comment" data-id="{{uid}}"/>
<input type="checkbox" class="summary" title="Check to set as Summary" />
</td>
And, instead of $('#summary').is(':checked'); you can write like this:
var summary = $(target).parent().find(".summary").is(':checked');
By doing this, we are making sure that we are checking the value of checkbox with the selected input field only.
Update: For listening on both the conditions i.e. when when checking checkbox first and then typing input box and when first typing input box and then checked:
Register the change event for checkbox:
// Whenever user changes any checkbox
$(".summary").change(function() {
// Trigger the "change" event in sibling input element
$(this).parent().find(".Comment").trigger("change");
});
You have missed the jQuery function --> $
$('#summary').is(':checked')
('#summary') is a string wrapped in Parentheses. $ is an alias for the jQuery function, so $('#summary') is calling jquery with the selector as a parameter.
My experience is that attr() always works.
var chk_summary = false;
var summary = $("#summary").attr('checked');
if ( summary === 'checked') {
chk_summary = true;
}
and then use value chk_summary
Change all the occurrences of
eventData
To
event
because event object has a property named target.
And you should have to know change event fires when you leave your target element. So, if checkbox is checked first then put some text in the input text and apply a blur on it, the it will produce true.
Use like this
var summary = $('#summary').prop('checked');
The prop() method gets the property value
For more details, please visit below link.
https://stackoverflow.com/a/6170016/2240375

Custom Binding for initial form display

I have a ViewModel which accepts JSON to build observableArray() and also have a selected observable for storing the object when editing.
var ViewModel = function (data) {
var self = this;
self.list = ko.observableArray(data);
self.selected = ko.observable();
}
I'm showing the list in a table with edit button. On edit, the selected object goes into selected
self.edit = function (o) {
self.selected = ko.observable(o);
}
Next, I have a form which binds with the selected and displays all the properties.
<form>
<input type="text" data-bind="value: selected().Name">
</form>
The problem is that I want this form to be shown for adding an item and not only when the user clicks edit. But initially, the selected observable is undefined and throws error. Also, I want to push the data in selected to my observableArray when the user clicks on Add button.
What will be the best approach? Where can I put a custom binding so that this scenario works?
Update
My problem is similar this question.
But I can't implement the given solution to an observable
Fiddle implementing a part of problem and a suggested solution
<form data-bind="with: selected">
<input type="text" data-bind="value: Name">
</form>
This will not render the content of the form until a item is selected
To reuse form use a template binding
<form data-bind="template: { if: selected, data: selected, name: 'my-template' }">
</form>
Use a "blank" item in the selected variable to act as your "new item". Then, filling in the form for adding an item will fill in the values in the "blank" item.
Here are some snippets (partially based on your code and on another question)
Form:
<form data-bind="with: selected">
<input type="text" data-bind="value: Name">
</form>
ViewModel
var ViewModel = function (data) {
var self = this;
self.list = ko.observableArray(data);
self.selected = ko.observable(
{ name: "" });
}
Then, the "add" button for putting a new item into the data array can contain this:
// Add this item
self.list.push(self.selected());
// Reset the form to a new blank item
self.selected({ name: "" });
So when the form is loaded, there is an item in the selected variable - a blank item.
To avoid having to create a new blank model with all of the fields, you can use a variation on Ryan Niemeyer's answer here, using a valueWithInit binding for nonexistent fields. See his example jsFiddle.
Then, you can do this:
Form:
<form data-bind="with: selected">
<input type="text" data-bind="valueWithInit: Name">
</form>
ViewModel
var ViewModel = function (data) {
var self = this;
self.list = ko.observableArray(data);
self.selected = ko.observable({});
}
Seems like just simple javascript was the answer to the question.
I added one Cancel button and on click of it:
document.getElementById("form").reset();
This also cleared the selected observable.
Also, for initial form display I used $data with all the properties:
<form data-bind="with: selected" id="form">
<input type="text" data-bind="value: $data.Name">
</form>
No custom binding handlers were required!!

checkbox array in knockout being all checked on click

I am having an issue with knockout,
when i check a box, they're all being checked...
this is what I have:
_Categories_List has all the items,
and
My_categories is the empty list where I want to have each id added
this is the code:
<!-- ko foreach: _Categories_List -->
<input type="checkbox" data-bind="attr: {value: $data}, checked: $root.My_categories" />
<span data-bind="text: CODE"></span><br />
<!-- /ko -->
and this is the JS part of the code (i cant really change this as to how the code is in the documentation because i'm building off someone else's work and should use the same code - referring to the mapping.fromJS):
var Poi = new Object();
Poi.My_categories = [];
var _Poi = ko.mapping.fromJS(Poi);
var Categories_List = [];
var _Categories_List = ko.mapping.fromJS(Categories_List);
$(document).ready
(
function () {
ko.applyBindings(_Poi);
// here there's an ajax function to load the categories returned in i_Input.My_Result, then:
ko.mapping.fromJS(i_Input.My_Result, _Categories_List);
}
);
this is what the object loaded from ajax looks like:
{"My_Result":[
{"CODE":"chalet","DEF_POIS_CATEGORY_ID":2,"DESCRIPTION":"chalet","ENTRY_DATE":"2012-10-10","ENTRY_USER_ID":2,"OWNER_ID":1},
{"CODE":"vila","DEF_POIS_CATEGORY_ID":3,"DESCRIPTION":"villa","ENTRY_DATE":"2012-10-10","ENTRY_USER_ID":2,"OWNER_ID":1}
]}
The checked binding works off of the value of the input element. In your code, you are setting the value equal to an object, which turns into [object Object], so both of your inputs have the same value, which is why checking one toggles both.
So, you would want to set your value equal to a key on the object like your CODE property. If necessary, then you can use a computed observable to represent the actual objects.
Here is a sample: http://jsfiddle.net/rniemeyer/7n9gR/

Categories

Resources