Angularjs string value or expression? - javascript

I have a directive which takes a parameter which can either be a string or an expression which resolves into a string. How can I determine whether it's one or the other?
// directive code around these lines...
$scope.$watch(attr.param, function() {
var evaluatedExpr = $parse(attr.param)(scope);
doSomethingWith(evaluatedExpr);
});
// for a string, I just call
doSomethingWith(attr.param);

If you want to eval a string as an expression use the $scope.$eval method. But, as #ChadRobinson pointed out, this shouldnt be something you need to do in your directive as the expr should probably be evaluated on the parent scope.
As a note to that, unless you are inheriting the parent scope in your directive your expr may not have the information it needs to actually evaluate from within your directive.
$scope.test = function(v){
var expr = $scope.$eval(v);
if(!expr){
console.log("could not eval, maybe a string... who knows?");
}else{
console.log(expr);
}
}

How have you defined this parameter in your scope block? Your choice here determines the answer:
# - This is a one-way binding, so it must be an expression.
& - This binds to a method that executes in the parent' scope, so you would be defining a 'getter' in the parent that the child can call. This may be useful if you are writing a generic library and you want the person using your directive to be able to choose what gets passed. In that case, you would expect that caller to perform any $parse operations required.
= - This makes a two-way binding, which means it must be a property name. It cannot be an expression (or method).
This feeds into your own code example. You are setting a watcher on the parameter. That seems to imply that you're using two-way binding - it wouldn't make sense for a one-way binding because that's only evaluated once. But in that case, you wouldn't call $parse because you'd have the raw value to work with, not an expression.

Related

Is there a way to evaluate a function defined as a string by using eval()?

I would like to store a function definition in a string, like for example:
var funcString = 'function(){ return 4+4 }'
Can I use eval() to evaluate the function?
Like for example:
var result = eval(funcString)
which will evaluate the string in funcString as a function and return 4+4 (8) in result.
edit: So from what you've told me I understood that I shouldn't use eval in that way, but for my case I don't think there is another way.
I would like to define a set of rules in a separate file and I want to instruct my Javascript Library to look into this file and search for the rules. The following is an example of a rule:
rule('logOut', (function() { return !JSLibrary.compareUserDetails })(), (function() { console.log("\n\nError: logged out user did not match\n\n\n") })())
I would like to evaluate both of the functions defined in the rules and I think that eval is the only way to do it. I don't know if there exist any other.
Fair warning, eval is "evil." I'll answer your question regardless, but you might want to rethink using eval.
In JavaScript, functions are values, just like 5, 'Stack Overflow' and document. In fact, what function myFunction(args) { body } does is create a function and throw it into a variable called myFunction*.
Say you wanted to take that function and put it into another variable. You can just say var otherVariable = myFunction. And if you want to call that function, you can just say otherVariable().
But say you want a function that isn't bound to a name, for instance if you're passing it to another function. You can use an anonymous function, defined as function(args) { body }. That's what's inside the string you're eval-ing. Anonymous functions are just like any other function, and you can call them as such. Actually, you can even call them right out of the result of eval by tacking on some parentheses at the end.
Unfortunately, it's not that easy though. You can't just write a line of code with an anonymous function definition and expect it to run: you will instead get an error complaining about it not being named. To fix this, throw some parenthesis around the definition to force it to be an expression instead of a statement.
Your final working code will look like this:
var funcString = '(function(){ return 4+4 })'
var result = eval(funcString)()
Sure, you should never use eval for this particular use, but I hope you learned a bit about JavaScript from this answer anyways.
* There's more to it than that, such as hoisting, but that doesn't really matter in this explanation.
Use of an immediately invoked function expression can call a function with eval.
document.body.innerHTML = eval('(function(){return 4+4;})()');
WARNING: do not ever do this: it's insecure, a performance-killer, harder to read/understand, etc

Syntax for scope value into other scope value

With angularjs, what is the correct way to get in the view :
{{votedList[{{song.Radio.idSong}}].voted}}
give Error: [$parse:syntax]
song.Radio.idSong is a integer, getting into a ng-repeat
votedList is a scope array
This one:
{{votedList[song.Radio.idSong].voted}}
The double brackets in a view are used for rendering an angular expression, therefore you don't need to interpolate again inside the expression.
Also, scopes inherit the properties of their ancestors (except for isolated scopes) so, as long as you are using that expression inside a scope that has visibility for that property, you are good to go.

JavaScript Anonymous Constructor Function: !function(){}();

