1

I have been experimenting with the use of services in controllers and in particular binding scope properties on a controller to properties in a service.

In the code below I am finding the $scope.stockCountProp in my controller does not update unlike the $scope.stockCountFunc property which does increment correctly.

I found some posts suggesting you should use scoped functions for binding to service properties, why will the direct property binding approach not work?

    <div ng-app="myApp" ng-controller="StockCtrl">
    <h2>Stock Checker</h2>
    <h3>Using a scoped function binding</h3>
    Current Stock Level: {{stockCountFunc()}} 
    <br>
    <h3>Using a direct property binding</h3>
    Current Stock Level: {{stockCountProp}} 
    <br><button ng-click="updateStock()">Update Stock</button>
</div>

<script>
var app = angular.module('myApp', [])
.service('StockService', function() {
    var StockModel = {};
    StockModel.totalCount = 5;

    StockModel.addStockItem = function() {
        //Some API call here to add a stock item...
        StockModel.totalCount++;
    };

    StockModel.getTotalCount = function () {
        return StockModel.totalCount;
    };

    return StockModel;
 })
.controller('StockCtrl', function($scope, StockService) {
    $scope.stockCountFunc = StockService.getTotalCount;
    $scope.stockCountProp = StockService.totalCount;


    $scope.updateStock = function() {
        StockService.addStockItem();
    };
});
</script>

Demo fiddle https://jsfiddle.net/yh0o7987/2/

mindparse
  • 6,115
  • 27
  • 90
  • 191

3 Answers3

1
$scope.stockCountFunc = StockService.getTotalCount;
$scope.stockCountProp = StockService.totalCount;

This line in your controller are execute once, it's the declaration. On the 1st one your are binding on a function not a value. The function will never change, the value will. On the second one, you assign the value of your service on your controler. It will never change.

Current Stock Level: {{stockCountFunc()}} 

When you call this function, no matter the value, it's always be the getter in your factory that we will call. It means that if the value change, the getter will return the new value.

Current Stock Level: {{stockCountProp}} 

This one will call the value of your controller because it's a value and not a function. It will simply return the initial value.

So, when it's value, Angular will not make the binding. It will be an affectation.

Pierre-Alexandre Moller
  • 2,354
  • 1
  • 20
  • 29
  • Thanks Pierre, so in the case of binding to stockCountFunc() how does Angular know when to re-evaluate this, in other words when does it know the totalCount property on the service has changed? I know a little about the role of watchers, is one used here on the function binding? – mindparse Aug 13 '15 at 12:05
  • It's the DOM which do this job. Watchers will re-evalute the DOM all the time. It's not only when your service change. Sometimes re-evalutation does nothing because nothing change. – Pierre-Alexandre Moller Aug 13 '15 at 12:08
1

If you wanted to changes to be occur in both object then you should use prototypal inheritance to update the value on controller as well as in service.

In order to make totalCount binding working as per prototypal inheritance we should use object & then should add totalCount in the object.

Markup

{{model.totalCount}}

Service

.service('StockService', function() {
    var StockModel = {};
    StockModel.model = {}; //change structure to object
    StockModel.model.totalCount = 5;

    StockModel.addStockItem = function() {
        //Some API call here to add a stock item...
        StockModel.model.totalCount++;
    };

    StockModel.getTotalCount = function () {
        return StockModel.model.totalCount;
    };

    return StockModel;
})

Controller

.controller('StockCtrl', function($scope, StockService) {
    $scope.stockCountFunc = StockService.getTotalCount;
    $scope.stockCountProp = StockService.totalCount;

    //this will provide you the two way binding thing as you are using dot rule
    $scope.model = StockService.model;

    $scope.updateStock = function() {
        StockService.addStockItem();
    };
});

Working Fiddle

Community
  • 1
  • 1
Pankaj Parkar
  • 134,766
  • 23
  • 234
  • 299
0

If the property in the service is primitive (in your case an int) then assigning it to the $scope in the controller makes a copy and not a reference. As a result, updating the service property has no effect since the change is not made to the controller copy.

The function you mention works since you are getting the current value from the service each time it's called.

As an alternative if you want to avoid using functions you can return an object from the service that contains the value, for example {myTotal: 123}.

Miles P
  • 710
  • 4
  • 12