0
votes

I'm new to Observables and still trying to get my head around exactly how they work.

I'm trying to take an array from an observable and allow it to be changed, but without affecting the observable. I thought I'd be able to do this with Object.assign()

The flow is:

  1. Get an array of invoices from the API.
  2. Store it as a BehaviorSubject Observable in a service.
  3. User navigates to 'view all invoices' screen.
  4. User clicks on 'Edit' one invoice
  5. Edit component loads, subscribes to the Observable, finds the correct invoice from the array and assigns it to a separate this.invoice object.
  6. User should be able to edit this.invoice without affecting the observable

But no. 6 doesn't work. If the user clicks on Edit invoice, makes a change to the invoice, doesn't save it to the DB but instead goes back to the view invoice screen and clicks edit again, the changes persist. I would expect the original invoice to be shown, not the newly edited one.

In user.service:

export class UserService {
  ...
  public invoices: BehaviorSubject<any> = new BehaviorSubject(null);
  ...
  constructor(){
  };
}

Get the invoice array:

userLogin() {
  this.commonService.request('login', '', {password: this.password, email: this.email})
    .then((response:any) => {
      ...
      if(response.invoiceData) this.userService.invoices.next(response.invoiceData);
      ...
      }
    })
}

View invoices component:

ngOnInit() {
  this.invoicesSubscription = this.userService.invoices.subscribe((invoices) => {
    if(invoices) {
      this.invoices = invoices;
    }

  });
}

Edit invoice component:

ngOnInit() {
  this.paramsSubscription = this.activatedRoute.params.subscribe((params: Params) => {
    if(params) {
      this.invoicesSubscription = this.userService.invoices.subscribe((invoices) => {
        if(invoices) {
          invoices.forEach((invoice) => {
            if(invoice._id == params.invoiceId) {
              this.invoice = Object.assign({}, invoice); 
              console.log(invoice) // This shows changes to the invoice when the screen loads for the second time.
            }
          });
        }
      });
    }
  });
}

How can I edit this.invoice object without the changes affecting the Observable?

2

2 Answers

0
votes

I solved this by using JSON.parse(JSON.stringify(invoice)) instead of Object.assign({},invoice). in the edit invoice component.

0
votes

Observables only let you treat data as streams, where you can do awesome stuff with it by using operators. But they don't "store" data in any way - They only see a list of elements that get served to the observers, leaving the data transformation part to the consumer.

Object.assign({}, obj) doesn't do a depth-copy of an object. In your case making changes on the level of this.invoice shouldn't get back to the original object that's lying in the BehaviorSubject (for instance this.invoice.foo = 'bar'), but if you go just one level deep, it will (for instance this.invoice.items.push('bar'))

You will need to make a whole copy of your object (search around, there are many ways to do that). You can also use Immutable.js to help you in that, but it's a bit of a pain to work with them... (it's a library that lets you store immutable data: Data that if you want to change some part of it, it will give you a whole new copy of the object with the change you needed, leaving the original intact)