Knockout bindingHandler causes pureComputed evaluation delay - javascript

I found that my bindingHandler causes unexpected pureComputed evaluation delay.
Demo here
Without binding handler
Without binding handler (simply comment the HTML out), I see the following log:
In subscription, new value is (newName), nameComputed is (newNameComputed)
As you can see, in the subscription, the computed was evaluated to the latest value.
With binding handler
With binding handler, I see the following log:
Evaluation in bindingHandler oldNameComputed
In subscription, new value is (newName), nameComputed is (oldNameComputed)
Evaluation in bindingHandler newNameComputed
As you can see, in the subscription, the computed was still using the old value (oldNameComputed), and in the next evaluation it's evaluated to latest value (newNameComputed)
Question
This is unexpected to me -- adding the binding handler changes the evaluation behavior.
How to explain this? Is this a bug or expected?
How to work around this? Do I have not to use pureComputed but use observable for nameComputed and set it in subscription on name?
Update 12/26/2019
Adding a bit context. My nameComputed is actually nameError which processes the name and detects whether the input is allowed or has errors (e.g. no special character, nonempty, max length etc).
In the subscription, I wanted to do some ajax calls if there is no name error. So I looked at the value of nameError and only fires the requests if there is no error.
But with the binding handler, my nameError check is outdated.
Yes, I do have the latest name value in the subscription, but I need to call the name error check function again to get the latest nameError value instead of directly using nameError computed value.

Credit to mbest in Github Issue
The first section in the pure computed documentation describes its two states. The difference you see in behavior is because the pure computed is in a different state. Without the binding handler, it is in the sleeping state, and with the binding handler, it is in the awake state.
It is normal that a subscription can run while state is being updated, and thus not all computed observables have been updated with the latest value. The main way to work around this is use deferred updates. If you set ko.options.deferUpdates = true;, you will get the results you want.
I might not go in this route for now because I'm working on a big project and don't want to introduce this global level change for now.

Related

HTML/Javascript - What is happening with '<input type=text' interactions?