I've seen this syntax for creating an anonymous constructor function in JavaScript:
var Application = Application || {};
!function(window, Application) {
Application.property = {/*...*/}
Application.method = function(){/*...*/}
}(window, Application);
I want to understand what the following parts here:
What is the advantage of using first line (i.e. var o = o || {};) vs just stating var o = (function(){})();?
Why ! is used in front of function?
Why would I pass window or Application as parameters when they are global object?
Is this the most convenient way for anonymous constructor function and how is this better than:
4a)
var Application = {
property: {},
method: function(){}
}
or 4b)
var Application = (function() {
var method = function(){/*...*/}
return {method:method};
}());
The first line is to ensure that Application always exists, and is generally used in cases where it's expected that Application already should exist, and the function just augments the existing object. If it doesn't exist, this makes sure that we don't get an error for accessing properties of undefined. Your examples are only equivalent in the case where Application does not yet exist. In all other cases, your code will obliterate the existing Application, which is almost certainly not the intent.
The comment from Vatev explains what the ! does. It's another syntax for making the function in question become a self executing anonymous function. (Incidentally, it also takes the return value of the function - which is currently undefined, and flips its truthyness, so it evaluates as true. Since the result isn't stored in any variable, though, that's clearly not the purpose.)
Finally, why pass window and Application into the function and use it there? This is a safety feature, in case other code changes window or Application later on. It guarantees that within the anonymous function, window and Application are exactly what you expect it to be. In the shorthand example you gave, this may appear to not matter - after all, why protect these variables if you're using them immediately and not storing them? In many cases, you return something from this function, and then window and Application would be stored in the closure, so you'd retain the variables. It makes it safe from people who later on decide to say Application = {...}.
I don't know all of the answers but I'll try to answer what I can.
Using var Application = Application || {}; simply means that you are dealing with scope. If the "Application" variable has already be defined, it will just mean that it inherits and is now available in the current scope. If not, the "||" part (means OR) means that it will be created as an empty object. You are dealing with creating an object and then acting on it, not just having the result given back raw. That's why you shouldn't use "o = (function() {...});". "o" would then be the result of the function and not the object.
The use of "!function" causes it to be treated as an expression, but this can be tricky because you may have to think in opposites. E.g. !null means you are checking that it isn't null, not that you are checking for it to be null.
Passing in the window and Application objects deal with scoping, again. It is explicitly passing them into the function, then returning them at the end. Think of this as putting clothes in the washing machine. You put them in, stuff can happen, then you get them back out. It's a really crude idea and isn't the best example, but it's how I thought of it a while back. You put clothes in, call then rinse function, then soap function, then rinse again, then spin to drain water.
Hopefully someone else can answer that, I'm not sure what the differences are.
Since two answers so far neglected these two details: A pattern used by libraries like jQuery is using the following two parameters:
(function (window, undefined) {
window.myPlugin = ...;
})(window);
There are two things going on here:
undefined is specified as a parameter, but not passed in. This way, undefined is guaranteed to have th expected value within the inner scope. This is only necessary for older browsers which allowed overwriting undefined (though it has always been considered bad practice to do so – it's just what libraries like jQuery do to avoid other code interfering with their code).
Aliasing a global object like window within the scope allows the code to be minified more (also works for undefined). Obviously, the more references you have to the aliased object(s), the more you will save:
.
(function(w,u){w.myPlugin=...;w.somethingElse=...;if(whatever===u){return;}})(window);
compared to
(function(){window.myPlugin=...;window.somethingElse=...;if(whatever===undefined){return;}})();
You won't save much with window since typically you don't wanna clutter the global object up anyway. But aliasing undefined can save you quite some space.

Confusion about first parameter of $scope.$watch

From the hitch hiker's guide to directive, light bulb example,
scope.$watch(function() {
scope.bulb = controller.getState();
});
First parameter of $watch is a function, what is exactly being watched here?
I read another SO's post on scope, this is the explanation.
"The first parameter of the $watch method (the "watchExpression") can be either an Angular string expression (that is evaluated against the $scope), or a function, which is called with $scope as the first parameter."
I am still not clear about the use of function as a first parameter to $watch, from light bulb example
- is scope implicitly passed as a parameter to that function?
- does that function implicitly return scope.bulb, so scope.bulb is being watched?
No, the function is being watched. This means the function is called and its value checked against the value it returned last time at least once every apply-digest cycle! The scope is indeed passed, but not used in your example. HTH
P.S. It is a bit odd to use a watch expression to set a value on the scope. What the watch expression function should do is return the state and set the scope value in the callback. That means it is only set when it changes rather than every time it is checked. Odd example!
To me, this looks like a wrong usage of $watch. The function should return a value, which would be watched for changes, but in this case, it will always be undefined, so a change of the value will never be watched.
A better approach would be something like this:
scope.$watch(function() {
return controller.getState();
}, function(newVal) {
scope.bulb = newVal;
});
This would watch for changes of controller.getState(), and would assign the new value to scope.bulb whenever it changes.
This is an idiom to be notified whenever $digest is called. From the documentation, see the doc for $watch:
If you want to be notified whenever $digest is called, you can register a watchExpression function with no listener. (Since watchExpression can execute multiple times per $digest cycle when a change is detected, be prepared for multiple calls to your listener.)

prevent access with setter

The problem is to prevent overriding from outside of a object.
my idea is to use setters:
var spot = function(val){
this.id = val;
this.__defineSetter__("id", function(val){alert("bang");});
}
The id should be set once in the constuctor and never be changed.
I would also like to define the setter on the prototype because I have 10.000 spots. But in this case the setter prevets access also to the consructor.
I am not willing to use var id in the constructor and define a getter on it. In this case every single one of the (10.000) objects has its own closure.
A second question is: Can the setter somehow know wether the var is changed from the constructor or later from a (spot)internal function? So I could prevent access just from outside the object.
Firstly, that code is non-standard and deprecated. Depending on the platform you're developing for, I suggest using preventExtensions or freeze. Or maybe you just want to define the id property as read-only, in which case use defineProperty.
Secondly, as you've done things, each instance of spot already has it's own closure. That setter that you defined is a unique function + lexical scope that will exist for every spot, aka closure. So I'm not sure what the big deal is with making a getter as well. Although the first point trumps here, and you should be using the new methods to achieve that functionality.
Lastly, no matter what direction you take, the setter will not be able to "know" anything. So you can either have a separate variable, something to the tune of isInitialised, or create a code invariant so that the value is undefined at first, and numeric afterwards, which allows the simple check if the variable has a defined value, in which case it's been set, and shouldn't set it again.

Categories