I've faced problem with binding modified at runtime viewmodel through prototype to particular html elements.
I've added an array to model, but after applying bindings - nothing happen. Added observables working fine, but problem exactly with arrays.
Here is a jsfiddle with code: click
var ViewModel = function() {
var self = this;
this.selectedItems = [{FullName: 'Mike'}]
};
var model = new ViewModel();
ko.applyBindings(model,document.getElementById('node'));
ko.cleanNode(document.getElementById('node'));
model.__proto__.items = [{FullName: 'Michael'}];
ko.applyBindings(model, document.getElementById('node'));
Thx for advice.
The problem is that ko.cleanNode is not working as you think it is. It is not removing all of the bindings as you think it is.
I would recommend completely removing your node div, and then cloning and re-adding it. Naturally jQuery makes this easy, if you happen to be using that. Otherwise I would back up a bit and really evaluate whether you need to add things to a viewModel's prototype and then re-bind to your div. Are you sure an if binding won't do what you want?
Also, consider testing this code with knockout 3 - I'm confident you'll get an error about applying bindings multiple times.
Related
I have searched prior SO posts here, here and here, and couldn't an answer that made sense to me. This should be a basic question, but I'm not understanding the posts I find. They don't seem to address using a this parameter.
I want to programatically add an input with an onchange event, such that the final result is this:
<input type="button" onchange="handleButtonOnChange(this)">ClickMe</input>
I am working on a project that is using an embedded IE6 browser inside a old Delphi application, so I have to have a solution that is IE6 compatible (yes, IE6 is horrible, but there are reasons I am stuck with it for now).
My initial attempt was this:
var DaySelect = document.createElement("select");
DaySelect.id = ParentID+"-day";
DaySelect.disabled = true;
MonthSelect.onchange="handleDayChange(this);" //<--- not correct
Parent.appendChild(DaySelect);
I then read that the .onchange should be assigned an object, not a string, and one should use this instead:
MonthSelect.onchange=handleDayChange; //<--- '(this)' removed
But it seem to me that this will result in this element (notice the missing this parameter)
<input type="button" onchange="handleButtonOnChange">ClickMe</input>
If I use the line below, instead, won't this make a closure, and the 'this' will refer to the event at the time the object is assigned to the .onchange property, instead of being the event at the time of the change event?
//Does the line below make a closure?
MonthSelect.onchange=handleDayChange(this); //<-- What does 'this' refer to?
I'm a relatively new web programmer, but long time Delphi programmer. Closures still make my head hurt. I appreciate any help in this.
Also, I read here about using addEventListener and the problems with older versions of IE, and the last post on the page provides a work around. But I don't understand how it works.
EDIT -- And what about passing other parameters? It seems that many event handlers will need to have parameters specific for the attached element. It seems that it is just not possible to add a listener with any parameters.
A simple closure if you are creating the elements in JS as you show:
var DaySelect = document.createElement("select");
DaySelect.id = ParentID+"-day";
DaySelect.disabled = true;
MonthSelect.onchange=function(){handleDayChange(DaySelect);};
Parent.appendChild(DaySelect);
Since the function is created inside the scope that you create the element in, the same variables will be available to it.
EDIT:
Additional parameters can be passed with this method, for example, the anonymous function we create and attach as the handler will still have the event object sent to it:
function(e){handleDayChange(DaySelect, e);};
In the event object you will have access to the event target, but in your example the event target and "this" are not the same element, so there would be no way for the handler to know about the DaySelect element.
jQuery makes a lot of event handling much simpler which is one of the reasons many people use it, it also normalizes it's methods between various browsers so you don't have to write multiple versions of the same code (in most cases)
Take the following fiddle:
fiddle
Now in this code you will see the following line:
newList.forEach(function(y){
activeUsers.push(y);
});
This works perfectly fine. however change the code to:
activeUsers = newList;
And the code no longer works.
Can anyone tell me why this is happening and how i might fix it so the two way binding works?
That doesn't work because you change the reference. Your old $scope.activeUsers still points to the same old empty array.
After you change the reference, you'll have to get the new reference back from the factory. That's why changing the reference will cause you issues.
If you take the reference from the factory, like you do, then you'll have to keep working on that same array if you want all the bindings to work as you expect.
$scope.activeUsers = testFactory.getActiveUsers();
You assign the array ref to $scope.activeUsers. so if you push some element to activeUsers, $scope.activeUsers will change also.
activeUsers = newList;
Then if you do this, activeUsers point to another array ref, so at this time, $scope.activeUsers is not same as activeUsers.
I was trying to keep my code a bit cleaner by using the OOP patterns that MooTools provides in combination with Knockout.js
I can't seem to get it to work as Knockout returns some function (the property function, meh?) when it data binds the values. If the values are retrieved with javascript however, it works as exprected. I made a quick jsFiddle so you can test it out:
http://jsfiddle.net/tixz/JK2jt/
Thanks in advance!
In your current structure, the observables are created on the prototype of your class. Knockout does not intend on them being on the prototype (they can't be shared as they keep private state), and an internal function ko.isObservable fails, because of it. This causes the unwrapping code in KO does not realize that it needs to retrieve the underlying value for your observables.
You would have to write it like:
var ViewModel = new Class({
initialize: function(secondVal){
this.someProp = ko.observable('Here is my value');
this.anotherProp = ko.observable(secondVal);
}
});
http://jsfiddle.net/rniemeyer/JK2jt/9/
Here's what I'm aiming to achieve:
HTML
<fieldset id="addmore">
<p>blah</p>
<a class="remove">remove me</a>
</fieldset>
<a class="add">add more fieldsets</a>
Javascript
var addmore = new AddMore($('fieldset'));
addmore.buildCache(/*this will pull the innerHTML of the fieldset*/);
// bind the buttons
addmore.bind('add', $('a.add'));
addmore.bind('remove', $('a.remove'));
I've found myself having a lot more 'addmore' stuff in my HTML lately so I've been trying to build a class that will do all the leg work for me that I can just reuse in all my projects. The above code will, hopefully, be all I have to add each time and then the rest is done for me.
I've been winging this thing so, off the top of my head, here's what the class has to do:
Apply the jQuery bindings to the supplied 'button' objects so we can add/remove fieldsets
When a new fieldset is added, we have to recall the bind function so the new fieldset's 'a.add' button will work (I've found jQuery's .live() function to be buggy, for whatever reason, and try to avoid it)
It will hopefully do this with no memory leaks :}
Javascript Class
/*
Class to handle adding more data to the form array
Initialise the class by passing in the elements you want to add more of
Then bind 'add' and 'remove' buttons to the functions and the class will do the rest
*/
/*
Pass the jQuery object you want to 'addmore' of
Ex: var x = new AddMore($('fieldset.addmore'));
*/
function AddMore($element)
{
if (!$element || typeof($element) != 'object')
throw 'Constructor requires a jQuery object';
this.element = $element; // this is a jQuery object
this.cache = null;
}
/*
Supply clean HTML to this function and it will be cached
since the cached data will be used when 'adding more', you'll want the inputs to be emptied,
selects to have their first option selected and any other data removed you don't want readded to the page
*/
AddMore.prototype.buildCache = function(fieldset)
{
if (!fieldset)
throw 'No data supplied to cache';
this.cache = fieldset;
}
/*
use this to create the initial bindings rather than jQuery
the reason? I find .live() to be buggy. it doesn't always work. this usually means having to use a standard .bind()
and then re-bind when we add in the new set
that's what this class helps with. when it adds in the new data, it rebinds for you. nice and easy.
*/
AddMore.prototype.bind = function(type, $button)
{
if (!type || !$button && (type != 'add' && type != 'remove'))
throw 'Invalid paramaters';
// don't reapply the bindings to old elements...
if ($button.hasClass('addmore-binded'))
return;
// jQuery overwrites 'this' within it's scope
var _this = this;
if (type == 'add')
{
$button.bind('click', function()
{
_this.element.after(_this.cache);
});
}
}
I was going to have the .bind() method (in my class) call itself upon adding the new fieldset to reapply the binding but lost confidence with efficiency (speed/memory).
How should I tackle this? Do you have any pointers? Can you recommend improvements?
Thanks for the help.
In the most simplest form, you can do something like this:
var html = '{put html to add each time here}';
$('.add').click(function() {
$(html).insertAfter($('fieldset').last());
return false;
});
$('.remove').live('click', function() {
$(this).parent().remove();
return false;
});
You may need to tweak it based on your exact needs, but this should accomplish what you described in your example.
Update: sorry, remove should use the live method.
For creation of the new DOM elements, allow the specification/parameters to be any of the following:
simple HTML as a string (like the example above),
a function returning either a DOM element or HTML text. You can skip bind() or live() issues by adding
the onclick element when creating the HTML/element in the function. Although doing it in the AddMore() scope would be more tedious
if it's not a DOM element that gets returned.
inputs to a helper/factory method (maybe a template and name/value pairs) - postpone this unless you know enough patterns already.
Option #1 seems almost useless, but #3 might be hard unless you have extra time now.
Notes:
You might want to use $(theNewDomElement).insertBefore(_this.button); rather than _this.element.after(theNewDomElement); so that new items are append to the end of the list.
Actually, for insertBefore() you might just use this rather than _this.button since presumably the button (or anchor) is this, but then that limits the functionality - just make it some sort of DOM element (or a jQuery object that equates to one).
In most cases, I'd presume your DOM elements represent data that you'll want to save/send/transmit to your server, so provide a before-removal function, too. Even allow the function to skip removal -- like after a confirm().
Good luck.
jQuery is currently providing me with a fun introduction to Javascript after 12 years of surviving happily without. I'm at the stage where I'm trying to learn as much as I can about optimising the code I write and, whilst I have found plenty of good reference material, there is something quite basic which is puzzling me and I have been unable to find anything about it anywhere.
When I'm attaching something to an element how should I be referring to that element within the function. For example, when attaching a function to an element's click event :
$('#a_button',$('#a_list_of_buttons')).click(function() {
// NOW WHAT'S THE BEST WAY TO REFER TO '#a_button' ?
});
I know not to keep re-selecting it like so as the browser has to search the whole DOM again from scratch to find what it's already found once :
$('#a_button').click(function() {
// I KNOW THAT THIS IS NAUGHTY
var buttonValue = $('#a_button').val();
$('#a_button').addClass('button_has_been_clicked');
});
Currently I'm using either of the following but am not entirely sure what each is actually doing :
$('#a_button').click(function() {
// USING this
var buttonValue = $(this).val();
$(this).addClass('button_has_been_clicked');
});
But is this just re-selecting like in the first "naughty" example?
$('#a_button').click(function(event) {
// USING event.target
var buttonValue = $(event.target).val();
$(event.target).addClass('button_has_been_clicked');
});
This seems like it might be better but is it efficient to refer to 'event.target' multiple times?
$('#a_button').click(function(event) {
// USING A LOCAL VARIABLE
var thisButton = $(this);
// OR SHOULD THAT BE
var thisButton = $(event.target);
var buttonValue = thisButton.val();
thisButton.addClass('button_has_been_clicked');
});
I understand the performance efficiencies of passing things to variables but I'm unsure whether or not in these situations using $(this) or $(event.target) provides me with the same efficiencies already and so by setting a new variable I'm actually doing more work that I need to.
Thank you.
this and event.target are not always the same.
this refers to the element you assigned the listener to ( in this case the '#a_button' ). event.target however is the element that actualy triggered the event, which can be a childnode of #a_button.
So $(this) is the thing you are looking for.
See reference: http://api.jquery.com/event.target/
I may be wrong, but this and event.target are both just different references to the same element.
this and event.target are not always references to the same element. But in answer to your question, var thisButton = $(this); is definitely the winner. If you were writing C# code, you would never do the following:
this.Controls[0].Controls[0].Text = "Foo";
this.Controls[0].Controls[0].Controls.Clear();
You would do this:
var control = this.Controls[0].Controls[0];
So you probably should never re-use $(this) more than once either. Althought it's trivial to convert this from a DOM element to a jQuery object, it's still an unnecessary overhead.
However, sometimes you need to gear back from optimisation to make sure your code maintains it's readability.
Another option of course is just to change what this is. This is javascript afteral:
this = $(this); // Now `this` is your jQuery object
Disclaimer: I only just tried the above and it seemed to work. Might have some issues though.
I built a little example to demonstrate how this and e.target actually work: http://jsfiddle.net/xZAVa/
In my experience i would go with the following:
$('#a_button').click(function() {
// USING this
var buttonValue = $(this).val();
$(this).addClass('button_has_been_clicked');
});
The this in the context of your click callback method is a reference to the DOM event. Since you already have a reference to the DOM object it is trival to convert it into a jQuery object since a lookup is not required.
But on a side note, if you don't need to use jQuery in your callback, then don't. You can simply get the value of the button using standard JS this.currentTarget.value.
The other examples you mentioned require a DOM lookup, and depending on the complexity of your selector can take longer. Using a id based lookup like '#a_button' will perform better than a class based looked like .myClass.