idontknow
idontknow

Reputation: 17

Shared service is passing object by reference

Hi I am having a issue with a shared service in angular. When my application starts the init function is called and returns some data that is used accross the application.

Components can inject this service and get the data but the data can change inside the component. With my implementation every time I change the object inside my component the object returned from my shared service gets updated too.

@Injectable()
export class SharedDataService {
   private _object1: SomeType[];
   private _object2: SomeType[];
   private _object3: SomeType[];
   constructor(private _http: HttpClient, @Inject('BASE_URL') private _baseUrl: string) {}

   public init() {
       this._http.get<IInitModel>(this._baseUrl + 'init').subscribe(
            (r: IInitModel) => {
                this._object1 = r.object1;
                this._object2 = r.object2;
                this._object3 = r.object3;
            }
   }

   public getObject1(){
       return this._object1;
   }

   public getObject2(){
       return this._object2;
   }

   public getObject3(){
       return this._object3;
   }

The function init() gets called on app startup and gets some data that is needed across the application and in my components I access this data like this:

export class SomeComponent {
    public object1: SomeType[];
    constructor(private _sharedDataService: SharedDataService) {}
    ngOnInit() {
       this.object1 = this._sharedDataService.getObject1();
    }
}

If I change the object in ngOnInit inside my component

this.object1.push({ prop1: 1, prop2: "SomeValue" })

The value of the private member in the shared service gets changed also.

console.log(this._object1) // In shared service returns: [{ prop1: 1, prop2: "SomeValue"}]
console.log(this.object1) // In a component injecting the service: [{ prop1: 1, prop2: "SomeValue"}]

Is this an expected behavior? Do objects get passed by reference when returning them like I am doing in my service.

Could someone recommend a better way to implement this

Upvotes: 0

Views: 595

Answers (2)

Surjeet Bhadauriya
Surjeet Bhadauriya

Reputation: 7156

In javascript, objects and arrays are set as a reference.

For example:

const obj1 = { name: 'Surjeet' }; // You have obj1 with name property
const obj2 = obj1; // assigning, it will set the reference of obj1 to obj2
obj2.name = 'Yuvi'; // now change obj2.name, it will also change obj1.name
console.log(obj1.name) // It prints Yuvi
console.log(obj1.name) // It also prints Yuvi

So, in this case, you have to make a deep copy of your object. So either you can use spread operator or lodash.

Using Spread Operator

const arr = [1,2,3,4]
const copiedArr = [...arr];

Using Lodash

const arr = [1,2,3,4]
const copiedArr = _.cloneDeep(arr);

SO IN YOUR CASE CHANGE IT LIKE BELOW

export class SomeComponent {
    public object1: SomeType[];
    constructor(private _sharedDataService: SharedDataService) {}
    ngOnInit() {
         this.object1 = _.cloneDeep(this._sharedDataService.getObject1());

         //OR

         //this.object1 = [...this._sharedDataService.getObject1()]; 
    }
}

Upvotes: 0

Yvan
Yvan

Reputation: 1121

You can use lodash in order to clone your object.

https://lodash.com/docs/4.17.15#cloneDeep

@Injectable()
export class SharedDataService {
   private _object1: SomeType[];
   private _object2: SomeType[];
   private _object3: SomeType[];
   constructor(private _http: HttpClient, @Inject('BASE_URL') private _baseUrl: string) {}

   public init() {
       this._http.get<IInitModel>(this._baseUrl + 'init').subscribe(
            (r: IInitModel) => {
                this._object1 = r.object1;
                this._object2 = r.object2;
                this._object3 = r.object3;
            }
   }

   public getObject1(){
       return _.cloneDeep(this._object1);
   }

   public getObject2(){
       return _.cloneDeep(this._object1);
   }

   public getObject3(){
       return _.cloneDeep(this._object1);
   }
}

You can also use Object.assign(target, ...sources) this method is used to copy the values of all enumerable own properties from one or more source objects to a target object. It will return the target object.

Upvotes: 0

Related Questions