Knockout.js multiple observable arrays not working? - javascript

Evening all.
I have been experimenting for the first time with Knockout.js and i am having issues with multiple knockout arrays in the same page.
http://jsfiddle.net/573Vc/
e.g.
var linksBinding = ko.applyBindings(new LinksViewModel());
var tasksBinding = ko.applyBindings(new TasksViewModel());
The above jsfiddle shows how the top "Tasks" are working great and if i remove all code / html that relate to tasks then the links will also work fine on their own. When i add them to the same page however the second one starts to fail. Can anyone shed any light? It complains that parameters are not defined when i know they are?
Thanks

I'm not a knockout expert. However, I believe that you can only bind once to a specific element. The document is the default element if none is specified in the binding. So, in your case, you're doing two bindings (i.e applyBindings()) but you don't specify an element so only one binding takes place.
Check out this fiddle (note, I added jquery so that I could use jquery to access the element.)
I simply added an id to each div and updated the bindings to specifically bind to each element.
<div id="tasks" class='app-panel-section' data-bind="foreach: taskCategories">
...
var tasksBinding = ko.applyBindings(new TasksViewModel(),$("#tasks")[0]);

Related

Initialize dynamically added select2

I've added select2 dynamically, but it won't initialize. I tried with following code (found on stackoverflow itself) but it wont work, please check my fiddle for code. Suggest me what I missed in code.
Thanks in advance
Check this
function initializeSelect2(selectElementObj) {
selectElementObj.select2({
tags: true
});
}
$(".select-to-select2").each(function() {
initializeSelect2($(this));
});
Some issues with the example provided:
select2 was already being applied to all .select2 dropdowns without any options with this line: $('.select2').select2();
$(".select-to-select2").each does nothing, because there are no dropdowns with the class select-to-select2. (Also, there's no need for the each, you can just pass $(".select-to-select2") to initializeSelect2.)
The string being appended to wrapper was not valid HTML. That last "remove" link ought to be within the wrapping .row div.
ids must be unique to the page. After adding more of the same exact dropdowns, they had the same ID/names as previous ones, so this would cause unpredictable behavior (old dropdowns could lose their select2 and only the new ones would get it). E.g. you can't have more than one element with id="pgm_que_ans1", the next one has to be different.
in this call, initializeSelect2(wrapper), wrapper does not specify which dropdowns to apply select 2 on: , it's better to save the new HTML to a variable (e.g. var $newSelects('html here');, and once that is added to the DOM, pass only the .select2s within that to initializeSelect2, e.g. wrapper.append($newSelects); initializeSelect2($newSelects.find('.select2'))
Here's a demo with all that thrown together:
http://jsfiddle.net/qw3y821g/1/

jQuery caches elements selected?

So I'm using this code to:
$('.something').on('click', function () {
console.log($(this).data('id'));
}
And for some reason, if I modify the data-id using the inspector, jQuery still sees the id that was there in the beginning. However, I tried the same thing using JS and it does see the changes. This makes me wondering if jQuery caches in some way the elements selected and uses them instead of the actual DOM.
Can someone please explain what happens and how jQuery does the event binding in the background?
Later edit: I want to specify that I'm talking about the "data-" attribute that I put in the HTML, not about the '.data()' provided by jQuery. Not sure if it's the same thing.
jQuery caches elements selected?
No. But the data managed by data is stored in an object cache maintained by jQuery, keyed by a unique identifier jQuery adds to the element (so it can look up the data). data is only initialized from data-* attributes, it is not an accessor for them. It's both more and less than that.
If you're interested, you can see that as an "expando" property on the element instance, it'll start with "jquery" and have a long number attached to it (currently; it's undocumented — for good reason — so this may change):
var foo = $("#foo");
console.log(foo.data("info")); // hi there
console.log("Expando name: " + Object.getOwnPropertyNames(foo[0]).find(name => name.startsWith("jQuery")));
<div id="foo" data-info="hi there"></div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

jQuery events not firing after "cleaning" Knockout bindings

In my project, I have an <div> where I specifically apply my Knockout.js bindings. I have to instantiate different viewmodels in that area depending on what the user clicks.
To prevent getting a cannot call bindings twice on the same element error, I first have to "Clean" the bindings to make the area available again. I call the initial applyBindings() function:
ko.applyBindings(new ViewModel("Planet", "Earth"), document.getElementById("bindings-area"));
Eventually, I will clean the <div> and call the new bindings:
var element = $("#bindings-area")[0];
ko.cleanNode(element);
ko.applyBindings(new ViewModel("NEW", "Bindings"), document.getElementById("bindings-area"));
Problem: When I include an HTML button in the #bindings-area div, it will no longer work after I clean the bindings and instantiate the new model. I'm sure it has to do with the ko.cleanNode() function somehow removing the button bindings as well. How can I re-initiate them or prevent cleanNode() from operating on the button in the first place?
Fiddle: http://jsfiddle.net/jL6L01xs/2/
This issue is nicely described in Knockout documentation. This quote describes what the issue is and what needs to be done:
When removing an element, Knockout runs logic to clean up any data
associated with the element. As part of this logic, Knockout calls
jQuery’s cleanData method if jQuery is loaded in your page. In
advanced scenarios, you may want to prevent or customize how this data
is removed in your application. Knockout exposes a function,
ko.utils.domNodeDisposal.cleanExternalData(node), that can be
overridden to support custom logic. For example, to prevent cleanData
from being called, an empty function could be used to replace the
standard cleanExternalData implementation:
ko.utils.domNodeDisposal.cleanExternalData = function () {
// Do nothing. Now any jQuery data associated with elements will
// not be cleaned up when the elements are removed from the DOM.
};
Here is the updated jsFiddle.

Using .jquery .on() without an event

I have input elements that get appended to my HTML document that I need to get the value of. I understand that .on() should be used to get appended elements, but .on() expects an event to activate it, and I don't need an event.
Below is the code. #save_design is on a different part of the page. #fields_cnt is the parent that holds all my appended forms. .reg_field is the element I'm trying to get the value of. The first child works because that form is present when the document is loaded, or in other words, it is not appended. The other children all returns values undefined, or false depending on the input type and the logic I'm applying to it. This leads me to believe that .on() or something close to it should be used.
To clarify, I am trying to get the value of an appended input when #save_design is clicked.
$('#save_design').click( function() {
group_text[form_count] = $('#fields_cnt .field_group:nth-child(' + child + ') .reg_field').val();
I have tried replacing the originating click event as so. It has not worked.
$('body').on('click', '#save_design', function(){
I have tried using .on() without an event or event object. It has also not worked.
$('body').on('', '', function(){
group_text[form_count] = $('#fields_cnt .field_group:nth-child(' + child + ') .reg_field').val();
});
I have found a plugin that should work.
JQuery selecting dynamically generated html without adding event (with live)
This is not ideal, because as stated in the github readme, it is being rewritten. Also, the SO answer is 3 years old now, so it may no longer be the correct answer.
https://github.com/brandonaaron/livequery
I'd like to find a more direct solution. Is there a way to do this natively in jQuery? Is there a way to use .on() without an event, and if so, what's the appropriate syntax?
UPDATE: Clarification: The input fields get appended when an add field button is pressed. Their values change after they are appended. I would prefer not to rerecord the value of an input, every time it is altered.
UPDATE Here is the HTML structure to give a better idea of how I'm defining child.
<div id="fields_cnt">
<div id="field_group_1" class="field_group form-group">
<input placeholder="Field" class="reg_field form-control" />
</div>
<!-- This is appended by jQuery. There may be multiple of these -->
<div id="field_group_2" class="field_group form-group">
<input placeholder="Field" class="reg_field form-control" />
</div>
</div>
I have found a great solution to my problem. It takes a different approach on selecting the element, but ultimately works and results in drier code.
Here, I use each, to grab each instance of the appended element I am trying to select. This returns the desired value.
$('.reg_field').each( function() {
group_text[form_count] = $(this).val();
form_count++;
});
I suspect the issue was my previous selector was unable to use $(this), which seems to work in more cases for appended elements. Another possibility that was brought up in the comments was the use of nth-child in the middle of a selection. I am not sure on these points, and would appreciate a better explanation. Hopefully this will help for someone who faces the same issue. Also, I appreciate the help on getting me to focus on the selection rather than the binding.

Jquery remove function follow up

I submitted this question last week:
chrome not working with jquery remove
and was able to resolve it (stupidity on my part really), however my example was very simple. Currently I'm trying to use .remove to eliminate a complete div from a page before sending an array of inputs to an ajax function. However, I am not able to get .remove to work at all.
Here's my latest try:
http://jsfiddle.net/CJ2r9/2/
I get function not defined on the jsfiddle on multiple browsers. On my application I get absolutely no errors, but nothing works either.
I'm relatively new to javascript scopes, so if the problem is scope-wise then please let me know how I'm screwing up.
I have also tried using the .on jquery function, but it's a bit more confusing considering my div ids are dynamically loaded from the server (jstl, spring MVC, etc). If that's a solution please let me know how I can get on the right track.
Thank you!
The two problems in your jsFiddle are:
Scope: removeElem is not in global scope, since you left the default configuration option to execute the code on DOM ready. You can change it to "no wrap" to make the funciton global.
The elements you want to remove don't exist. The div elements have IDs like "removeXXXp" and in your event handlers you pass "removeXXXs".
Here is an other, simpler solution (in my opinion) for element removal. Given your markup:
<div class="scheduleSet" id="remove315p">
<!-- ... -->
Remove
</div>
You can use .on like so:
$('.schduleSet a.optionHide').on('click', function() {
// traverses up the DOM tree and finds the enclosing .schduleSet element
$(this).closest('.scheduleSet').remove();
});
You don't even need IDs at all.
I made a simple fiddle, the inline onclick doesn't see the function defined in javascript so I get a ReferenceError: myRemove is not defined.
By adding the listener in js, .remove() works fine.
Sorry I don't know what causes the difference in behavior though.
Test it out: http://jsfiddle.net/xTv5M/1/
// HTML5
<div id="removeme">foo bar</div>
<button onclick="myRemove('removeme')">Go</button><br>
<div id="removeMe2">foo bar</div>
<button id="go2">Go Again</button>
// js
function myRemove(name){
$('#'+name).remove()
};
$('#go2').click(function(){ myRemove('removeMe2') });
I see that you are already using jquery. Why dont you do it this way:
<div id="foo">This needs to be removed</div>
Remove
function removeElem(element){
$('#'+element).remove();
}
$(function(){
$("#remove").click(function(){
removeElem($(this).data('remove'));
});
})
Fiddle here: http://jsfiddle.net/vLgpk/
They way this works is, using data-remove (can be anything like data-xyz btw), binds the remove link with the div. You can then read this binding later when remove is clicked.
If you are new to jQuery, and wondering what data-remove is, its just custom attribute that you can add to you code which can be later retrieved using the data() call on the element. Many great frameworks like Bootstrap use this approach.
Advantage of using this approach in my opinion is you can have the remove links anywhere in your UI and they don't need to be related structurally to your divs by siting inside them.

Categories