After creating a new binding for the value property on an input, the value no longer changes when a user modifies it manually.
What is lost when the binding is changed? Does the regular user interaction use key press listeners?
I am changing the setter/getter to have an element's text automatically bound to an input's value property (using Object.defineProperty(input,'value')...
This is causing user interaction with the input to no longer change the value property. So if I, as the user, type '2' into the input, the value for the input will still be whatever it started as and not '2'.
So redefining the value property must be breaking the old setter.
I can't seem to find an authoritative reference right now, but it's not generally possible/safe to update host objects (basically any object provided by the browser, instead of by JS itself) in this way. If you insist, you might try Object.getOwnPropertyDescriptor to see if you can get at the default setter before you Object.defineProperty and overwrite it.
More info: http://perfectionkills.com/whats-wrong-with-extending-the-dom/ (see esp. "Host objects have no rules")

How can I avoid "parse" validation error for custom angularjs directive when setting model to undefined?

I have a directive for a date input field that uses $formatters and $parsers to modify the model and view value.
Whenever I clear the input field I want to set the model value to undefined. This is done by returning undefined from the $parsers function. The problem is that when invalid is returned from a $parsers function the input will get invalid:
$error":{"parse":true},
Is there some way to set the model value to undefined without making it invalid?
Example on jsfiddle
Edit:
The only solution I've come up with so far is setting a timeout before returning undefined from the $parser function:
$timeout(function(){
ngModel.$setValidity('parse', true);
}, 0);
return undefined;
This feels like a hack however.
As Paulo points out above, if it works for your situation, returning null (or some comparable blank value that's not undefined) is probably your best bet.
Unfortunately, it seems that returning undefined will definitely cause the $parsers pipeline to stop and force an error state.
Your $timeout definitely does feel like a hack, but if you REALLY have to return undefined, it's probably the only way.

Running into infinite digest cycle while binding to function that has $http inside

I need to generate http link for a tag. For that reason I put in html line
My link
in controller it defined as:
$scope.getLink = function(inputUrl){
$http.get(inputUrl).success(function(data){/*.....*/});
}
Why AngularJS ends up in infinite cycle? What is the right design?
As explained in another answer, watched expressions are evaluated on every digest and the resulting value is compared to their previous value - dirty checking. If there is a change, another iteration of the digest starts because a change in one value might cause a change in another.
If there is a circular dependency (including, the circle of one, i.e. the same expression is different every time), it results in an infinite loop that Angular stops after 10 iterations.
Specifically, your getLink function's return value is a promise (the return value of $http), and Angular bindings do not "wait" on a promise.
What you want to do is to kick start the $http call and in its handler assign the return value to a ViewModel property that would be bound to <a>:
function getLink(){
$http.get(inputUrl)
.success(function(data){
$scope.url = data.data;
});
}
You can call getLink, for example, when your controller runs.
In the View you just bind url to ng-href (not href) attribute:
<a ng-href="url">My Link</a>
Once you add an Angular expression in View like {{expression}}, it would be added to watch list of current scope.
Angular uses a mechanism called dirty checking to archive two way binding. Each time some specific events happen, Angular will go through the watch list to check whether the watched value has changed or not, this action is called as digest loop.
Here some specific events consist of user input, model change, $http requests finish, etc. As you are using a function getLink in the expression, each time when Angular trigger a dirty check/digest loop, this function will be executed once again to check whether its return result has changed.
The problem is, the function getLink here is a $http request, after it's executed, Angular will triggered another round dirty check ,which will execute this function again ... Bang, it's an infinite loop.
Conclusion: do not add any $http call in angular expression.

AngularJS : Difference between the $observe and $watch methods

I know that both Watchers and Observers are computed as soon as something in $scope changes in AngularJS. But couldn't understand what exactly is the difference between the two.
My initial understanding is that Observers are computed for angular expressions which are conditions on the HTML side where as Watchers executed when $scope.$watch() function is executed. Am I thinking properly?
$observe() is a method on the Attributes object, and as such, it can only be used to observe/watch the value change of a DOM attribute. It is only used/called inside directives. Use $observe when you need to observe/watch a DOM attribute that contains interpolation (i.e., {{}}'s).
E.g., attr1="Name: {{name}}", then in a directive: attrs.$observe('attr1', ...).
(If you try scope.$watch(attrs.attr1, ...) it won't work because of the {{}}s -- you'll get undefined.) Use $watch for everything else.
$watch() is more complicated. It can observe/watch an "expression", where the expression can be either a function or a string. If the expression is a string, it is $parse'd (i.e., evaluated as an Angular expression) into a function. (It is this function that is called every digest cycle.) The string expression can not contain {{}}'s. $watch is a method on the Scope object, so it can be used/called wherever you have access to a scope object, hence in
a controller -- any controller -- one created via ng-view, ng-controller, or a directive controller
a linking function in a directive, since this has access to a scope as well
Because strings are evaluated as Angular expressions, $watch is often used when you want to observe/watch a model/scope property. E.g., attr1="myModel.some_prop", then in a controller or link function: scope.$watch('myModel.some_prop', ...) or scope.$watch(attrs.attr1, ...) (or scope.$watch(attrs['attr1'], ...)).
(If you try attrs.$observe('attr1') you'll get the string myModel.some_prop, which is probably not what you want.)
As discussed in comments on #PrimosK's answer, all $observes and $watches are checked every digest cycle.
Directives with isolate scopes are more complicated. If the '#' syntax is used, you can $observe or $watch a DOM attribute that contains interpolation (i.e., {{}}'s). (The reason it works with $watch is because the '#' syntax does the interpolation for us, hence $watch sees a string without {{}}'s.) To make it easier to remember which to use when, I suggest using $observe for this case also.
To help test all of this, I wrote a Plunker that defines two directives. One (d1) does not create a new scope, the other (d2) creates an isolate scope. Each directive has the same six attributes. Each attribute is both $observe'd and $watch'ed.
<div d1 attr1="{{prop1}}-test" attr2="prop2" attr3="33" attr4="'a_string'"
attr5="a_string" attr6="{{1+aNumber}}"></div>
Look at the console log to see the differences between $observe and $watch in the linking function. Then click the link and see which $observes and $watches are triggered by the property changes made by the click handler.
Notice that when the link function runs, any attributes that contain {{}}'s are not evaluated yet (so if you try to examine the attributes, you'll get undefined). The only way to see the interpolated values is to use $observe (or $watch if using an isolate scope with '#'). Therefore, getting the values of these attributes is an asynchronous operation. (And this is why we need the $observe and $watch functions.)
Sometimes you don't need $observe or $watch. E.g., if your attribute contains a number or a boolean (not a string), just evaluate it once: attr1="22", then in, say, your linking function: var count = scope.$eval(attrs.attr1). If it is just a constant string – attr1="my string" – then just use attrs.attr1 in your directive (no need for $eval()).
See also Vojta's google group post about $watch expressions.
If I understand your question right you are asking what is difference if you register listener callback with $watch or if you do it with $observe.
Callback registerd with $watch is fired when $digest is executed.
Callback registered with $observe are called when value changes of attributes that contain interpolation (e.g. attr="{{notJetInterpolated}}").
Inside directive you can use both of them on very similar way:
attrs.$observe('attrYouWatch', function() {
// body
});
or
scope.$watch(attrs['attrYouWatch'], function() {
// body
});
I think this is pretty obvious :
$observe is used in linking function of directives.
$watch is used on scope to watch any changing in its values.
Keep in mind : both the function has two arguments,
$observe/$watch(value : string, callback : function);
value : is always a string reference to the watched element (the name of a scope's variable or the name of the directive's attribute to be watched)
callback : the function to be executed of the form function (oldValue, newValue)
I have made a plunker, so you can actually get a grasp on both their utilization. I have used the Chameleon analogy as to make it easier to picture.
Why is $observe different than $watch?
The watchExpression is evaluated and compared to the previous value each digest() cycle, if there's a change in the watchExpression value, the watch function is called.
$observe is specific to watching for interpolated values. If a directive's attribute value is interpolated, eg dir-attr="{{ scopeVar }}", the observe function will only be called when the interpolated value is set (and therefore when $digest has already determined updates need to be made). Basically there's already a watcher for the interpolation, and the $observe function piggybacks off that.
See $observe & $set in compile.js

Monitor primitive String declaration

Is there ANY way of monitoring primitive String declaration in NodeJS?
For example, when I do "test"; is there any way of triggering a special event with "test" as a parameter?
I am not sure what you mean by do "test;"; but if you want to trigger an event whenever a variable is assigned a new value, no, there is no way to trigger an event for variable changes.
If you want to watch a variable, it is better to re-design your system to make a call instead of assigning a variable. Instead of doing:
running_mode = "test";
require
switch_mode("test");
and call whatever event handlers you want to trigger on this update.
If you really want to watch the value of a global variable, you can do it by checking the value once in every turn of the main loop:
function is_mode_changed() {
if (running_mode=="test") event_handler();
process.nextTick(is_mode_changed);
}
Once you call this function, it will continue running once each turn of the main loop. If it what you want to do is something like following a variable to do some specific tasks time to time, like following a global counter and doing some cleanup tasks everytime counter reaches 1000, this is a good way to do. If what you want to do something immediately after a variable is changed, it is not possible.
I hope I could understand your question correctly.
UPDATE
[I am adding this in regards to the comment below which rendered everything above unrelated to question because I had misunderstood it.]
As you mentioned yourself, a string literal like "test" is a primitive value which is not an object. So, it is handled by the interpreter in a way we cannot alter.
From Ecma-262:
4.3.2 primitive value
member of one of the types Undefined, Null, Boolean, Number, or String as defined in Clause 8
NOTE: A primitive value is a datum that is represented directly at the lowest level of the language implementation.
To prevent confusion, Clause 8 is the section of standard on Types as listed above.
Since you specified V8, and not per-spec-ECMAScript, you have more concrete options at your disposal. In the V8 API there is classes/templates or primitives that are separate from those of object wrappers for primitives. In order to be able to hook in a way to know when this actually happens would likely require modifying v8 in a custom manner, but it is doable.
http://code.google.com/p/v8/source/browse/branches/bleeding_edge/include/v8.h#1017
Also much of the action takes place in js itself. Perhaps not the very constructor itself, but everything that happens thereafter. String.prototype.toString/valueOf.
http://code.google.com/p/v8/source/browse/branches/bleeding_edge/src/string.js

Categories

Resources