0

I have the below code which uses the data in the staff array to calculate the staff members pay.

'use strict';
var app = angular.module('app', []);
app.factory('staffFactory', function ($http) {
    var staff = [
        {"id": "1","name": "Kate","rate": "10", "hours": "10"},
        {"id": "2","name": "John","rate": "20", "hours": "10"},
        {"id": "3","name": "Matt","rate": "15", "hours": "10"}
    ];

  function calcPayInner(){
    var unique = {},
        distinct = [],pay = [];
    for (var i in staff) {
        if (typeof (unique[staff[i].id]) == "undefined")  {
            distinct.push(staff[i].id);
        }
        unique[staff[i].id] = unique[staff[i].id] || {pay:0};
        unique[staff[i].id].name = staff[i].name;
        unique[staff[i].id].pay += (parseInt(staff[i].rate, 10) * parseInt(staff[i].hours, 10));
    }

        for (var p in unique) {
            pay.push([p, unique[p]]);
            pay.sort(function (a, b) {
              return (b[1].pay - a[1].pay);
            });
        }
        return pay;
    }
    var staffService = {};
    staffService.allStaff = function () {
        return staff;
    };

    staffService.CalcPay = function () {
        return calcPayInner();
    };

    return staffService;
});

    app.controller('MainCtrl', ['$scope', 'staffFactory', function ($scope, staffFactory) {
        $scope.staff = staffFactory.allStaff();
        console.log($scope.staff);
        $scope.CalcPay = staffFactory.CalcPay();
        $scope.keyPress = function(keyCode){
            $scope.CalcPay = staffFactory.CalcPay();
        };    
    }]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
        <div ng-controller="MainCtrl">
          <table>
            <tr><td>name</td><td>rate</td><td>hours</td></tr>
            <tr ng-repeat="s in staff">
                <td>{{s.name}}</td>
                <td><input ng-keyup="keyPress(s.rate)" ng-model="s.rate"></td>  
                <td><input ng-keyup="keyPress(s.hours)" ng-model="s.hours"></td>
            </tr>
            </table>
          <table>
            <tr><td>name</td><td>pay</td></tr>
            <tr ng-repeat="b in CalcPay">
                <td>{{b.1.name}}</td>
                <td>{{b.1.pay}}</td>
            </tr>
            </table>          
    </div>
</div>

This all works as intended however now I want to get the staff data from an API call rather than hard coded data.

I have tried the below. (see plnkr)

    var staff = [];
    var test = $http.get('staff.json')
    .success(function(data) {
        staff = data;
        console.log(staff);
    });

Which seems to return the data as I can see in my console but I can't get it to work (returns a blank array) with my existing function.

I have also tried to wrap the function in .finally after .success but then my function becomes undefined.

How can I use the API Data in the functions in my factory so I can get my page working as it was originally with the hard coded array?

2 Answers 2

2

The problem as others have mentioned is that you are trying to get data before it's available. The solution to the problem is to use promises inside your factory if you want to keep single source for data.

below code uses q so you will have to inject it on your factory.

app.factory('staffFactory', function ($http, $q) {}

allStaff implementation

/* Private var to hold the staff */
var staff = [];

// this method returns a promise
staffService.allStaff = function () {
    var deferred = $q.defer();

    // check if staff is already fetched from server if yes resolve the promise immediately
    if (staff.length > 0) {
        // we have data already. we can avoid going to server
        deferred.resolve(staff);   // staff holds all the data
    } else {
        // data is not available yet. fetch it from server

        $http.get('staff.json')
        .success(function(data) {
            // once data is available resolve
            staff = data;
            deferred.resolve(data);
        })
        .error(function (error) {
            deferred.reject(error);
        });
    }

    return deferred.promise;
};

In controller.

staffFactory.allStaff().then(function (staff) {
    $scope.staff = staff;
    console.log($scope.staff);
    $scope.CalcPay = staffFactory.CalcPay();
    $scope.keyPress = function(keyCode) {
        $scope.CalcPay = staffFactory.CalcPay();
    }; 
});

checkout the plunkr

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, not only for the answer but for the great comments in the code as well. This will be a good working example for others when working with promises as well.
1

This is how I did it:

$scope.documents = [];

$http.post('get_xml.php')
    .then(function (result) {
        $scope.documents = result.data;
        console.log(result.data);
    });

As far as I know .then is an asynchronous function, which will update your array as soon as it becomes available.

So in your case, it should be:

app.factory('staffFactory', function ($http) {

    var staff = [];
    var test = $http.get('staff.json').then(function(result) {
        staff = result.data;
        console.log(staff);
    });

    /* Rest of your code... */
});

1 Comment

Thanks. Every thing i read says to keep all business logic out of controllers i am looking for a working solution in the factory.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.