Knockout binding not working incase of inline editing - javascript

I was trying to create an inline editing with knockout.
I created both 'span' and 'input' for same field.
On click of span I hide the span and 'show' the 'input'.
But the change in the input is not reflection on the span.
My Html field
<td>
<span data-bind="text: name" style="display: inline;">Furious Lizard</span>
<input data-bind="value: name" style="display: none;" type="text">
</td>
My code for inline
$('td').on('click', function () {
var spanElement = $(this).find('span');
$(this).find('span').hide();
$(this).find('input').show().select().on('blur', function () {
$(this).hide();
spanElement.show();
});
});
Why isn't the binding working?
JSFiddle

I believe the reason is that eventhough you are binding to an observableArray, the properties on your objects are not themselves observable, so when the property is altered other bound elements aren't notified of the change.
I have edited your sample:
http://jsfiddle.net/879Pk/3/
There you can see that the first element in your data, instead of just being standard properties, they are observable as well:
{
name: ko.observable("Well-Travelled Kitten"),
model: ko.observable(352),
price: 75.95
}
NOTE: I didn't modify the price since you use it below for calculations. For that to work you'd have to modify all prices to be observable, and then while computing actually call the observable (using parenthesis) in order to get the actual value.
In order to avoid having to manually create the observables for each property, Knockout has a plugin called "Mapping" (http://knockoutjs.com/documentation/plugins-mapping.html) which does exactly that, using the following syntax:
var viewModel = ko.mapping.fromJS(data);
Now, regarding your second JSFiddle, I have just made a few corrections:
http://jsfiddle.net/879Pk/5/
When you were adding the element the properties on the new one weren't observable, and you were also missing the parenthesis when evaluating the price property.

you want that the data writen in the input to be visible in the span element as text?
$(this).find('span').html($(this).find('input').val());

Related

How to click a HTML element with javascript

I need to use javascript in order to click an element from element collection. As seen the code has C# but also as seen I need to use a javascript command as I do.
I am looking for this javascript code.
This element doesn't have an ID or name. Otherwise, I could have used the ID but that doesn't work in this case. How would it be possible to use the iterating elements in order to click with javascript"?
The problems are:
1. First I need to click this input/textbox to make it possible to edit.
2. Now when the input is editable. I need to put a number value to the textbox.
foreach (Gecko.GeckoHtmlElement elements in wb1.Document.GetElementsByTagName("input"))
{
if (elements != null)
{
if (elements.OuterHtml.Contains("thisstring"))
{
//This element doesn't have an ID or name. "how to use elements in order to click with javascript"?
//1. First I need to click this input/textbox to make it possilbe to edit
//2. Now when the input is editable. I need to put a value to the textbox.
webbrowser.Navigate("javascript:void(document.getElementById('someID').click())");
}
}
}
The HTML surrounding the element I want to click is below. You can see the input there:
<td class="date-cell" cm-inventory-grid-copy-action-focus data-header-date-index="0" data-cm-inventory-grid-copy-action-focus-type='availability' ng-class="{ 'zero': roomTypeDatesByRoomTypeId[roomType.id][headerDates[0].fullDate].availability <= 0, weekend: headerDates[0].weekend, 'dirty': roomTypeDatesByRoomTypeId[roomType.id][headerDates[0].fullDate].availabilityChanged, 'copy-focused': roomTypeDatesByRoomTypeId[roomType.id][headerDates[0].fullDate].copyFocused && roomTypeDatesByRoomTypeId[roomType.id][headerDates[0].fullDate].copyFocusType == 'availability' }" ng-form="cellForm">
<input name="rtd-availability" type="number" onclick="this.select()" ng-model="roomTypeDatesByRoomTypeId[roomType.id][headerDates[0].fullDate].availability" ng-change="handleRoomTypeDateChange(roomTypeDatesByRoomTypeId[roomType.id][headerDates[0].fullDate])" pattern="\d+" ng-disabled="::!allowAvailabilityEdit" required sm-no-scroll cm-inventory-grid-date-cell-validator/>
</td>
To get html element in javascript you can use "document.querySelector('input')" or if there are many input elements you can use "document.querySelectorAll('input')".
querySelector returns HTML node element and querySelectorAll returns array of elements.
By using document.qerySelector you can identify the correct input quite easily since you can use attributes and the html structure as criteria.
//select an input that is a child of a td with class date-cell who's name is rtd-availability
let input = document.querySelector('td.date-cell > input[name=rtd-availability]')
//no need to call .click(), you can use .select() directly
input.select();
input.value = 42;
javascript:(() => {let in = document.querySelector('td.date-cell > input[name=rtd-availability]'; in.select(); in.value = 42;})();

how to set visible = false by knockout

I have posted more detailed question this is much clear and straightforward
here
hello, I am trying to set the value of list element to false by using knockout
this is my HTML
<li>
<a onclick="log(this)" data-bind="visible: true, text: $data"></a>
</li>
is there a way to say something like this :
myViewModel.items()[i].setVisible(false);
Don't set the visible binding to true set it to the variable you define in your viewModel. Also you can access individual elements of an observable array through the foreach binding. Lastly, if you want to use $data you can access the property of the individual array object directly using the "." operator. The documentation I referenced at the end of my post has more information. See below:
<div data-bind="foreach: shouldShowMessageArray">
<div data-bind="visible: $data.shouldShowMessage">
Message goes here.
</div>
</div>
<script type="text/javascript">
var myViewModel;
$(document).ready(function(){
myViewModel = new viewModel();
function viewModel() {
this.shouldShowMessage = ko.observable(false) // Message initially visible
this.shouldShowMessageArray = ko.observableArray([
{shouldShowMessage: ko.observable(true)},
{shouldShowMessage: ko.observable(false)}
]);
}
ko.applyBindings(myViewModel);
});
</script>
Knockout foreach / $data documentation
If you are looking to load the page with a starting value of visible:false, you may run into some issues with the "visible" binding.
I have had trouble with the "visible" binding when I want an element to be hidden on page load, and then made visible after some action taken by the user. You will see a flash of the hidden element while your knockout.js logic loads if you are using the visible binding. If you try to use inline CSS to set display:none on the element, then it will never become visible even when the KO logic results in a visible:true value. This is because KO applies the CSS rules after it has handled changing the element.style.display value.
One solution to this issue that I have found is to set up a CSS class that well set display to none, and then use a data binding to conditionally apply that CSS rule based on an observable.
CSS:
.hideMe { display: none; }
HTML:
<div class="hideContent" data-bind="css: { hideContent: !yourObservable() }">
<p>Content</p>
</div>

Chained method calls doesn't work on original nor cloned element? [duplicate]

This question already has answers here:
Chained method calls doesn't work on original nor cloned element, why?
(2 answers)
Closed 6 years ago.
I have the following HTML:
<input type="text" id="condition_value_1" style="display: none" />
<button id="showme">Make Select2</button>
<button id="clickme">Make Input</button>
Then take a look to the following jQuery:
$(function() {
var cond1 = $('#condition_value_1');
var cloned_cond1 = cond1.clone();
var cond1_select = '<select name="condition_value_1" id="condition_value_1" multiple="multiple"><option></option><option value="1">Opt1</option><option value="2">Opt2</option></select>';
$('#showme').click(function() {
cond1.removeAttr('style').replaceWith(cond1_select).select2({
placeholder: 'Select choice'
});
});
$('#clickme').click(function() {
if ($('#condition_value_1').hasClass('select2-hidden-accessible')) {
$("#condition_value_1").select2('destroy');
}
$('#condition_value_1').replaceWith(cloned_cond1).removeAttr('style');
});
});
You can try the code above here.
Now as soon as you click on #showme you should remove the attr style, replace the original element with the given one and turn it into a Select2, the last part isn't working.
In the other side if you click on #clickme you should destroy the previous Select2 replace the #condition_value_1 with the cloned element and remove the attr style because the cloned has that attribute but this is not working either.
The idea is to switch between elements and turn on/off properties on demand.
Maybe I am missing something here but I am not sure what. Could any help me here?
Note: I've deleted my previous post to avoid confusions, apologies about that!
The problem is because replaceWith() returns the original jQuery object which now contains no elements as you replaced them.
In your logic structure this means you can't chain from those elements and need to start calls on the appended elements, like this:
var $cond1 = $('#condition_value_1');
var $cloned_cond1 = cond1.clone();
var cond1_select = '<select name="condition_value_1" id="condition_value_1" multiple="multiple"><option></option><option value="1">Opt1</option><option value="2">Opt2</option></select>';
$('#showme').click(function() {
$cond1.replaceWith(cond1_select);
$('#condition_value_1').select2({
placeholder: 'Select choice'
});
});
$('#clickme').click(function() {
if ($('#condition_value_1').hasClass('select2-hidden-accessible')) {
$("#condition_value_1").select2('destroy');
}
$('#condition_value_1').replaceWith($cloned_cond1);
$cloned_cond1.removeAttr('style');
});
If you do the following:
$("#div").replaceWith(".item2")
The object returned by the replaceWith method is the original set of objects. This because they might be replaced, but they still exists. Maybe not in the DOM but outside of it. Therefor you might want to do something else with it after replacement.
Therefor you need to make a seperate Javascript call where you select the right element and call the removeAttr and select2 function.
The .replaceWith() method, like most jQuery methods, returns the jQuery object so that other methods can be chained onto it. However, it must be noted that the original jQuery object is returned. This object refers to the element that has been removed from the DOM, not the new element that has replaced it.
http://api.jquery.com/replacewith/

Use jQuery to set Focus on input before span

I have the following bootstrap html:
<div class="input-group">
<input id="dbTest" class="input-sm input-s datepicker-input form-control dirty" type="text" data-bind="datepicker:DOB" data-date-format="dd-mm-yyyy" readonly="readonly">
<span class="input-group-addon" id="dp3"><i class="fa fa-calendar"></i></span>
</div>
The data-bind is a knockout extension which is all working well and when the focus is on the input all the datepicker works. I created a test for this like so:
$("#dp3").click(function () {
$("#dbTest").focus();
});
What I want to achieve though is the ability to create a global function for the addon button for any other datepickers I create so that I don't have to add ids and a function for every datepicker I create. For example I would want to add say a class called datepicker-addon:
<span class="input-group-addon datepicker-addon" id="dp3"><i class="fa fa-calendar"></i></span>
And then do something like:
$(".datepicker-addon").each(function() {
$(relevant input).Focus();
});
Any ideas on how I could get the relevant input element?
Not sure I fully understand, but given your markup if you are trying to focus on the input without id's etc you could use
$(".datepicker-addon").each(function() {
$(this).parent().find('input').Focus();
})
N.B. as someone mentioned, you might of meant click() in your question rather then each(), in which case
$(".datepicker-addon").on('click', function() {
$(this).parent().find('input').Focus();
})
is what you'd want.
if the span and input are next to each other I would use jquery .prev() function instead of parent then find.
http://api.jquery.com/prev/
$(".datepicker-addon").each(function() {
$(this).prev().Focus();
})
.prev looks at the html elemnet immediatly before the current element.
.prev vs .parrent matters solely on your html structure. Is the input always right before the datepicker? Is there always only one input inside the parent element of the datepicker
The former would seem a much better constraint than the latter from an outsiders perspective.
Assuming that your input tag:
always has class 'datepicker-input'
always comes before span with addon button
you can use following code:
$('.input-group-addon').click(function(){
$(this).prev('.datepicker-input').focus();
});

toggleClass() on Parent not working even though the parent is being found

Hi I have the following HTML repeated in my page (obviously the names, for and id attributes change in each instance):
<div class="funkyCheckBox">
<label for="uniqueName"> Whatever Text </label>
<input type="checkbox" name="uniqueName" id="uniqueName" />
</div>
What this does with some CSS is make the give the appearance of a big button, the input is hidden and I add a class to the div depending on the checked value of the input. I use the following JavaScript /jQuery for this
$(".funkyCheckBox").live("click, tap", function(event){
$(this).toggleClass("funkyCheckBoxActive");
var nextCheckBox = $(this).find("input[type=checkbox]");
nextCheckBox.prop("checked", !nextCheckBox.prop("checked"));
});
Now this was all fine and good but during testing I noticed that if you click on the label text the class was not applied and the value of the input isn't toggled... thus I added the following...
$(".funkyCheckBox label").live("click, tap", function(event){
$(this).parent("div").toggleClass("funkyCheckBoxActive");
var nextCheckBox = $(this).next("input[type=checkbox]");
nextCheckBox.prop("checked", !nextCheckBox.prop("checked"));
});
Now this is great as clicking the label text now changes the value of the input however the parent DIV is not taking / toggling the "funkyCheckBoxActive" class. I am unsure why is as I then used console.log($(this).parent("div")) within the callback function and I am outputting the attributes of th dom object. Does anyone know why my toggleClass is not being applied?
Depending on the version of jQuery, your code will work or not.
Note that the browser is already toggling the checkbox when you click on a label that references it; so you would only need to do this:
$('#uniqueName').change(function() {
$(this).parents("div").toggleClass("funkyCheckBoxActive");
});
please use the "on" method instead of "live" as it is deprecated. also the "for" attribute in LABEL Tag points to an existing Id.
here is the corrected and working code:
<div class="funkyCheckBox">
<label for="uniqueName"> Whatever Text </label>
<input type="checkbox" name="uniqueName" id="uniqueName" />
</div>
and
$(".funkyCheckBox label").click(function(event){
$(this).parent("div").toggleClass("funkyCheckBoxActive");
var nextCheckBox = $(this).next("input[type=checkbox]");
var nextCheckBoxValue = nextCheckBox.val();
nextCheckBox.val(! nextCheckBoxValue);
}); ​
​
EDIT: here is the jsFiddle link
http://jsfiddle.net/RUYWT/
EDIT2: #Mike Sav: I have revised your code and it's working now with all possible cases:
http://jsfiddle.net/RUYWT/11/

Categories