1

In my Service I have this code:

setInterval(function () {
    $rootScope.$apply(function () {
        cart.push({
            "Id": 4,
                "Name": "Some item",
                "Price": 4
        });
    });
}, 3000);

Working example: http://jsfiddle.net/ko8edf32/7/

This works correctly: the cart change is pushed to the view.

However, when I assign a new value to cart, the change is NOT pushed to the view:

setInterval(function () {
    $rootScope.$apply(function () {
        var newcart = [{
            "Id": 4,
                "Name": "Some item " + Math.random(),
                "Price": 4
        }];

        cart = newcart;
    });
}, 3000);

example: http://jsfiddle.net/ko8edf32/8/

  • What is the reason of this?
  • What is the best/most elegant solution to solve this issue?

EDIT

This is the working solution I've built, based on the answers below: jsfiddle.net/ko8edf32/11

Thomas Stock
  • 10,927
  • 14
  • 62
  • 79
  • Hi @Thomas Stock, I see you are using factory and then you save cart on rootScope, it would be better for your purposes to store the data into factory/service and nicely wire it with controller in two-way data binding instead of using apply – maurycy Sep 11 '14 at 08:55
  • @maurycy: could you update my fiddle? I'm new at AngularJS and don't really understand what you mean. I use rootScope because I was experimenting, I don't yet fully understand what it does to be honest. ;-) – Thomas Stock Sep 11 '14 at 08:59
  • That is because `ng-repeat` creates new child scope. So that is why you can't track or digest this scope explicitly anymore. So it's better to clear cart instead of create new cart. – hawk Sep 11 '14 at 09:08
  • http://jsfiddle.net/ko8edf32/7/ this is with using a local variable in the factory and a differently named scope variable in the controller. – Thomas Stock Sep 11 '14 at 09:21
  • http://jsfiddle.net/ko8edf32/8/ this is second fiddle changed in the same way, and what I would like to fix. I will update my question with this simplification – Thomas Stock Sep 11 '14 at 09:22
  • @ThomasStock I've added my answer with use of factory for two-way data-binding – maurycy Sep 11 '14 at 09:45

2 Answers2

1

You have to use objects on $scope.

$scope.model = { cart: [] };

Here is a working example: http://jsfiddle.net/56vkqygn/2/

Here is a explanation: What are the nuances of scope prototypal / prototypical inheritance in AngularJS?

Community
  • 1
  • 1
Marian Ban
  • 8,158
  • 1
  • 32
  • 45
  • 1
    an array is an Object, not a primitive type. – Thomas Stock Sep 11 '14 at 08:39
  • also, your fiddle was probably unsaved after changing it, because it is identical to my second fiddle – Thomas Stock Sep 11 '14 at 08:40
  • thanks, your updated fiddle seems to be working indeed, but I think your explanation is not entirely correct. – Thomas Stock Sep 11 '14 at 08:44
  • @ThomasStock i edited my answer and added a new link, i hope it helps you to find a answer. – Marian Ban Sep 11 '14 at 08:54
  • I still dont like this answer. The controller now depends on the service's naming of variables. When I change "model.cart" to "model.mycart" in the controller (and not in the service), your fiddle is no longer working. – Thomas Stock Sep 11 '14 at 08:55
  • @ThomasStock yes because you created a model on the root scope inside your cartService which is then inherited by your controller. Better solution will be to create the view model on the controllers scope which will be automaticaly inherited by your service. – Marian Ban Sep 11 '14 at 08:58
  • By the way thanks for all the effort! I don't really want to use the rootScope in my service. I don't fully understand what that does but was just experimenting to try to get this to work. I'm not sure what you mean with your last comment. (see the comments on my question, I think @maurycy is talking about the same thing) – Thomas Stock Sep 11 '14 at 09:01
  • @ThomasStock yes, do not use rootScope from your services. If you want to manipulate with scope, then do it only in controller or directive. The second sentence of my last comment is wrong, because service does not inherits controller's scope, it can work only with the $rootScope. – Marian Ban Sep 11 '14 at 09:24
  • thanks for the help. I have figured out my issues. I have marked the other answer as accepted because it is more complete. – Thomas Stock Sep 11 '14 at 11:23
1

I've changed your cart into more "angular" way with two-way databinding. I've added the second controller to show you how nice it works all together without getters/setters and generally with a bit of magic

http://plnkr.co/edit/U5pUlAPJTNd9V0baSjAu?p=preview

homeApp.factory("cartService", function($rootScope) {
  var service = null
  service = {
    all: function() {
      return service.cart;
    },
    add: function(item) {
      service.total += item.Price
      service.cart.push(item);
    },
    remove: function(item) {
      service.total -= item.Price
      service.cart.splice(service.cart.indexOf(item), 1);
    },
    cartUpdated: function(newValue) {
      service.cart = newValue;
    },
    cart: [],
    total: 0
  }
  return service;
});
maurycy
  • 8,455
  • 1
  • 27
  • 44
  • hmm ok so basically you reference the service variable straight into the view. I have updated my example with that reasoning and it is indeed working: http://jsfiddle.net/ko8edf32/11/ I will investigate if this solves the issue in my actual implementation (obviously my fiddle is a simplified version) and report back. – Thomas Stock Sep 11 '14 at 09:59
  • Hey, thanks for the help. I have investigated further and there seems to be a problem when passing an item from 1 ng-repeat list to another. I wish I could go further into detail but im afraid I've already put too much time into this. I will mark your answer as accepted because it is the most complete, but I prefer my fiddle in the comment above as it is closer to my original example. – Thomas Stock Sep 11 '14 at 11:22