AngularJs $parse string to object - javascript

So lets say I have a server that returns a string like:
{
add:function(a,b) {
return a+b;
},
subtract:function(a,b) {
return a-b;
}
}
is it possible to $parse that into a function?
I tryed to parse it with:
$parse(element.Content) // Content is the string above
but get
Token '{' is an unexpected token
I would like something like:
elementFunctions = $parse(element.Content)

$parse does not do what you think it does!
To put it simply, it just resolves properties, by their name, against some context (angular scopes) -- basically.
Well, it's more than that, it provides a way to interact with these properties, but you'll get the gist of it by having a quick look at its documentation.
It does not evaluate Strings as javascript code.
You're most likely looking to evaluate your string.
Although in many, many cases there's a better, faster, more controllable and more reasonable solution!
const code = ''
+ 'const functions = {'
+ 'add: function(a,b) {'
+ 'return a+b;'
+ '},'
+ 'subtract: function(a,b) {'
+ 'return a-b;'
+ '}'
+ '};'
+ 'functions';
console.log(eval(code).add(50, 1));

Related

Shorthand Shortcircut. Is it possible to do some stuff

This question is about a TypeScript code-base, but the general concepts that underpin it are ECMAScript concepts, therefore; I have tagged the question as both JS & TS. If I should tag it differently, you can either let me know, or make the edit yourself and I will accept it if I see it.
In a TypeScript document, I have defined an object-type that contains 4 string typed properties.
Here is an example of the type
type StrObjType = {
foo?: string;
boo?: string;
fuz?: string;
buz?: string;
}
Along with the object described above, I have a function — its called funkyFunc — with a parameter that takes an argument of type StrObjType.
This is what the function looks like:
function funkyFunc(obj:StrObjType)
{
let mesg = '';
if(obj.foo){ mesg += '\n Foo: ' + obj.foo; }
if(obj.boo){ mesg += '\n Boo: ' + obj.boo; }
if(obj.fuz){ mesg += '\n Fuz: ' + obj.fuz; }
if(obj.buz){ mesg += '\n Buz: ' + obj.buz; }
if(!mesg) return '';
// If a string was concatenated to mesg, return w/ newline appended
return mesg + '\n';
}
About the Function
If you can't tell by looking at it, the function creates a list using only properties that were added to the functions object-argument. The new-lines ('\n') are added as a means to format the list.
You can see in the function that each property in the object's type is checked for a truthy value. When a property is truthy, the props string-value is appended to the mesg variable via the "Addition Assignment Operator".
About the Problem & Solution
What I'd like to know, is if there is a better way to write the if statements, if I can short circuit them, or use a shorthand. Here is an example of a short circuit that uses nullish coalescing. The link shows a short circuit used to check an objects property, and assign it, only if the property is non-nullish (null or undefined). I was hoping to use that operator, but becauese I am appending the value when the property exist, rather than assigning it, the nullish coalescing won't seem to work.
Nevertheless, I can't help but to feel like this is the ideal situation for implementing a short circuit, but if there is a better way to write the function, I can't figure out what it is.
There is this way of writing it, but IDK if it's better.
let mesg = '';
mesg += obj.foo ? '\n Foo: ' + obj.foo : undefined;
mesg += obj.boo ? '\n Boo: ' + obj.boo : undefined;
mesg += obj.fuz ? '\n Fuz: ' + obj.fuz : undefined;
mesg += obj.buz ? '\n Buz: ' + obj.buz : undefined;
if (!mesg) return '';
return mesg + '\n';
}
The problem with the above method of writing the function is that it requires using the JS built-in undefined constant, and that feels hacky to me, as the undefined constant wouldn't be needed otherwise. I could replace the undefined constant with an empty string I guess, but it still doesn't feel right, as it requires adding a nullish value, which are values I typically try to avoid (not that I see any immediate harm that comes from using undefined, or '').
So I guess my question is this...
Is there a shorthand, or short-circuit I can use to eliminate the if keywords in the first function example I added? The short circuit or shorthand cannot be the ternary example I already provided, and it needs to shorten the codes length, or improve the codes readability.
If there is any confusion, or if the question just isn't making sense, let me know, I will clear things up. I am peer reviewed, and I feel like there is something I am missing here. I would like to feel more confident about how I am authoring this function.
There is this way of writing it, but IDK if it's better.
What you have after the text above will result in the string "undfined" appearing in mesg, which I assume you don't want. :-) If you wanted to do it that way, you'd use "", not undefined, for the third operand of the conditional operators:
mesg += obj.foo ? "\n Foo: " + obj.foo : "";
mesg += obj.boo ? "\n Boo: " + obj.boo : "";
mesg += obj.fuz ? "\n Fuz: " + obj.fuz : "";
mesg += obj.buz ? "\n Buz: " + obj.buz : "";
What I'd like to know, is if there is a better way to write the if statements, if I can short circuit them, or use a shorthand.
Not really. Moreover, what you have with the if statements (or the series of conditional operator expressions) is clear and easy to debug. There are other ways to do it, but they may be harder for people coming to the code fresh to understand.
For example, you could have this constant:
const messageParts = [
{key: "foo", label: "Foo"},
{key: "boo", label: "Boo"},
{key: "fuz", label: "Fuz"},
{key: "buz", label: "Buz"},
] as const;
(The as const is important, that way TypeScript can see that the key values are valid keys for StrObjType.)
Then the code is something like:
for (const {key, label} of messageParts) {
const value = obj[key];
if (value) {
mesg += `\n ${label}: ${value}`;
}
}
Playground link
For a one-off, that's definitely not an improvement, and it's certainly not short-circuiting or shorthand. But if there are a lot of places you use this same information, it gives you just one place to maintain it.
But if it's a one-off just to build mesg, I think you're best off with the ifs you already have, or the conditional operator expression alternative you noted (with the undefined => "" fix).

