When you use Array.push, it creates a copy that duplicates all nested elements,

Situation

Currently, I am developing a web application using Typescript/Angular2 RC1. In my project, I have two classes - Class1 and Class2. Class1 is an Angular2 service with a variable myVar = [obj1, obj2, obj3]. On the other hand, Class2 is an Angular2 component where I obtain myLocalVar = class1.myVar in the constructor by utilizing viewInjector to inject the service.

Dilemma

In Class2, I perform a copy of myVar using a push method which results in a shallow copy (copying objects). This means that myLocalVar becomes equal to [obj1, obj2, obj3] where each object has a property like {number:X}. The issue arises when I modify one item such as: myVar[0] = {number:5} in Class1. These changes do not reflect in Class2's myLocalVar and consequently, myLocalVar[0].number still holds the value of 1. Strangely, the original array myVar in Class2 gets updated. It seems that the line

this.myLocalVar.push(this.myVar[int]);
created a deep copy instead of a shallow copy.

Is there any change in the behavior of the push method in Javascript due to Typescript? If affirmative, what steps should be taken to maintain the reference and execute a shallow copy?

Sample Code

@Injectable()

export class Class1{

    public myVar:any[];

    constructor(){
        this.myVar = [{number: 1}, {number: 2}, {number: 3}];
    }

    changeVar(newVar):void{
        this.myVar[0] = newVar; // newVar = {number: 5}
    }
}

@Component({
    selector: 'class2',
    viewInjector: [Class1],
    templateUrl: 'someURL'
})
export class Class2{

    private myLocalVar:any[] = [];

    constructor(class1: Class1){
        this.myVar = class1.myVar;
    }

    showVars():void{
        console.log(this.myLocalVar[0]);
        console.log(this.myVar[0]);
    }

    ngOnInit(){
        for(let int = 0; int < this.myVar.length; int++){
            this.myLocalVar.push(this.myVar[int]);
        }
    }
}

The console.log will display two distinct values: console.log(this.myLocalVar[0]) will output 1, while console.log(this.myVar[0]); will showcase 5 after someone calls changeVar()!

Update

To better understand the issue, you can refer to this Plunker link.

  • Click on show, check your console, and observe identical values
  • Click on change
  • Click on show again to witness different values for myVar and myLocalVar

I require a solution where every time I alter myVar[x]=newObj, myLocalVar mirrors that modification. Kindly provide a resolution to this problem if you seek validation for your response.

Answer №1

Modify

ngOnInit(){
    for(let i = 0; i < this.myVar.length; i++){
        this.myLocalVar.push(this.myVar[i]);
    }
}

to

ngOnInit(){
  this.myLocalVar = this.myVar;
}

In the first example, only the reference of the array elements is copied. In the second example, the reference of the entire array is being copied.

Answer №2

It seems like you are trying to update the values of an object, but instead of doing that, you are creating a new object and replacing the original one in the array.

To achieve your intended result, you can use the following code:

changeVar(newVar: Object): void {
    this.myVar[0].number = newVar.number;
}

The Plunker code you provided is different from the code shown here. The Plunker code is passing a number instead of an object. It looks like this:

changeVar(newVar): void {
    this.myVar[0] = {number:newVar}; // newVar = {number: 5}
}

In this case, you should modify the code to:

changeVar(newVar: number): void {
    this.myVar[0].number = newVar;
}

Answer №3

The method "Object.assign" is used to create a shallow copy of an object.

(<any>Object).assign(this.shallowCopyObject, this.myObject);

Answer №4

The issue at hand arises from the fact that myLocalVar and myVar are distinct references, essentially serving as separate array containers.

When you transfer an element from myVar to myLocalVar, you are essentially moving the references of those elements from myVar into myLocalVar.

For instance, if myVar = [{name: 1}, {name: 2}], both objects exist within it with their respective references being pushed into myLocalVar.

Upon executing the changeVar method, you are effectively replacing the reference of the element in myVar with a new object reference. Nonetheless, the previous reference remains intact and is still linked through myLocalVar, leading to the observed outcome.

To rectify this situation,

You should reconsider how your changeVar method assigns values. Instead of this.myVar[0] = {number:newVar};, opt for

this.myVar[0]['number'] = newVar;
as it helps maintain the reference structure.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Resolving issues with jQuery's live() method while incorporating AJAX calls

One of the challenges I'm facing is with buttons on a webpage that are part of the class "go". The code snippet below demonstrates how I handle actions related to these buttons: $(".go").live('click', this.handleAction); The issue arises w ...

Is it possible to inject JavaScript into the DOM after it has been loaded using an AJAX call?

I have a specific div element identified by the id #id1 that contains clickable links. Upon clicking on these links, an AJAX call is made to retrieve additional links from the server. My current approach involves replacing the existing links within #id1 w ...

