Trying to use the new cleaner event handling in Knockout 2 along with the new control flow bindings I'm working on implementing a simple dynamic list, adding and removing strings. So my markup roughly looks like this:
<div data-bind="foreach:myList">
<input data-bind="value: $data" />
<button data-bind="click:$parent.removeFromList">X</button>
</div>
and my viewmodel has a matching remove function which immitates a sample from Steve Sanderson.
removeFromList: function(item) {
this.myList.remove(item);
}
Now I would expect 'this' to refer to the viewmodel and item to refer to the array member being removed (since event handlers now receive the current model value as their first parameter). However, 'this' also seems to refer to the string being removed. Therefore when I click remove I get:
this.myList is undefined
I've created a JSFiddle at http://jsfiddle.net/davidc/rFd7H/ which illustrates my problem. How should I be removing items from the list?
There are many ways that you can ensure that your handler has the correct value for "this".
Build your view model in a function and use bind against the current this Sample: http://jsfiddle.net/rniemeyer/rFd7H/4/. Or don't build your view model in a function and use bind like: http://jsfiddle.net/rniemeyer/rFd7H/10/
Build your view model in a function and keep a variable with the correct value of this and use it in your handler. Sample: http://jsfiddle.net/rniemeyer/rFd7H/5/
Bind to the correct context within your binding like: data-bind="click: $parent.removeFromList.bind($parent)" Sample: http://jsfiddle.net/rniemeyer/rFd7H/8/
Call it as an anonymous function off of the $parent object like: data-bind="function() { $parent.removeFromList($data); }" Generally, not recommended, as it makes the markup ugly/verbose. Sample: http://jsfiddle.net/rniemeyer/rFd7H/9/
Related
I want to create a directive with isolated scope, but I'm not able to get it working.
jsFiddle
I want to isolate age model in a directive scope. I want to perform some business logic on that model and then set that model to parent binding. I hope the fiddle is explanatory.
I am also adding a button to the template which when clicked should invoke a submit function:
<button ng-click="submit()">click me</button>
It seems the button is working fine, but why is $scope.$watch() is not begin triggered? In a normal situation, if I change the view value it will automatically update the model value. But now it isn't.
$watch requires a dollar sign, and you pass either a function or a string that is evaluated on your scope, i.e.:
$scope.$watch('age', function(value) {
There are many more errors in your code, for instance you don't have a declared variable called 'age' so this line will reference window.age and give you an error because it is undefined, you need to say $scope.age I think:
age = age+10;
It just looks like your updated fiddle is a playground, hope these point you in the right direction. I'd recommend going through the egghead.io angular videos.
Amongst other things, I have read:
what-does-this-mean
you-must-remember-this
mythical-methods
but they haven't solved 'this' problem I'm having with a piece of JavaScript.
I have a Section object that gets passed some XML which it uses to populate the section. In the Section object I append a div which has a specified index. The resulting jQuery object is pushed into a sections Array. The following code is from the Section object code:
sections.push($('#section' + p_sectionIndex));
this.showSection = function() {
this.show();
}
this.hideSection = function() {
this.hide();
}
sections[sections.length-1].on('show', this.showSection.call(sections[sections.length-1]));
sections[sections.length-1].on('hide', this.hideSection.call(sections[sections.length-1]));
Elsewhere I call sections[index].trigger('hide'); and sections[index].trigger('show');
The first of the links I mentioned above seemed to suggest this in a function depends on HOW it's called and that you could pass a reference to this into the function by using call. I know the showSection and hideSection function ARE being triggered - I just can't get the this in those functions to refer to the jQuery objects in the sections Array.
I have tried multiple variations of the above (excluding the call, using $(this) in the functions, adding the showSection and hideSection functions to the jQuery object - amongst others) but I'm kind of out of ideas.
Any help much appreciated!
this in an event handler is the element node that the event was bound to. If you want a jQuery object wrapping that node, use $(this)
Demo: http://jsfiddle.net/b36M6/
This of course assumes you revert back to the correct way of passing a function to the event binding.
When you use .call(), you're invoking the function immediately.
Since you want this to refer to the element, bound, just pas the function itself.
sections[sections.length-1].on('show', this.showSection);
sections[sections.length-1].on('hide', this.hideSection);
Now this in the showSection and hideSection methods will refer to the sections[] member to which it was bound.
I assume "show" and "hide" are some sort of custom events.
I have several knockout models, that I use on my website. Let's say for a given model I have a property
function modelA {
this.doSomething = function () { ... };
}
Now i want to use the doSomething() function in a different model. I would like to do this, such that I am not obligated to rewrite doSomething() in every single model that needs it. Ideally i would like it if this function becomes available as soon as ko.applyBindings(new modelX()) is called.
I know that i can have the functions inherit from a prototype, but that also leads repeated code in every single model. Is there a way to alter knockout.js to inject this property into all model objects?
EDIT:
In case there's a different approach that would achieve my desired goal, i'll elaborate on that desired goal. We use knockout-validation.js which allows me to call isValid() on every property in a model that has been extended with validation rules. I want to append a function to every model that will inspect the other properties in the model, and call isValid() on them. That way i can write a generic validation function that can be used with every model, without having to explicitly add it to the model.
You can add a custom function onto knockout's core datatypes. Alternatively you could look at extenders, and inside the extension you could add the function to an observable. Note, however, that both of these techniques address cross-cutting concerns at the "observable" level, not at the ViewModel level.
I would be curious to understand what doSomething is doing. Assuming that it has to access some observable(s) inside the ViewModel you should be able to reframe the problem so that the behaviour is attached to an observable.
I'm trying to do something like this:
http://jsfiddle.net/bATu3/5/
Where the entire view is generated within an object, privately and return via a public method so that it can be generated on the page. I'm doing something wrong and would appreciate any pointers to help me sort this out.
Try this:http://jsfiddle.net/bATu3/10/
Basically there were few errors: Be careful of the use of 'this' within callback functions.
Also, note the data bind variables <p><strong data-bind="text:firstName"></strong></p>
another way to do this is: http://jsfiddle.net/bATu3/14/
you can specify the scope for computed values by passing it as a secondary parameter as noted here: Knockout: Computed Observables (read the "Managing ‘this’" section)
With Knockout 2.0 using this data-bind:
data-bind="click: $root.deleteSomeEntity.bind($data, $parent)"
in the Knockout viewmodel JavaScript the first argument in
self.deleteSomeEntity = function (data, parent) {
// perform deletion
}
seems to be the parent rather than the data.
Is there a reason for this behavior or something I'm missing?
When you call bind the first parameter will be the value of this. So, in your call this will be $data and the first argument will be $parent.
If $root is $parent in this case, then you can just do:
$root.deleteSomeEntity.bind($root)
KO will pass the data as the first parameter and this will be set to $root.
If $parent is not $root (and you likely don't want to rely on this being a different object that $root in your method on root), then you would do something like:
$root.deleteSomeEntity.bind($root, $data, $parent)
Otherwise, there are certainly ways to make sure that you have the proper this within your view model. It depends on your structure though.
Why are you using bind()? By default, if you just write the name of the javascript function as the click event Knockout will pass $data as the first argument and the event as the second.
http://knockoutjs.com/documentation/click-binding.html (Note 1&2)
Why bother with bind() when you can simply do this:
data-bind="click: function() {$root.deleteSomeEntity($data, $parent)}"