I'm wondering if there's a Angular2 equivalent to the bracket notation in Javascript that lets you use variables to select an object property. Or a way of doing the same thing.
Consider this code:
let selector = "foo";
let someObject = {foo: "some content", guu: "some other content"};
console.log(someObject[selector]); //bracket notation, logs "some content"
I've tried the following in Angular2:
{{someObject[selector]}}
{{someObject['selector']}}
Neither worked. Is there another way of doing it without Javascript?
This appears to work just fine in Angular: https://stackblitz.com/edit/angular-mrky5n.
Some reasons that it may not be working in your current application:
1) If selector is a variable with a string selector in it, perhaps that selector variable is not available to the scope of the template (e.g. it's private on the component class, defined within a method, or lifecycle hook, etc.
2) If selector is the property you actually want to access, perhaps there is a typo in the name of selector -- maybe the real property is selectro or selecter. This is a reason to not use this approach, since you lose Typescript's type checking. someObject.selectorr throws an error while someObject['selectorr'] goes through fine.
3) That property has not received a value yet -- maybe the safe navigation operator would help.
Angular template syntax supports many JS features, including bracket notation.
{{someObject[selector]}} expression assumes that both someObject and selector are component properties. This won't work if they are local variables that are defined with let.
Related
I am using this script to make a style object of all the inherited, etc. styles.
var style = css($(this));
alert (style.width);
alert (style.text-align);
With the following, the first alert will work fine, but the second one doesn't... it's interpreting the - as a minus I assume. The debugger says 'uncaught reference error'. I can't put quotes around it, though, because it isn't a string. So how do I use this object property?
Look at the comments. You will see that for CSS properties, the key notation is not compatible with a number of properties. Using the camel case key notation therefore is the current way:
obj.style-attr // would become
obj["styleAttr"]
Use key notation rather than dot
style["text-align"]
All arrays in JavaScript are objects and all objects are just associative arrays. This means you can refer to a place in an object just as you would refer to a key in an array.
arr[0]
or the object
obj["method"] == obj.method
A couple things to remember when accessing properties this way:
they are evaluated so use strings unless you are doing something with a counter or using dynamic method names.
This means obj[method] would give you an undefined error while obj["method"] would not
You must use this notation if you are using characters that are not allowed in JavaScript variables.
This regex pretty much sums it up:
[a-zA-Z_$][0-9a-zA-Z_$]*
The answer to the original question is: place the property name in quotes and use array style indexing:
obj['property-with-hyphens'];
Several have pointed out that the property you are interested in is a CSS property. CSS properties that have hyphens are automatically converted to camel casing. In that case you must use the camel cased name like:
style.textAlign;
However this solution only works for CSS properties. For example,
obj['a-b'] = 2;
alert(obj.aB); // undefined
alert(obj['a-b']); // 2
CSS properties with a - are represented in camelCase in JavaScript objects. That would be:
alert( style.textAlign );
You could also use a bracket notation to use the string:
alert( style['text-align'] );
Property names may only contain characters, numbers, the well known $ sign and the _ (thanks to pimvdb).
Use brackets:
var notTheFlippingStyleObject = {
'a-b': 1
};
console.log(notTheFlippingStyleObject["a-b"] === 1); // true
More information on objects: MDN
NOTE: If you are accessing the style object, CSSStyleDeclaration, you must use camelCase to access it from JavaScript. More information is here.
alert(style.textAlign)
or
alert(style["textAlign"]);
To directly answer the question: style['text-align'] is how you would reference a property with a hyphen in it. But style.textAlign (or style['textAlign']) is what should be used in this case.
Hyphenated style properties are referenced via camelCase in JavaScript, so use style.textAlign.
To solve your problem: The CSS properties with hyphens in them are represented by JavaScript properties in camelCase to avoid this problem. You want: style.textAlign.
To answer the question: Use square bracket notation: obj.prop is the same as obj["prop"] so you can access property names using strings and use characters that are forbidden in identifiers.
I think in the case of CSS styles they get changed to camelCase in JavaScript, so test-align becomes textAlign.
In the general case, where you want to access a property that contains non-standard characters, you use array-style: ['text-align']
The object property names are not one-to-one matches for the CSS names.
At first, I wondered why the solution didn't work on my end:
api['data-sitekey'] // Returns undefined
...later on I figured out that accessing data attributes was different:
It should be like this:
var api = document.getElementById("some-api");
api.dataset.sitekey
Example use case:
I have an object with an attribute "myProperty", having getter and setter ("Property Getters and Setters" are supported since EcmaScript 5: https://www.w3schools.com/js/js_es5.asp):
var obj = {
myProperty:'myPropertyValue',
get myProperty(){
return this.property;
},
set myProperty(value){
this.property=value;
}
};
I would like to bind that attribute to a view, which is the task of a custom function that is called bindProperty.
In order to pass the property myProperty to the function, I could do something like
bindProperty(obj, 'myProperty');
However, I would like to avoid to pass the property name as a hard coded String. Strings have the disadvantage, that they are not updated when the attribute name changes during refactoring.
If I would use
bindProperty(obj, obj.myProperty);
the function would only know the property value 'myPropertyValue' and not where the value comes from.
=>How can I pass/identify the property itself, without using a String?
A. Using reflection?
I could imagine something like
bindProperty(obj, ()=>obj.myProperty);
The function bindProperty would then have to do some reflection magic to find out the name of the attribute in the body of the lambda expression (pseudo code):
let attributeName = getNameofArgumentFromBodyOfLambdaExpression(lambda);
obj[attributeName] = 'newValue';
=>Is it possible in JavaScript to evaluate the body of the lambda expression using reflection to get the name of the property?
(I know that this can be done in .NET languages, e.g.
Private Sub BindProperty(Of T)(propertyExpression As Expression(Of Func(Of T)))
Dim propertyName As String = GetPropertyName(propertyExpression)
'...
)
B. Using complex attributes
An alternative whould be that I use wrapping property objects, having their own getters and setters. Howerver, then I would have to use the property like
obj.myProperty.set('newValue')
or
obj.myProperty('newValue') //similar to knockout observables
I still want to be able to use the great Getter/Setter feature. With other words: I want to use my properties like plain attributes:
obj.myProperty = 'newValue'
Therefore, this is not an option for me and I would prefer to use Strings instead of B.
C. Any other alternatives?
An object in javascript is more or less just a mapping of strings or symbols to values. There is no real reflection that you can call upon in the runtime environment that would enable you to move backward from the value to the property name.
If all you need is refactoring, the one way to do this would be to just configure your IDE to recognize string accessors by providing some sort of type information either via Flow or Typescript or something of that sort (the type information is likely what allows reflection to work in languages like .NET). Or you could just settle for a unique prefix like "viewable_propName" and just do simple find and replace if you need to rename.
If you are really focused on getting this to work without type information and in current ES6 syntax, you could do the following:
function getNameofPropFromVal(obj, val){
for(let prop in obj){
if(obj[prop] === val) return prop;
}
}
obj[getNameofPropFromVal(obj, obj.myProp)] = 'newVal';
Though this has shortcomings:
(1) There is no guarantee that two properties won't share the same value.
(2) It introduces unnecessary runtime overhead.
Finally, if you're willing to be cutting edge and use a transformer like babel you could use decorators for your bindProperty method. That way you can just do the binding in the object definition itself. Here is an article explaining the gist and here is the more formal ECMAScript proposal.
I just found following simple work around that might fullfill my needs:
function bindProperty(obj, lambdaExpression){
let expression = lambdaExpression.toString(); // "()=> obj.myProperty"
let subStrings = expression.split(".");
let propertyName = subStrings[1];
console.info(propertyName );
//...
}
I'm trying to work out what is considered valid for the property name of a javascript object. For example
var b = {}
b['-^colour'] = "blue"; // Works fine in Firefox, Chrome, Safari
b['colour'] = "green"; // Ditto
alert(b['-^colour']); // Ditto
alert(b.colour); // Ditto
for(prop in b) alert(prop); // Ditto
//alert(b.-^colour); // Fails (expected)
This post details valid javascript variable names, and '-^colour' is clearly not valid (as a variable name). Does the same apply to object property names? Looking at the above I'm trying to work out if
b['-^colour'] is invalid, but works in all browsers by quirk, and I shouldn't trust it to work going forward
b['-^colour'] is completely valid, but it's just of a form that can only be accessed in this manner - (it's supported so Objects can be used as maps perhaps?)
Something else
As an aside, a global variable in javascript might be declared at the top level as
var abc = 0;
but could also be created (as I understand it) with
window['abc'] = 0;
the following works in all the above browsers
window['#£$%'] = "bling!";
alert(window['#£$%']);
Is this valid? It seems to contradict the variable naming rules - or am I not declaring a variable there? What's the difference between a variable and an object property name?
Yes, objects can be used as maps, and any string can be a property name. As you've discovered, some properties can only be accessed using the bracket syntax.
window['abc']
is accessing a property. It is not a variable, even though it refers to the same value (at the global level) as:
abc
Object property naming rules and variable naming rules are separate. The standard only "reserves" a handful of property names (such as prototype and constructor, IIRC), but other than those, any string goes.
Except when the execution environment (i.e. the browser) decides to add more magic properties, of course. (I hear setting __proto__ breaks some things in quite weird ways)
Every time you create a global variable you create in fact a new member of a global object (which is window in browser environment, global in Node.js, etc.). This is why window.x is exactly the same like (global) var x, this.x or just x.
Understanding JavaScript object like a map is quite right, because: a) you can add a new element dynamically - at any moment; b) the element can have any name - also including special characters, c) you can try to access a non-existing element of an object/map and it is not an error, d) you can remove an element from an object.
If you like to access object members with standard dot notation (eg. a.x) you are not allowed to use any special characters different than _ or $; also the name cannot start from a number. For all other cases you are forced to use square brackets and quotation marks to access object elements.
I've just found the following line in Knockout's source code:
target.subscribe = target['subscribe'] = function …
Why are they assigning the function to the same property twice? The only difference is the way they access it. As far as I know this shouldn't make a difference with the given property name (JavaScript property access: dot notation vs. brackets?).
It's possible that this is done to prevent things breaking when code is minified.
target.subscribe can be minified to something like target.a, however there may be code that relies on target.subscribe still being there. For instance, you might have:
var x = 'subscribe';
target[x](something);
Assigning to both will allow the minifier to do its work, without breaking support for expression access.
I'm trying to work out what is considered valid for the property name of a javascript object. For example
var b = {}
b['-^colour'] = "blue"; // Works fine in Firefox, Chrome, Safari
b['colour'] = "green"; // Ditto
alert(b['-^colour']); // Ditto
alert(b.colour); // Ditto
for(prop in b) alert(prop); // Ditto
//alert(b.-^colour); // Fails (expected)
This post details valid javascript variable names, and '-^colour' is clearly not valid (as a variable name). Does the same apply to object property names? Looking at the above I'm trying to work out if
b['-^colour'] is invalid, but works in all browsers by quirk, and I shouldn't trust it to work going forward
b['-^colour'] is completely valid, but it's just of a form that can only be accessed in this manner - (it's supported so Objects can be used as maps perhaps?)
Something else
As an aside, a global variable in javascript might be declared at the top level as
var abc = 0;
but could also be created (as I understand it) with
window['abc'] = 0;
the following works in all the above browsers
window['#£$%'] = "bling!";
alert(window['#£$%']);
Is this valid? It seems to contradict the variable naming rules - or am I not declaring a variable there? What's the difference between a variable and an object property name?
Yes, objects can be used as maps, and any string can be a property name. As you've discovered, some properties can only be accessed using the bracket syntax.
window['abc']
is accessing a property. It is not a variable, even though it refers to the same value (at the global level) as:
abc
Object property naming rules and variable naming rules are separate. The standard only "reserves" a handful of property names (such as prototype and constructor, IIRC), but other than those, any string goes.
Except when the execution environment (i.e. the browser) decides to add more magic properties, of course. (I hear setting __proto__ breaks some things in quite weird ways)
Every time you create a global variable you create in fact a new member of a global object (which is window in browser environment, global in Node.js, etc.). This is why window.x is exactly the same like (global) var x, this.x or just x.
Understanding JavaScript object like a map is quite right, because: a) you can add a new element dynamically - at any moment; b) the element can have any name - also including special characters, c) you can try to access a non-existing element of an object/map and it is not an error, d) you can remove an element from an object.
If you like to access object members with standard dot notation (eg. a.x) you are not allowed to use any special characters different than _ or $; also the name cannot start from a number. For all other cases you are forced to use square brackets and quotation marks to access object elements.