xaisoft
xaisoft

Reputation: 3451

Modifying local variables in components modifies shared service variable in Angular 2?

Here is my service:

import { Injectable } from '@angular/core';
import { Headers, Http, Response } from '@angular/http';
import { Observable } from 'rxjs/Rx';

import 'rxjs/observable/of';
import 'rxjs/add/operator/share';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';

@Injectable()
export class CaseService {

    private data:any;
    private observable: Observable<any>;

    constructor(private _http: Http) {}

    public GetCase() {

         const url = 'http://localhost:55130/api/case?caseId=123';

         if (this.data) {
             //after filtering an array of objects on a local variable
             //in a controller, this.data is now modified
             console.log(this.data);
             return Observable.of(this.data);
         } else if (this.observable) {
            return this.observable;
         } else {
             let headers = new Headers();
            headers.append('Content-Type', 'application/json');
             this.observable = this._http.get(url,{headers:headers})
                 .map(response => {
                    this.observable = null;
                    this.data = response.json();
                    return this.data;
                 }).share();
                 return this.observable;
        }

    }
}

In my home component, in the ngOnInit, I am doing the following:

  ngOnInit() {
    this._caseService.GetCase().subscribe(data => {
      this.case = data;

      //filter this.case.CaseContent array
      //When I do this, it does return the correct object from the array
      //but it also modifies the data variable on the shared service
      //which is what I don't want
      this.case.caseContent = this.case.CaseContent.filter(function(item){
          return item.Route == '/home';
      })[0];

    });
  }

Why when I modify the local variable in the home controller, it modifies the variable in the shared service. Basically what I am trying to do is when I go to the home page, I filter out the content for the home page and then when I access the contact page, I get the full object again, from the cache and filer out the content for the contact page, but when I go to the contact page, the CaseContent is now an object and not an array of objects.

Upvotes: 0

Views: 305

Answers (1)

Marfu
Marfu

Reputation: 425

You're using same instance of 'data' variable. Returning reference type from method doesn't clone/copy that object, it just creates new pointer (that points to the same object in memory).

You can solve it by removing internal caching:

@Injectable()
export class CaseService {

  constructor(private _http: Http) {}

  public GetCase() {

     const url = 'http://localhost:55130/api/case?caseId=123';
     let headers = new Headers();
     headers.append('Content-Type', 'application/json');
     return this._http.get(url,{headers:headers})
             .map(response => {
                return response.json();
             });
  }
}

Or if you really want to cache observable and data, then you should clone that object on returning, smth like 'return Observable.of(DeepClone(this.data));'

Search in google how to deep clone objects in js, or implement a custom method.

In your case, would be best to cache just string json response and call 'JSON.parse()' on every access.

this.data = response.text();
return JSON.parse(this.data);

Upvotes: 2

Related Questions