LineBreak in console object

Line break in javascipt string console
console.log("Foo" + "\n" + "Bar");
Line break in javascript object console
console.log({ value : "Foo\nBar" });
Is it possible to add linebreaks in javascript objects.
The answer is no: when you print an object to the console log, strings will be written as javascript objects (similar but not identical to what you'd get if you explicitly converted them into JSON, like console.log(JSON.stringify(object))).
If you want for some reason to print your strings with line breaks, you'd have to implement the object-to-string conversion yourself; perhaps with something like this:
function customString(object) {
let string = '{\n';
Object.keys(object).forEach(key => {
string += ' "' + key + '": "' + object[key] + '"\n';
});
string += '}';
return string;
}
console.log(customString({ value: "Foo\nBar" }));
(It sounds like you have an idea in mind of exactly how you want this output to look, so adjust the function above until it works as expected.)
You can make JSON pretty with automatic line breaks using:
console.log(JSON.stringify({ test: { key: { inner: 'val' } }}, null , 2))
Where 2 is the number of spaces/indent for each level.
You can use ES6:
console.log(`hello
world`)
will produce:
hello
world
I think its originally creating a line break, but due to the object, it's not showing directly. Try to assign it in variable and access that in the console.
Code:
var v = {val:"test\ntest"};
console.log(v.val);
Output:
test
test

How to invoke javascript in WebView Windows 10 UWP?

I am trying to load a javascript in WebView to do some calculations and get the output in a string. I tried to use following code
string htmlFragment = "<html><head><script type='text/javascript'>" +
"function doubleIt(incoming){ " +
" var intIncoming = parseInt(incoming, 10);" +
" var doubled = intIncoming * 2;" +
" document.body.style.fontSize= doubled.toString() + 'px';" +
" return doubled.toString());" +
"};" +
"</script></head><body>" +
"<div id = 'myDiv'>I AM CONTENT</div></body></html>";
htmlView.NavigateToString(htmlFragment);
htmlView.LoadCompleted += async(s1,e1) =>
{
string result = await htmlView.InvokeScriptAsync("eval", new string[] { "doubleIt(25)" });
Debug.WriteLine(result);
};
Update
I am able to load simple javascript easily now based on help provided in the answer. But now I am facing issues when there is more than one function in javascript, I am getting an exception. I am trying the following code
string htmlFragment = #"<html><head><script type='text/javascript'>" +
"function a(){return 10;};" +
"function b(){return 20;};" +
"function c(){return 30;};" +
"return (a()*b()*c());" +
"</script></head><body>" +
"<div id = 'myDiv'>I AM CONTENT</div></body></html>";
Please suggest.
The documentation for this feature is really poor. It took me some time to figure out how to invoke Javascript in UWP WebView
When you first look at the function call webView.InvokeScriptAsync(string,string[]) your initial reaction is that they want the function name as the first parameter and then the function paramaeters as the string array. (mainly because the MSDN documentation says this)
Parameters
scriptName
Type: System.String [.NET] | Platform::String [C++]
The name of the script function to invoke.
arguments
Type: System.String[]
[.NET] | Platform::Array [C++]
A string array that
packages arguments to the script function.
HOWEVER, this is wrong and will lead to hours of head banging. REALLY, what they want is the word "eval" in the first parameter and then a string array of functions, and or commands you wish to eval
var value = await webViewer.InvokeScriptAsync("eval",
new string[]
{
"functionName(functionParams)"
});
Having worked with Microsoft APIs for a few years now I am convinced that this is not the intended way of consuming this function and is a bit of a hack. Unfortunately if you want to consume JavaScript this is the only way that I know that works currently.
Anthony,
Try to check your own suggestion:
await webViewer.InvokeScriptAsync("eval",
new string[]
{
"functionName(functionParams)"
});
or:
await webViewer.InvokeScriptAsync(functionName, new string[]{ functionParameters });
The same as Microsoft suggests, just you are limiting a function name by one ("eval") - not necessary. Trust me, you can use any function name, as I am now with UWP and before with windows phone hybrid apps.
The question is already 4 years old, but I'm coming to see why you were getting an empty string as a result.
In your example, the functions in JavaScript return integers while the expected value is of type string.
By modifying these functions and returning a string like this:
string htmlFragment = #"<html><head><script type='text/javascript'>" +
"function a(){return '10';};" +
"function b(){return '20';};" +
"function c(){return '30';};" +
"</script></head><body>" +
"<div id = 'myDiv'>I AM CONTENT</div></body></html>";
We get the good result on the way back.

Partial HTML string escaping in Angular.js

I have read about angular's way of escaping everything by default and $sce, so I white-list data with $sce.trustAsHtml() through filter (since $sce is not working in service), like this:
<sup class="ng-binding" ng-bind-html="row|logEntry"></sup>
But the problem is, that I don't trust some parts of HTML.
To dive into details - I have translations that have HTML in it, but they have replaceable tokens/variables in them. So translations support HTML, but I don't want provided tokens to include HTML.
My filter logEntry internally looks like this:
var translated = $translate('Log.' + msg.context.entity_type) + '.' + msg.context.action, {
'object_name': msg.context.object_name,
'user': msg.context.user_name
});
return $sce.trustAsHtml(translated);
For example I can have translation about userX changing article, but I don't want result text to trigger alert() if user's name includes <script>alert('evilname')</script>
$translate by itself is not relevant, it can be any HTML string where I want some parts to be replaced with regular JS .replace() with content staying "as text".
So my question is - how can I escape parts of HTML? Do I have to resort to slicing it in parts inside of a view? Or do I have to resort to custom escaping (
Fastest method to escape HTML tags as HTML entities? )? Is there a preferred practice for such things?
Let's start by restructuring your logEntry to separate out the interpolateParams
var translationId = 'Log.' + msg.context.entity_type) + '.' + msg.context.action;
var interpolateParams = {
'object_name': msg.context.object_name,
'user': msg.context.user_name
};
var translated = $translate(translationId, interpolateParams);
return $sce.trustAsHtml(translated);
You want to escape all HTML from interpolateParams but leave any HTML in your translation templates. Use this code to copy the object, iterate over its values and replace with escaped HTML.
var safeParams = angular.copy(interpolateParams);
angular.forEach(safeParams, function(value, key, obj) {
obj[key] = encodeEntities(value)
// if you want safe/sanitized HTML, use this instead
// obj[key] = $sanitize(value);
});
var translated = $translate(translationId, safeParams);
Lastly, the encodeEntities functionality of angular isn't exposed, so we had to borrow the source from angular-sanitize.js
var SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
// Match everything outside of normal chars and " (quote character)
NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g;
function encodeEntities(value) {
return value.
replace(/&/g, '&').
replace(SURROGATE_PAIR_REGEXP, function(value) {
var hi = value.charCodeAt(0);
var low = value.charCodeAt(1);
return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';
}).
replace(NON_ALPHANUMERIC_REGEXP, function(value) {
return '&#' + value.charCodeAt(0) + ';';
}).
replace(/</g, '<').
replace(/>/g, '>');
}
Update: After updating to angular-translate 2.7.0 this message appeared:
pascalprecht.translate.$translateSanitization: No sanitization
strategy has been configured. This can have serious security
implications. See
http://angular-translate.github.io/docs/#/guide/19_security for
details.
Sp instead of the trustlate answer above, angular-translate can accomplish the same result with:
$translateProvider.useSanitizeValueStrategy('escapeParameters');
See the docs for more Sanitize Value Strategies
In your app add
$translateProvider.useSanitizeValueStrategy('escapeParameters');
So that, your code looks like this :
myApp.config(function ($translateProvider) {
//...whatever
$translateProvider.useSanitizeValueStrategy('escapeParameters');
});

Eval a string to a callable JS function

I need to eval a function kept as string and then use it in JS code, here's the scenario:
Step 1
<textarea id="function_text">function test(e){ alert(e.id); }</textarea>
Step 2
var thatFunction = eval($("#function_text").val()); // returns undefined
Step 3
thatFunction.call({id: 100});
Is there a way to do that?
P.S. I'm aware of all security considerations, I just need 100 to be alerted at step 3!
I'm going to give you a warning, don't do it, but here is how:
var thatFunction = Function('return ' + $("#function_text").val())();
thatFunction.call({id: 100});
The code in the string will be evaluated as function declaration and not produce a return value, and that's why eval returns undefined.
You could concatenate the function declaration with your variable assignment:
eval('var thatFunction = ' + $("#function_text").val());
or simply call the function by the name it has (it you know it):
test.call({id: 100});
You might not want to use the variable name in the string. That's not a problem, all you have to do is force eval to consider the function definition as function expression and have it return the function. For example, you can do this by using the grouping operator:
var thatFunction = eval('(' + $("#function_text").val() + ')');
A more obscure way would be the comma operator:
var thatFunction = eval('0,' + $("#function_text").val());

Categories