Unveiling all Gatsby.js routes/endpoints in Cypress tests: A comprehensive guide

I am currently in the process of creating end-to-end tests with Cypress for a project built on Gatsby. One specific test requires me to iterate through all pages of my Gatsby site. To accomplish this, I require a test fixture (such as endpoints.json) cont ...

What prevents `console.log` from working within a button click event?

Why is this not functioning correctly? <button (click)="console.log('ok');">Display Details</button> The error message reads: Cannot read property 'log' of undefined However, a console.log statement in the class construc ...

Exploring the integration of react-leaflet with Nextjs: A step-by-step guide

Hello everyone, I'm currently facing an issue while trying to integrate a Leaflet map into my Next.js application. The error window is not defined keeps popping up and despite searching on stackoverflow, I haven't found a solution yet. The code ...

Dynamically pass a template to a child component

How can I dynamically load content on my page based on the active navigation point? export class Sub_navigation_item { constructor( public title: string, public templateName: string ) {} } I have a navigation item with an ID from an ...

Utilizing custom form fields with JavaScript in Symfony2

Here is my form field template: {% block test_question_widget %} {% spaceless %} <div {{ block('widget_container_attributes') }}> {% set type = type|default('hidden') %} <input type="{{ typ ...

Transitioning from SJAX to AJAX

I am in the process of updating a portion of my script to use AJAX instead of Synchronous JAX to prevent the page from freezing. My goal is to check if a password is valid before sending it to the server. If the password is not valid, I want the password f ...

Executing all middleware within an express route

Currently, I am in the process of constructing an API using express and have implemented multiple middleware functions in my routes. One of the endpoints I am working on is displayed below: Router.route('/:id/documents') .get([isAuthenticated, ...

Switch between classes when hovering over / exiting ngFor elements

Displayed below is an element created using ngFor <span *ngFor="let picture of pictures; let i = index"> <a target="_blank" href="{{picture.image}}" class="thumbnail-display image-overlay"> <span class="overlay-icon hide"> ...

Accessing state property of a different component in ReactJS: A comprehensive guide

I have a main component that incorporates a menu component. The menu component utilizes a state property to store information about the selected menu item. However, I am now faced with the challenge of retrieving the selected module in the main component. ...

issue TS2322: The function returns a type of '() => string' which cannot be assigned to type 'string

I have recently started learning Angular 6. Below is the code I am currently working on: export class DateComponent implements OnInit { currentDate: string = new Date().toDateString; constructor() { } ngOnInit() { } } However, I am encounterin ...

Attempting to iterate through a Query each loop for the Raphael object

I'm currently facing a challenge with creating a jQuery .each() function to iterate through an array that I've constructed from a Raphael object. While I am able to achieve this using a traditional JavaScript for-loop: for (var i = 0; i < reg ...

Iterating over an array of lists to tally the elements

I've been struggling to count the number of objects in an array using JavaScript. Below is the array I'm trying to work with: <script> var arr = [ {"gateways":["ccu1"],"manufacturer":["homematic"],"ir":["ir_no"],"ip":["ip_cam", ...

How can I retrieve the $index value of an ng-repeat element within a uib-dropdown?

I am currently working on implementing an ng-repeat loop that includes a dropdown menu for each element. I want the dropdown menu to contain functions that operate on the specific element, requiring access to the index of that element. Below is the code sn ...

The WebDriver encountered an error while trying to click on an element

When using Selenium WebDriver, I am facing an issue with selecting an item from a drop-down list. The element I want to click on is labeled as "game club". Despite attempting to select different elements, I keep encountering an error stating that none of ...

Wait for Axios Request Interceptor to complete before sending another ajax call

One feature I have added is a request interceptor for all axios calls. This interceptor checks the JWT token and automatically refreshes it if necessary. axios.interceptors.request.use((config) =>{ const currentState = store.getState(); // get upd ...

Angular Bootstrap Modal not Displaying

<img id="1" data-toggle="modal" data-target="#myModal" data-dismiss="modal" src='assets/barrel.jpg' alt='Text dollar code part one.' /> <div id="myModal" class="modal fade" *ngIf="isModalShowing"> <div class=" modal-lg ...

Creating a JavaScript script to implement a CAPTCHA feature on Google Forms

I'm looking to implement a JavaScript solution that can prevent spam on Google Forms. The idea is as follows: Generate a random number a between 1 and 1000; Generate another random number b between 1 and 1000; Obtain input from the user, storing it a ...

Error: Unexpected termination of data in JSON file on line 2, starting at the first character

I keep encountering an error while trying to execute a basic JSON request. Check out the following PHP code that contains the data: <?php header('Content-Type: application/json; charset=utf-8'); $wealth = array( "name" => &q ...