AngularJS Directive Not Evaluating Object Properly - javascript

I'm using objects improperly somehow. Basically, I want:
angular.module('mobileDashboardApp')
.directive('localForageModel', function ($localForage) {
return {
link: function postLink(scope, element, attrs) {
scope.$watch(attrs.ngModel, function () {
$localForage.setItem(attrs.localForageModel, scope[attrs.ngModel]);
console.log(attrs.ngModel);
console.log(scope[attrs.ngModel]);
console.log(scope.user.companyId);
console.log(scope["user.companyId"]);
});
}
};
});
to output
user.companyId
dsf
dsf
dsf
instead of the current output which is:
user.companyId
undefined
dsf
undefined
Can anyone point me in the right direction? Or suggest a better title for this?

You have incorrect notation, it must be
var props = attrs.ngModel.split(".");
scope[props[0]][props[1]]
As dot notations are not valid for dynamic properties, so object['abc.def'] must be written as object['abc']['def']
Side-note, definitely you should have some kind of object property checkings, for example, if your ngModel attribute is not abc.def - this will throw exception, so better have generic function for this

In JS, there's a difference between property name containing . (dot) and nested objects accessed by dot. So scope.user.companyId is something different than scope["user.companyId"]. You can have JS object like this:
{
user: {
companyId: 1
},
"user.companyId" : 2
}
So, if you really need to access property having string that represents path within the object (not single property name) you need to parse it. There are many ways to do this. Naive way would be something like this:
function getProperty(obj, pathString) {
var properties = pathString.split(".");
var result = obj;
for (var i in properties) {
result = result[properties[i]];
}
return result;
}
In your case can be used like this: getProperty(scope, "user.companyId").

The way you're accessing the object is incorrect in e.g.
console.log(scope["user.companyId"]);
This should be
console.log(scope["user"]["companyId"]);
I hope this helps you :-)

Related

How to destructure the return value of a function?

