-1

I'm quite new to Angular. I created a factory in order to reuse some functions across several controllers. This FIDDLE is a simpler version of my code, which doesn't work.

JS:

var AppModule = angular.module('myApp', []);

AppModule.factory('AppService', function(){
    return{
        convert: function convert(unit, value){
            if (unit === '/day') {
                cost = value*12/365;
            }
            else if (unit === '/month') {
                cost = value;
            }
            else if (unit === '/year') {
                cost = value*365;
            }
            return cost;
        },
        convert_all: function convert_all(selected_unit, costs){
            converted_costs = angular.copy(costs);
            angular.forEach(costs,function(cost, key){
                converted_costs[key].value = convert(selected_unit, cost.value);
            });
            return converted_costs;
        }
    }    
});

AppModule.controller('AppCtrl', function($scope, AppService){
    $scope.units = ['/day', '/month', '/year'];
    $scope.selected_unit = $scope.units[1];
    $scope.costs = [{title:'Rent', value:800},{title:'Food', value:400}];
    $scope.converted_costs = AppService.convert_all($scope.selected_unit, $scope.costs);     
});

HTML:

<div ng-app="myApp">
    <div ng-controller="AppCtrl">
        <select ng-model="selected_unit" ng-options="selected_unit for selected_unit in units"></select>
        <div ng-repeat="cost in converted_costs">
            <p>{{cost.title}}: {{cost.value | currency}}</p>
        </div>
    </div>
</div>

The purpose is to be able to select a unit and have the costs converted automatically.

I think I have a first issue (probably wrong syntax) with my call to convert from convert_all, couldn't find out why.

And if I test with the convert function only on a single cost I get the conversion to work but only on the first instance. i.e. it won't update when another unit is selected. I understood that this is because the factory is singleton and doesn't watch for the changes in the selected unit. I read that I could emit the factory result to rootScope or broadcast the controller scopes, or maybe use promises but I couldn't get any of these solutions to work on my code and couldn't find a clear answer on the best way to deal with that.

Any idea or recommendation would be greatly appreciated.

thx

MaT
  • 51
  • 6
  • the problem with your code is not angular that part you are doing it almost completley right but trying to acess convert function inside convert_all the way it was all declared look at this refactoring http://jsfiddle.net/d65yan/76VJz/20/ – Dayan Moreno Leon Jun 09 '14 at 12:23

2 Answers2

1

Or you can to that in simpler way : jsfillde

View:

<div ng-app="myApp">
    <div ng-controller="AppCtrl">
        <select ng-model="selected_unit" ng-options="selected_unit for selected_unit in units" ng-change=update()></select>
        <div ng-repeat="cost in converted_costs">
            <p>{{cost.title}}: {{cost.value | currency}}</p>
        </div>
    </div>
</div>

js:

var AppModule = angular.module('myApp', []);

AppModule.factory('AppService', function(){



    function convert(unit, value){
            if (unit === '/day') {
                cost = value*12/365;
            }
            else if (unit === '/month') {
                cost = value;
            }
            else if (unit === '/year') {
                cost = value*365;
            }
            return cost;
        }
    function convert_all(selected_unit, costs){
            converted_costs = angular.copy(costs);
            angular.forEach(costs,function(cost, key){
                converted_costs[key].value = convert(selected_unit, cost.value);
            });
            return converted_costs;
        }


   var service =
    {
        convert: convert,
        convert_all: convert_all
    }

   return service;
});

AppModule.controller('AppCtrl', function($scope, AppService){
    $scope.units = ['/day', '/month', '/year'];
    $scope.selected_unit = $scope.units[1];
    $scope.costs = [{title:'Rent', value:800},{title:'Food', value:400}];
    $scope.converted_costs = AppService.convert_all($scope.selected_unit, $scope.costs); 
    $scope.update = function(){

    $scope.converted_costs = AppService.convert_all($scope.selected_unit, $scope.costs); 

    }    
});
sylwester
  • 16,312
  • 1
  • 22
  • 32
1

Similar to one of the other answers but having the service return all of the methods (maybe you want to use convert in your controller. Also added a watch to controller:

var AppModule = angular.module('myApp', []);

AppModule.factory('AppService', function () {
    var self = this;
    self.srv = {
        convert: function convert(unit, value) {
            if (unit === '/day') {
                cost = value * 12 / 365;
            } else if (unit === '/month') {
                cost = value;
            } else if (unit === '/year') {
                cost = value * 365;
            }
            return cost;
        },
        convert_all: function convert_all(selected_unit, costs) {
            var converted_costs = angular.copy(costs);
            angular.forEach(costs, function (cost, key) {
                converted_costs[key].value = self.srv.convert(selected_unit, cost.value);
            });
            return converted_costs;
        }

    }
    return self.srv;
});

AppModule.controller('AppCtrl', function ($scope, AppService) {
    $scope.units = ['/day', '/month', '/year'];

    $scope.$watch('selected_unit', function (val) {
        $scope.selected_unit = val;
        $scope.converted_costs = AppService.convert_all($scope.selected_unit, $scope.costs);
    });

    $scope.costs = [{
        title: 'Rent',
        value: 800
    }, {
        title: 'Food',
        value: 400
    }];

});
lucuma
  • 17,929
  • 4
  • 60
  • 86
  • Nice, Thanks. I read that using this in Factory isn't recommended. Why do you think it is better in this case? Your watcher seems more concise than the one written by Maxime, do you think it performs better this way? – MaT Jun 09 '14 at 13:04
  • Are you asking about `convert` or the premise for your question? Services (factories) are easily testable and sharable across projects it can work well, but you could put that in an inherited controller too. – lucuma Jun 09 '14 at 13:04
  • That is why I set `self` equal to `this`. It is a pretty standard pattern. http://stackoverflow.com/questions/962033/what-underlies-this-javascript-idiom-var-self-this although it isn't really required. Could just do `srv = {}; .... return srv;` etc – lucuma Jun 09 '14 at 16:42