I am reading this Angular doc, it says:
With any object-like expression—such as object, Array, Map, or Set—the identity of the object must change for Angular to update the class list. Updating the property without changing object identity has no effect.
So, how do I change an object't identity?
Thanks.
The wording there is slightly unfortunate. You can't really change an object's identity - the identity is that object. It's like trying to make X not X. What it means is that a new object or array needs to be created for Angular to detect it as different. For a tiny example in vanilla JS:
const isSameObject = (obj1, obj2) => obj1 === obj2;
console.log(isSameObject({}, {})); // false, different objects
const someObj = {};
const sameReferenceToSomeObj = someObj;
sameReferenceToSomeObj.newProp = 'newVal';
console.log(isSameObject(someObj, sameReferenceToSomeObj)); // true, same object
For Angular to detect a change the identity on a property, the property value must be changed to a new value, rather than having the old value mutated. The second example in the snippet above is an example of how not to do things in Angular; mutating an object doesn't change its identity (it's still the same object), so if you did that in Angular, it would see that the old object on the property is === to the new object on the property, and would not produce a visual change as a result.
Create an entirely new object instead, with the desired properties, and put that object on the component, and then Angular will be able to see that the new object is not === to the old object, and take the appropriate action as a result, eg
this.theClassList = { ...this.theClassList, newProperty: 'foo' }
and not
const newClassList = this.theClassList;
newClassList.newProperty = 'foo';
this.theClassList = newClassList;
Related
I'm having a strange situation in JavaScript where I am creating an Object and then passing it as an Argument to a Function which then updates the values and then returns a new updated Object.
function objBuild() {
var obj_old = {}; // original object
var obj_new = {}; // updated object
// set values for original object
obj_old.val01 = "val01_old";
obj_old.val02 = "val02_old";
obj_old.val03 = "val03_old";
// set values for new object using
// the original object as a template
obj_new = objUpdate(obj_old);
console.log(obj_old); // this shows obj_new data which I don't want
console.log(obj_new); // this shows obj_new data which I want
}
function objUpdate(obj) {
obj.val03 = "val03_new";
return obj;
}
I was expecting the new Object to be updated with the new Value, which it is, however the old Object is updated as well.
Maybe the function is taking the old Object as a reference and even though I'm returning a separate value it remembers what's happened to it?
I'm not sure but if that is the case is it possible to keep the old Object intact?
Please read: Is JavaScript a pass-by-reference or pass-by-value language?
In javascript, objects are "technically" passed by "reference". Hence, the original value is altered, because obj is, in fact, the original object.
You need to clone the object, and there is a vast scenario about "how" you should do that, since object may be shallow copied or deep copied. Read more about that here: What is the most efficient way to deep clone an object in JavaScript?
and here: How do I correctly clone a JavaScript object?
Anyway, to fix your issue, just use Object.assign on a brand new object to copy the values of the original object, for your case, it's just enough, despite I recommend you to read the above posts to learn when and how to properly copy objects.
function objBuild() {
var obj_old = {}; // original object
var obj_new = {}; // updated object
// set values for original object
obj_old.val01 = "val01_old";
obj_old.val02 = "val02_old";
obj_old.val03 = "val03_old";
// set values for new object using
// the original object as a template
obj_new = objUpdate(obj_old);
console.log(obj_old); // this shows obj_new data which I don't want
console.log(obj_new); // this shows obj_new data which I want
}
function objUpdate(obj) {
var _cloned = Object.assign({}, obj);
_cloned.val03 = "val03_new";
return _cloned;
}
objBuild();
You're not actually creating a new object, you're setting the old object to the new object. So you're correct, the old object value is still being referenced.
If you want to make a new object, and you don't have any nested objects, I would use Object.assign(). This makes a shallow copy of the object. You could do something like this:
obj_new = objUpdate(Object.assign({}, obj_old));
This will create a new object with the enumerable properties of the old object.
If you have nested objects, these will still be copied by reference, so I would loop over the object and copy the properties that way.
Remember that objects, including arrays are passed by reference while strings, booleans and numbers are passed by value.
Here, you are passing object(by reference) so it is modifying the values of old object. Both old and new objects are pointing to the same value.
function objBuild() {
var obj_old = {}; // original object
var obj_new = {}; // updated object
// set values for original object
obj_old.val01 = "val01_old";
obj_old.val02 = "val02_old";
obj_old.val03 = "val03_old";
// set values for new object using
// the original object as a template
obj_new = objUpdate(obj_old);
console.log(obj_old); // this shows obj_new data which I don't want
console.log(obj_new); // this shows obj_new data which I want
}
function objUpdate(obj_old) {
var obj = JSON.parse(JSON.stringify(obj_old));
obj.val03 = "val03_new";
return obj;
}
objBuild();
So I have been looking at Object.freeze() and Object.seal().
Object.freeze() - will make all existing properties non-writable, and will not allow any new properties to be added.
Object.seal() - "Sealing an object prevents new properties from being added and marks all existing properties as non-configurable."
I am looking for a way to make all existing properties "frozen" (non-writable), but allow new properties to be added.
Is there shorthand for doing that?
The manually way of doing what I want is:
let freezeExistingProps = obj => {
Object.keys(obj).forEach(k => {
Object.defineProperty(obj, k, {
writable: false
});
});
};
The above function works surprisingly well to freeze existing top-level properties on an object (it doesn't overwrite them, just changes them to non-writable), but I am hoping there might be a more official/quicker way to do the above.
You might do the following:
instance -> frozen static proto -> dynamic proto
Some sample:
function freeze(stat,dyn){
Object.setPrototypeOf(stat,dyn);
Object.freeze(stat);
}
var a={unchangeable:1};
var b={changeable:2}
freeze(a,b);
Now have a look at a and change some b props.
Well, if you want to do it in the manner of freeze, then freezing it immediately, and setting up to a prototype of another object might help, but it will return a copy (pointing to the original object as prototype), exactly in the form how you want. there are obviously some pros and cons, as the properties will not be the immediate properties, but we can find it out by its __proto__ if we need all the keys (assuming you have a dedicated use case)
So, just another try
function freezeExistingProps (obj){
var OBJECT = function(){};
Object.freeze(obj)
OBJECT.prototype = obj;
return new OBJECT();
}
You may want to consider cloning your object into a new one with extra attribute. It's also a very good practice (look for immutability).
An example:
const setAge = (person, age) => ({ ...person, age });
const person = {
firstName: 'Luke',
lastName: 'Skywalker',
};
const personWithAge = setAge(person, 24);
I am using Map
because I want to store an object as a key.
My question is - can I access a map the same way I would access a plain object?
For example:
let m = new Map();
let obj = {foo:'bar'};
m[obj] = 'baz';
console.log(m[obj]);
is this supposed to work correctly as is, or do I need to use the get/set methods of a Map?
The reason I ask is because if I need to use get/set it forces to me to carefully refactor a lot of code.
Here is a real life example of code that may need to be refactored:
// before (broker.wsLock was plain object)
function addWsLockKey(broker, ws, key) {
let v;
if (!( v = broker.wsLock[ws])) {
v = broker.wsLock[ws] = [];
}
if (v.indexOf(key) < 0) {
v.push(key);
}
}
// after (broker.wsLock is a Map instance)
function addWsLockKey(broker, ws, key) {
let v;
if (!( v = broker.wsLock.get(ws))) {
v = [];
broker.wsLock.set(ws, v);
}
if (v.indexOf(key) < 0) {
v.push(key);
}
}
is there some way to set v on the same line as the set() call?
If you want access to the actual values of the Map object, then you have to use .get() and .set() or various iterators.
var m = new Map();
m.set("test", "foo");
console.log(m.get("test")); // "foo"
Regular property access on a Map such as:
m["test"] = "foo"
just sets a regular property on the object - it does not affect the actual map data structure.
I imagine it was done this way so that you can access the Map object properties separately from the members of the Map data structure and the two shall not be confused with one another.
In addition, regular properties will only accept a string as the property name so you can't use a regular property to index an object. But, map objects have that capability when using .set() and .get().
You asked for a "definitive" answer to this. In the ES6 specification, you can look at what .set() does and see that it operates on the [[MapData]] internal slot which is certainly different than the properties of an object. And, likewise, there is no where in that spec where it says that using normal property access would access the internal object [[MapData]]. So, you'll have to just see that normal property access is describe for an Object. A Map is an Object and there's nothing in the Map specification that says that normal property access should act any different than it does for any other object. In fact, it has to act the same for all the methods on the Map object or they wouldn't work if you happened to put an item in the Map with the same key as a method name. So, you're proof consists of this:
A simple test will show you that property access does not put anything in the Map itself, only a regular property.
The spec describes a Map as an object.
The spec describes how .get() and .set() operate on the internal slot [[MapData]].
There's nothing in the spec that says property access on a Map object should work any different than it always does.
If property access did access the MapData, then you would not be able to access methods if you happened to put a key in the Map that conflicted with a method name - that would be a mess if that was the case.
What is the difference between a JS:
Object, Property and Variable ?
Sorry I'm new to JavaScript but from the way I'm understanding it is a Variable is a container to store information/data types yes ?
An object is a variable but with several different properties (whereas with a variable you have one property)? name:value pairs
a property is the building blocks of objects? is that what makes an Object an Object? because it is a variable with several name:value pairs? ........
I'm supper confused!!! are all three the same are they like interchangeable?
the only example I can think of is
Human body:
Cells
Tissues
Organs
-organs are made up of tissues
-tissues are made up of cells
-cells are tissues, basically lots of cells make up tissues and lots of tissues make up organs.
So basically organs are also cells but they are made up of a lot of cells?
I'm a bit dumb and slow when it comes to learning can somebody please enlighten me?
Explain the differences between them in very simple basic language like your explaining it to a 10 year old or something please
answers much appreciated,
Thanks :)
ps There may be a part 2 to this question
the way I'm understanding it is a Variable is a container to store information/data types yes ?
Almost. A variable is a container that stores a value. Each value is of a specific data type. Common types are number, string and Boolean.
Example:
var userID = 42;
userID is a variable. It contains the value 42. 42 is a number value, i.e. it is of type number.
A JavaScript object is a value of type object. Objects are not just simple, scalar values, they are "container" values. They can themselves contain multiple different values.
Essentially objects are key-value stores, i.e. they contain one or more keys associated with a value. These key-value pairs are called properties.
Example:
var record = {
name: 'Paul',
age: 42
};
record is a variable. It contains an object as value. The object has two properties, name and age. name holds a string value, age a number value.
When one refers to 'variable' one typically imagine a container with some memory to hold a value.
Objects are variables too but dynamically transform to containers of primitives or more-complex values upon Assignment! Complex values can be objects that hold primitive data types or even other objects such in the example below :
var SNOCounter; //gives undefined ^
SNOCounter = 3;
AccObjVar = {firstName:"John", lastName:"Smith"}; //creates an JS "object" variable with two "properties" that hold 'string' type values
AccountWrapperObj = {SNO:SNOCounter,AccountName:AccObjVar};
The dynamism of object properties is such that although AccountWrapperObj which is a JS Object holds a primitive value and Object as its original value. Replacing the AccountName property with an integer can be done by just assigning it the integer value (the properties have dynamic data types just like variables in Javascript)
AccountWrapperObj.AccountName= 'Albert Einstein'; // changes the type of AccountName from AccObjVar object type to a String
----------Extra Info ---------------
^ I am not quite clear on the memory assignment part at this stage. Link says there needs to be a bare minimum memory here for referencing the variable and actually assigning it a value.
Does declaring a variable in JavaScript with var without assignment consume memory?
A variable is a binding to a value in memory and not an object.
The item in a box analogy isn’t quite right. I think that it’s more along the lines of two tin cans connected by a string, one can being the reference(variable) and the other being the value.
I'm also new to JS, so I'll tell what's helping me here
one thing that's helping me is to think about variables as 'labels', something temporary related to execution (a metaphor from Luciano Ramalho's Fluent Python book...), and not as 'boxes', a metaphor that I've seen in a lot of tutorials
so variables are temporary, and related to execution of some function, or of the whole script (depending of where they're declared... see the difference of var, let and const for more about this)
properties, on the other hand, are related to objects, attached to the obj while it or the property exists; so you cannot create a property that's not related to an obj
let myObj = {}; // 'myObj' is the 'label' of the obj we're creating
myObj.prop = true; // we create 'prop', a property of 'myObj', using the dot notation
almost everything in JS is an obj, and objs are custom types/structures for data; functions are also objects, but they're a 'special' kind of obj, we can create and return objects with them (we can execute functions to create/return objs); so
let foo; // declaring an empty variable; the word let is related to the scope of the variable, you can use var, let or const when declaring variables
foo = function(){ return {}; }; // foo returns an empty obj
myObj = foo(); // we execute foo() so that myObj is again an empty obj
the value of a property can also be an object, so we can also do
myObj.foo = function(...args){ // receives no or more arguments
const createProps = (el, i) => { // declares a variable and defines an arrow function
this[`prop${i+1}`] = el; // uses the index of the argument to create the name of the property, with the argument value
}
args.forEach(createProps); // for each arg, create a property
}
myObj.foo('some', 'new', 'properties'); // execute the function, creating new properties in 'myObj'
above, the function that creates properties for my myObj is part of myObj, as a property...
so objects and properties have to do with data structuring, how I relate the different kinds of data in my code; and functions and variables - these 'temporary labels' - have to do with execution, doin' stuff, creating objs, and so on... both 'portions' workin' together, of course
Ok, I'm trying to track any changes made to a huge form on a web application. When the page is loaded, I create a JS object that 'captures' the initial state of all input fields (selects, radio buttons, checkboxes etc...).When the user alters the value of any of the literally hundreds of input elements, the new value is tracked in a second object. When the user clicks Update, these two objects are compared and only those values that have been changed are sent, to update the data accordingly.
Rather then building 2 completely separate objects, I thought it wise to use inheritance:
var initialState = getInitialState();//eg:{bar:'1',foo:'bar',atom:'bomb',some:{nested:'objects'}}
var tracker = Object.create(initialState);
As things go, the tracker object might end up looking something like this:
{bar:'0',foo:'bar',atom:'peace',some:{nested:'objects'}}
When calling JSON.stringify on this object in FF and chrome, all is well: only the objects' own properties are returned. Not so in IE: the tracker has no prototype property, so it would appear that Object.create creates copies rather then inheritance chains?tracker.__proto__ === initialState returns true, whereas tracker.prototype === initialState evaluates to false, in fact the tracker.prototype property is undefined.
Question1: is there an alternative way to set up an inheritance chain in IE that allows me to peel away the unchanged prototype values?
Question2:I'd also like a method -if at all possible- to set up an inheritance chain that allows for nested objects. As things are now, the nested objects are dealt with by iterating over the main object, using a recursive function. Kind of silly, since that's what I'm trying to omit.
In short:I want to know if this is out there:
var a = {bar:'1',foo:'bar',atom:'bomb',some:{nested:'objects'}};
var b = Object.magicDeepCreate(a);//<=== preferably X-browser
b.bar = '0';
b.bar.some.nested = 'stuff';
console.log(JSON.stringify(b));
//{"bar":"0","some":{"nested":"stuff"}}
As always: no jQuery tag, means no jQuery Note: by IE I mean that monstrosity IE8, not IE9 (company policy, sadly)
tracker.__proto__ === initialState returns true, whereas tracker.prototype === initialState evaluates to false, in fact the tracker.prototype property is undefined.
The __proto__ property is non-standard and FF-only. To get the prototype object of an object, use Object.getPrototypeOf(). The prototype property of function objects is a property referencing the object from which all instances of that function (created using new) inherit.
Not so in IE
Object.create() is not supported at all in IE8. Did you use the common shim or does it silently fail? Or did you even use a function that really copies all properties?
Object.magicDeepCreate(a), preferably X-browser
That should be simple, assuming that all target browsers implement Object.create:
Object.deepCreate = function deepCreate(o) {
var res = Object.create(o);
for (var i in o)
if (Object.hasOwnProperty(o, i) && typeof o[i] == "object")
res[i] = deepCreate(o[i]);
return res;
};
stringify only those that have been altered
That should be standard behaviour of JSON.stringify - the prototype object is not taken into account.
However, I'm not sure why you need inheritance at all for that tracker object. Just use an empty object, and add all properties that have been altered. If you want to delete those that have been reset to initial state, you could store that in an extra object to compare with - but there is no reason for inheritance. Just use:
Object.emptyStructure = function s(o){
var res = {};
for (var i in o)
if (typeof o[i] == "object")
res[i] = s(o[i]);
return res;
};
var initialState = getInitialState();
var tracker = Object.emptyStructure(initialState);
// set:
if (newVal == initialState.some.nested)
delete tracker.some.nested;
else
tracker.some.nested = newVal;