What secrets is Javascript hiding this time?

In my Angular service, I have two properties of the same type. For testing purposes, I am pushing the same objects in reverse order to both properties. However, when I modify one object in one array, I notice that the same change reflects in the second array as well, even though I don't mention the second array in the code. Below is my service class:

class Myservice {

  public meetings : Meeting [];
  public participated : Meeting [];

  constructor(){
      this.meeting = [];
      this.participated = [];
  }

  create(participants, id){

       let meeting : Meeting = {
          id : id,
          participants : participants
       }

      this.meetings.push(meeting);
      this.participated.unshift(meeting);
  }

  addParticipant(userId : number, meetingId : number){
      this.meetings.forEach((meeting : Meeting) => { 
         if(meeting.id == meetingId)  {
           meeting.participants.push(userId)
         } 
      })
  }


}

Here is my Meeting Interface:

interface Meeting {
     id : number;
     participants : Array<number>;
}

This is how I test it:

describe('My service', () => {

     let provider : Myservice;

     beforeEach( () => {
        provider = new Myservice();
     });

     it('should add a participant to the participants', () => {

        provider.create([2], 1);
        provider.create([2], 2);
        provider.create([2], 3);
        provider.create([2], 4);

        provider.addParticipant(6, 3);

        expect(provider.meetings[2].participants).toEqual([2,6])  
        //expect(provider.participated[1].participants).toEqual([2,6])  

        console.log('meeting par:', provider.meetings[2].participants)
        console.log('Partic par:', provider.participated[1].participants)
     })

The test passes as expected, but I noticed that the object in the participated array also changed. Any ideas why this might be happening? Versions of Karma/Jasmine used:

"@types/jasmine": "^3.3.13",
"@types/node": "^12.0.4",
"angular2-template-loader": "^0.6.2",
"core-js": "^3.1.3",
...

Edit: Even with the updated form of the create function, the issue persists:

  create(participants, id){
      this.meetings.push({
         id : id,
         participants : participants
      });
      this.participated.unshift({
         id : id,
         participants : participants
      });

  }

Answer №1

Your issue stems from pushing the same object reference to two different arrays, causing the problem you are experiencing. To resolve this, consider creating a new object for each push or cloning the existing object.

Update #1

Another concern is the participants array in your code.

In JavaScript, both arrays and objects are passed by reference when assigned directly.

To address this, you can use Object.assign for simple objects and [..].slice() for basic arrays.

If you need to clone, you can also utilize var a = {...myObj} for objects and var b = [...myArr] for arrays.

Keep in mind that these methods offer simple cloning and may not handle nested objects or arrays effectively. For deep cloning, consider writing a recursive function or using cloneDeep from Lodash, which works well with both arrays and objects.

Answer №2

After reviewing your code:

createMembers(participants, id){
  let meetingRoom : Meeting = {
    id : id,
    participants : participants
  }

  this.meetingRooms.push(meetingRoom);
  this.participating.unshift(meetingRoom);
}

You are essentially creating one object and storing references to it in two different arrays. This means that when you modify the object within the addParticipant method, you are actually modifying the original object referenced by both arrays.

It's important to understand that the arrays do not hold copies of the object; rather, they contain references to the same object in memory. Therefore, changes made to the object via one array will be reflected in the other array as well.

To address this issue, you can create new objects each time you push to an array by spreading the participants array into a new one. For example:

  this.meetingRooms.push({id: id, participants: [...participants]});
  this.participating.unshift({id: id, participants: [...participants]});

Alternatively, you can leverage object literal shorthand syntax since the properties have the same names:

  this.meetingRooms.push({id, participants: [...participants]});
  this.participating.unshift({id, participants: [...participants]});

By doing so, each array will reference a distinct object, ensuring that each participants array is unique within the Meeting object structure.

I hope this explanation clarifies the situation for you!

Edit 1: Additional details have been provided to address multiple issues identified in the code snippet.

Edit 2: Resources on utilizing the spread operator with arrays have been included for further guidance.

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

Issue: Cannot access the 'map' property of an undefined value in a React Mongo DB error

My React code was running perfectly fine until I encountered an error message in the browser console: "TypeError: Cannot read property 'map' of undefined". Let me share the snippet of my code with you. const MyComponent = () => { const [dat ...

Is it possible for the Chrome mobile camera to take up the full screen size on

Currently, I am using the code below to access the camera and display the stream. The width of the element is 100%, but the height seems to be around 70%. Is there a better way to make it fill the entire screen? HTML <video autoplay class="screen"> ...

Having trouble setting up react-i18n with hooks and encountering a TypeError: Cannot read property '0' of undefined?

Encountering an error while setting up the react-i18n with hooks: TypeError: Cannot read property '0' of undefined Here's the content of i18n.js: import i18n from 'i18next'; import { initReactI18next } from 'react-i18next/h ...

Angular: Utilizing Nested ng-repeat Alongside groupBy Feature on Initial Page Load or Refresh

With some help, I've made it this far on my project. However, I need to take one more step to achieve my goal. I want to group data based on an attribute that is currently passed through an ng-click action. Is there a way to automatically do this on p ...

Use the onClick attribute with Iframe

Is there a method to wrap an Iframe within another element to create a simulated onClick event? I am aware that Iframes do not support events, however, I require the ability to monitor clicks from my website that are being redirected through the Iframe. A ...

Obtain translations from HTML and JavaScript files

Currently, I am working on a project using Angular 1.6 and incorporating angular-translate for internationalization. The setup for Angular-translate is complete and functioning properly. When I manually include text like: {{'Test' | translate}} ...

The execution of a function in PHP is determined by the data passed from Angular

I've encountered a new challenge while working on web development and would greatly appreciate some assistance. Currently, I have several buttons that need to execute different functions when clicked, such as ng-click='loadA', ng-click=&apos ...

The ipcRenderer.on() function is not receiving the mainWindow.webContents.send() message

Within the electron main.js file, my goal is to send an event from a child Window to a mainWindow. My initial plan was to achieve this by triggering an event from the childWindow to the Main Process, and then having the Main Process send an event to the ma ...

What is the process for utilizing the JWT token on the front-end after obtaining it?

After hours of research, I am still struggling to implement login functionality using JWT Tokens. Here is my progress so far: My login API const loginUser = async (req, res, next) => { try { const {username, password} = req.body //g ...

At what point are watch variables accessible in the Chrome-Node-Debugger tool?

My test file runs periodically, either every minute or second, depending on how I configure it. https://i.sstatic.net/duXl5.png Despite setting watches on variables in the file, they do not populate as expected: https://i.sstatic.net/W6CFo.png Interest ...

Configuring the baseUrl for Axios in a Vue.js application triggers the sending of a request

I have encountered an issue in my app where Axios automatically makes a request to the baseUrl without me explicitly making one. This occurs even when the app is loaded in the browser. In my main.js file, I have set the baseUrl using: axios.defaults.baseU ...

Utilizing repeated directives within a single controller in Angular

Currently, I am in the process of developing a webpage that utilizes Highcharts to display some data. To ensure reusability, I have encapsulated the desired chart within a directive. 'use strict'; angular.module('statisticsApp') .dir ...

What is the proper way to import Axios in Vue 3 once you have created a new project using the CLI?

When starting a new project, I used the command: vue create hello-world This generated essential files like HelloWorld.vue, app.vue, and main.js. Next, I followed the documentation on Npm vue-axios to install Axios: npm install --save axios vue-axios In ...

Is there a sophisticated method for breaking down a nested property or member from TypeScript imports?

Just curious if it's possible, not a big deal otherwise. import * as yargs from 'yargs'; // default import I'm looking to extract the port or argv property. This would simplify the code from: bootstrap(yargs.argv.port || 3000) to: ...

How can I export an ES Object in Node.JS to an NPM package?

I am currently facing a challenge involving an ES5 object/function that I want to integrate into an NPM package. This particular object is within a namespace defined as MY_NAMESPACE.myObject = function(){...} where MY_NAMESPACE is simply an object. Typic ...

Error TS2322: The function signature '(state: State, exRep: number, exName: string) => void' does not match the expected type 'Mutation' in Vuex when using TypeScript

I'm currently facing an issue while working with Vuex and TypeScript. I have encountered the following error in my code, but I am unsure how to resolve it: The error : TS2322: Type '(state: State, exRep: number, exName: string) => void' i ...

What is the best way to pass createdDt and updatedDat values in an Angular form without displaying them on the template?

I am working on a message creation form in Angular where I need to include createdDt and updatedDt as hidden values. These values should not be visible in the template. I want to automatically set the current date and time for both createdDt and updatedD ...

Contains a D3 (version 3.4) edge bundle chart along with a convenient update button for loading fresh datasets

I am looking to update my D3 (v3.4) edge bundling chart with a new dataset when a user clicks an 'update' button. Specifically, I want the chart to display data from the data2.json file instead of data1.json. Although I have started creating an u ...

jQuery Validation plug-in - disabling validation using class attributes

Is there a way to disable validation in the jQuery Validation plug-in using class attributes and only rely on json rules? The current setup is causing conflicts with my jQuery templating system. ...

Avoid altering the state directly; utilize setState() to update it instead. Remember to follow react/no-direct-mutation

Here's the code snippet I'm working with: constructor(props) { super(props) this.state = { loginButton: '', benchmarkList: '' } if (props.username == null) { this.state.loginButton = &l ...