3
votes

I am learning Angular 6 now and now facing an observable issue that I can't understand.

Please take a look at my code below.

export class ShoppingCartComponent implements OnInit {
  cart$;
  products = {};
  constructor(private shoppingCartService : ShoppingCartService, 
  public productService : ProductService) { 
  var subscription = from(this.shoppingCartService.getCart());
  subscription.subscribe(cart => cart.subscribe(shoppingcart => {
     var items = shoppingcart.items;
     console.log(shoppingcart.items);
     for(let key in items){
         console.log(key);
         this.productService.get(key).valueChanges().subscribe(product=>{
            this.products[key] = product['title'];
            console.log(product);
            console.log(this.products);
            //console.log(key);
         });
     }
  }));
 }
}

cart in subscribe is Observable< ShoppingCart>.

export class ShoppingCart {
    constructor(public items: ShoppingCartItem[]){ }

    get productIds(){
      return Object.keys(this.items);
    }

    get totalItemsCount(){
      let count = 0;
      for (let productId in this.items)
        count += this.items[productId].quantity;
      return count;
    }
}

export interface ShoppingCartItem{
    product : Product;
    quantity : number;
}

This angular app gets shoppingcart from firebase database and if I run this I get weird result. Please look at the screenshots. Products object seems to be overwritten by the last key which is cauliflower. enter image description here

But if I toggle back the comment which is "console.log(key)". It works fine like below screenshot.

enter image description here

Is this because of let variable in for loop?

If let keyword is replaced with var, products object only has cauliflower regardless of console.log(key).

Please let me know.

Thanks,

1
let keyword is used to prevent problems like this one (including async cases like setTimeout or Observable.subscribe). Can you build a minimal example on StackBlitz that demonstrates the issue? - Ivan Kashtanov
Have you tried the const keyword? - Boudi

1 Answers

0
votes
for(var value of ['foo', 'bar', 'baz']) {
  setTimeout(() => console.log(value), 0);
}
// baz
// baz
// baz

console.log(value);
// baz

As a setTimeout was called outside of for-of loop it used the same value each time, value exist outside of loop (block scope).

let and const block scope variable.

With let keyword babel will transpile in:

var _arr = ['foo', 'bar', 'baz'];

var _loop = function _loop() {
      var value = _arr[_i];
      setTimeout(function () {
            return console.log(value);
      }, 0);
};

for (var _i = 0; _i < _arr.length; _i++) {
      _loop();
}

On each iteration of for-of loop new value is created in _loop function's scope.