I'm looking for a better syntax for writing the following code, and I would like to know if there is an option for assigning the return value of a function by using a destructuring assignment:
const object = {
property: 10,
getFunction() {
return "getFunction value";
}
}
const {property, getFunction} = object;
console.log("Property: ", property, " getFunction: ", getFunction);
Here, this code returns the following, which is totally normal:
"Property: 10, getFunction: [Function: getFunction]"
I'd like to know if there is a syntax option to write something like: (won't work)
const {property, getFunctionValue: getFunction()} = object;
And get the "getFunction value" from the assignment.
Unfortuntely, the syntax you're looking for doesn't exist (I've also wanted to do it many, many times). You can't call a function you're retrieving as part of a destructuring operation.¹ You're not allowed to use an arbitrary expression for the "from" part of a destructuring pattern. Destructuring always does property access, not function calls.
You'll have to do it separately, e.g.:
const { property } = object;
const getFunctionValue = object.getFunction();
or similar.
¹ unless it's the getter function for an accessor property

Replace String In Object Property Value After Looping Array Javascript

I'm needing to replace a character in an object's property value. I'm looping over the array of objects (outputting to console) and retrieving the FeatureUrl property.
I have data coming back from the Svc for that property in the following form:
index.html#/blah
I'm needing to replace the '#' with '#/app' so that my new url comes back in the following form:
index.html#/app/blah
I'm not sure if .replace is the right method to use here, but it is what I have seen suggested. Can someone point me in the right direction?
var localFeatureDetails = function() {
$scope.user = userService.GetUserInformation();
$scope.featureDetails = $scope.user.Features;
var featureUrlRewrite = function () {
var index;
var urlCount;
for (index = 0; index < $scope.featureDetails.length; index++) {
urlCount = $scope.featureDetails[index];
urlCount.FeatureUrl.replace("#","#/app");
console.log(urlCount);
}
};
featureUrlRewrite();
};
localFeatureDetails();
I did not test your code but based on how .replace() works you have to assign the value to your object property again by overriding it otherwise you're not saving the value.
Assuming everything else is correct, try this:
$scope.featureDetails[index].FeatureUrl = urlCount.FeatureUrl.replace("#","#/app");
Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace
I think that you might want something more like this
$scope.featureDetails.map(detail => angular.merge({}, detail,
{ FeatureUrl: detail.replace('#', '#/app') }))
instead of the bulky for loop.
we take every detail object out of the featureDetails array, access the FeatureUrl property and replace the # with #/app
.merge(destination, ...sources) merges sources into destination, left-to-right, overwriting properties as it goes.
so angular.merge({}, {foo: 5, bar: 3}, {foo: 7}) would return { foo: 7, bar: 3 }
Removed the console.log() but you can always
console.log($scope.featureDetails);
and then look at the object that is returned in the inspector.

How to declare nested objects in JavaScript?

I'm trying to create an object that contains an object, so think of it as a dictionary:
var dictionaries = {};
dictionaries.english_to_french =
{
{english:"hello",french:"bonjour"},
{english:"i want",french:"je veux"},
{english:"bla",french:"le bla"}
};
but it gives the error Uncaught SyntaxError: Unexpected token {
what am I doing wrong?
Thanks !
Edit
I'm sorry that I did not clarify what I want to do.
Edited the code above.
You're trying to give your object a property, and that property will be a single object:
dictionaries.english_to_french =
{english:"hello",french:"bonjour"}
;
You don't need the extra { }. You could declare the whole thing at once:
var dictionaries = {
english_to_french: {
english: "hello", french: "bonjour"
}
};
I would suggest that a better format for your dictionaries might be:
var dictionaries = {
english_to_french: {
"hello": "bonjour",
"chicken": "poulet", // ? something like that
"Englishman": "rosbif"
}
};
That way you can look up words directly without having to search. You could then create the reverse dictionary from that:
dictionaries.french_to_english = function(dict) {
var rv = {};
for (var eword in dict)
rv[dict[eword]] = eword;
return rv;
}(dictionaries.english_to_french);
In order to nest two or more objects, the objects need to have an attribute assigned to them. For example,
{
"hello":{
"english":"hello",
"french":"bonjour",
"portuguese":"ola"
},
"good day":{...},
"how are you":{...}
}
"hello" at the beginning of the object would be the attribute. Then the object is its value. So that way you can access the object by accessing its attribute. Just putting an object in an object does not work. That's why you're getting your error.

Reference another field inside a javascript object

I have an object in javascript:
admins: {
articles: {
path: '/admins/articles',
template: '/views/admins/articles.html',
link: function() {
return path; // !!! how to reference the 'path'?
}
}
}
I have a lot of objects like this, and each of them has a path field and a link function. I want to use the field path in link, but I can't just use path.
What should I do?
You can use this to reference the object. Standard object.method() "dot" syntax will set this to object within method:
var someObj = {
admins: {
articles: {
path: '/admins/articles',
template: '/views/admins/articles.html',
link: function() {
return this.path; // !!! how to reference the 'path'?
}
}
}
};
var returnedPath = someObj.admins.articles.link();
Demo: http://jsfiddle.net/2Pt7n/
(There are other ways to call a function such that this will not be set to the appropriate object, but I hope they don't apply here - you don't really say how you're using the objects or calling the function, but if not in the way I showed then please update your question and I'll update my answer accordingly.)
I'll just point out that you don't want to use ES6 fat arrow here, because there will be no this pointer in that case:
var someObj = {
admins: {
articles: {
path: '/admins/articles',
template: '/views/admins/articles.html',
link: () => {
return this.path; // 'this' is undefined
}
}
}
};
someObj.admins.articles.link() === undefined
What you are showing is not JSON. It is a Javascript object, which is different than JSON. JSON is a strictly defined data serialization format that is a subset of Javascript object literals.
Javascript provides no syntax for referencing peer properties in an object literal, as you want to do. Naming them is one idea, but it won't help, because the name won't exist while the literal is being defined, so the name is not available for you to use in the literal itself.
Also, note that the syntax you define makes the object lop-sided: you can access path as obj.admins.articles.path, but link is a function you would have to invoke: obj.admins.articles.link().
I won't talk about how this is not JSON (others covered it well).
You can do this to get path:
return admins.articles.path;
Here's a fiddle to show it working: http://jsfiddle.net/UwbLt/
I'm reading the answers and even understanding the point of some users (that JSON should be used just for data) and agreeing that this is correct, I just created a proof of concept example. Take a look.
// just a regular object
var obj = {
a: "aaa",
b: "bbb",
c: function() {
return this.a;
}
};
console.log( obj.c() ); // prints "aaa"
// isn't it json just because it has a function? ExtJS will treat it like JSON, but jQuery not
var json = "{" +
"\"a\": \"aaa\", " +
"\"b\": \"bbb\", " +
"\"c\": function() {" +
" return this.a;" +
"}" +
"}";
// ok, the "json" above
console.log( json );
//var jsonObj = $.parseJSON( json ); // does not work
//var jsonObj = eval( json ); // does not work too
var jsonObj = Ext.decode( json ); // it works! shortcut for Ext.JSON.decode
console.log( jsonObj.c() ); // prints "aaa"
It is almost the same that nnnnnn posted, but I think I would post it too, just to complement the answers. jsFiddle: http://jsfiddle.net/davidbuzatto/rhKAM/
So I think, even contradicting the definition of JSON, that JSON maybe can have (or should have?) the same characteristics of a object created using the regular object initializer sintax, since its name is JavaScript Object Notation, not "Lightweight" Object Notation. I know, I know, a deserializer won't be able to deserialize a function depending on the target language, but why ExtJS supports this "behavior"? A good discussion can be found here: Is it valid to define functions in JSON results?
Just to clarify. I don't use (and I won't use too) functions inside my JSONs.

JavaScript Function arguments

I am trying to create a function that deserialises a JSON object and creates some functions, but I want to be able to specify a variable set of arguments.
For example, in JSON I want to specify something like:
{
"handler" :
{
"args" : ["evt","value"],
"content" : "console.log(value);"
}
}
And when I parse the object, I will do something like:
var myFunction = new Function(handler.args,handler.content);
But the challenge is that each argument is supposed to be a n strings followed by the content of the function as the last argument. Is there any easy way of specifying n number of arguments in a new Function()?
To solve the technically issue: You can use apply [docs].
handler.args.push(handler.content);
var myFunction = Function.apply(null, handler.args);
However the question is why you are doing something like this? What is the context? Spontaneously I would say you should consider another solution for whatever problem you are trying to solve ;)
According to MDN
Parameters
arg1, arg2, ... argN
Names to be used by the function as formal argument names. Each must be
a string that corresponds to a valid
JavaScript identifier or a list of
such strings separated with a comma;
for example "x", "theValue", or "a,b".
So the arguments list can either be one or more strings seperated by commas, or just one string with each identifier in it seperated by commas.
Also since
['evt', 'value'].toString() == 'evt,value'
Simply passing your handler.args array as the first argument to the new Function constructor should work exactly as you want it to
new Function(handler.args, handler.content);
Internally, new Function casts every argument to a string if it is not already one. So conceivably something like this would also work
new Function({ toString: function() { return 'a,b,c' } }, 'return a+b+c');
Not that I'm suggesting you do anything silly like that.
This works in every browser I've tried including IE
I think the simplest route would be to combine the 2 properties. Then use apply to construct the function.
var x = {
"handler" :
{
"constructorArgs" : [
"evt",
"value",
"alert(value);"
]
}
};
var f = Function.apply(undefined, x.handler.constructorArgs);
f(1, 2);
To keep it similar you can use Array.prototype.concat.
var x = {
"handler" :
{
args: [ "evt", "value" ],
content : "alert(value);"
}
};
var f = Function.apply(undefined, x.handler.args.concat(x.handler.content));
f(1, 2);
Can;t you just make the body of your functions work with the arguments property?
http://jsfiddle.net/9XcEb/
var add = new Function(
"var total=0;"+
"for (var i=0; i < arguments.length; i++) {"+
"total+=arguments[i]"+
"};"+
" return total"
);
alert(add(3,4,5,6));

Categories