A quick look at Angular Signal Forms
A brief, practical tour of the new Signal Forms in Angular—setup, validation, template binding, and submission.
A guide to copying JS arrays & objects; safely
Louis la Grange - 3 min read
JavaScript is as well known for its object notation as it is notorious for its nuances. For example, did you know that [] == ![] will equate to true and not false as you might expect!? See this list on GitHub for an explanation and more examples.
Some time ago I encountered a strange outcome when trying to copy an array in JavaScript. For most of my development career a simple spread operator [...array] would suffice all of my array-copying needs. Very handy!
Now to explain my problem I'm going to have to get a little bit technical. The array I was trying to copy looked something like the following:
[ { prop: "dummy", anotherprop: 123, subArray: [ { title: "one", type: "DIGIT", value: 1 }, ...more objects... ] }, ... more objects... ]
NOTE: The parent array contains a set of objects. Each of these objects contain another array subArray with their own set of objects.
To my surprise, when I copied the parent array with the spread operator, the subArray still referenced the ORIGINAL array's subArray. This is a problem if you're trying to edit the copy while keeping the original intact.
I don't recall having an issue with the spread operator until now and looking back revealed that I either didn't notice this behaviour before, or I haven't worked with such complex nested arrays/objects before. This was new behaviour to me that warranted some investigation.
The very trusty MDN documentation for the spread operator came to my rescue. It states:
Spread syntax effectively goes one level deep while copying an array. Therefore, it may be unsuitable for copying multidimensional arrays.
Aha, the answer!
Now I know the why and reading further started to reveal the solution.
The same is true with
Object.assign()— no native operation in JavaScript does a deep clone.
Deep clone? I've never heard of this term before. After doing digging some more I realised that I had to go down to the deepest level of my array/object and perform the copy from there.
I accomplished this by iterating over all of the elements in the parent array with a .map(). I then performed a spread for each parentElement, and manipulated the subArray with another iterable, such as .filter()
...map( return { ...parentElement, subArray: parentElement.subArray.filter(...) } )
This finally produced the result I was looking for, copying the parameters one-by-one instead of referencing the object, but it wasn't pretty.
Luckily a much better solution was revealed in the last sentence of the paragraph for the spread operator.
The Web API method
structuredClone()allows deep copying values of certain supported types.
Even though JavaScript has no native deep cloning function, there are plenty of libraries (think es-toolkit -> cloneDeep()) that do. Here I learned that the Web API has introduced this function as well!
The Web API - for those unfamiliar with it - is built directly into your browser exposing lots of useful functions to your JavaScript application. The structuredClone() function was first introduced into the HTML DOM API for Deno 1.14 and Node.js 17.0.0 by October 2021 and has since become supported by all major browsers around February 2022.
So in a way structuredClone() now IS “natively" supported and it should save you some hassle going forward. No more JSON.parse(JSON.stringify())!!
https://developer.mozilla.org/en-US/docs/Web/API/structuredClone https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Introduction#what_are_apis