nofear87
nofear87

Reputation: 869

Refresh controller when factory data updated

I have a Factory:

function PeriodDataService (APIService) {

    var periodData = {}

    periodData.refresh = function(user) {
        APIService.query({ route:'period', id: user._id }, function(data) {
            periodData.orders = data[0].orders
        })
    }
    periodData.orders = []
    periodData.preiod = 1

    return periodData


}
angular
    .module('app')
    .factory('PeriodDataService', PeriodDataService)

And some controllers...for example this one, which use the factory data

function ProductionCtrl ($scope, PeriodDataService) {

    $scope.board = PeriodDataService.board
    $scope.period = PeriodDataService.period
}
angular
    .module('loop')
    .controller('ProductionCtrl', ProductionCtrl)

When I call the refresh, the Controlles dont update there data. Whats the reason?

PeriodDataService.refresh(user)

Thank you!

Upvotes: 0

Views: 776

Answers (2)

Fernando Carvajal
Fernando Carvajal

Reputation: 1945

I used $rootScope in the factory and $scope.$on in the controller to solve this. When I change the factory, i use $rootScope.$broadcast to tell the controller that I change it.

.factory('dataFactory', ['$http', '$rootScope', function ($http, $rootScope) {
    var dataFactory = {
        stock: null,
        getStock: getStock
    }

    function getStock() {
        $http.get("/api/itemfarmacia/").then(function success(res) {
            dataFactory.stock = res.data;
            $rootScope.$broadcast('dataFactory.stock');
        }, function error(err) {
            onsole.log("Bad request");
        })
    }

    return dataFactory;
}])

and in the controller

.controller('atencion', ["$scope", "$state", "dataFactory", function ($scope, $state, dataFactory) {
    $scope.stock = dataFactory.stock;
    dataFactory.getStock(); //wherever you execute this, $scope.stock will change

    $scope.$on('dataFactory.stock', function () {
        $scope.stock = dataFactory.stock; //Updating $scope
    })
}])

Upvotes: 0

MikeJ
MikeJ

Reputation: 2324

The problem is that, when you call refresh and your service does

periodData.orders = data[0].orders

your service is changing the periodData.orders property to point to a different array than the one it was set to point to when you initialized it (via periodData.orders = []).

This breaks the connection between your controller and the data array in your service because your controller was set up to point to the original array when you did

$scope.orders = PeriodDataService.orders

(which I don't actually see in your sample code but which I assume you're doing somewhere).

This simply set $scope.orders equal to the same pointer as periodData.orders, thus pointing to the original array in memory, and allowing the controller to see changes to that array.

When your service changes periodData.orders to a different pointer, nothing changes the pointer value of $scope.orders, so it's still pointing to the original array, which hasn't changed.

There are different ways you can fix this.

Approach 1

You could have your service .push() the new data into periodData.orders rather than set periodData.orders equal to the returned data array. So your refresh method would look like this:

periodData.refresh = function(user) {
    APIService.query({ route:'period', id: user._id }, function(data) {
        periodData.orders.length = 0; // empty current orders array

        // push the returned orders data into orders array
        data[0].orders.forEach(function (item, index) {
            periodData.orders.push(item);
        });
    });
};

As long as you just add to or remove from the original array, and don't redefine it (don't ever do another periodData.orders =), it should work fine.

Approach 2

Alternatively, you could create a new object to hold the orders array (and any other data elements you want to expose) and have a $scope property pointing to that object rather than to the array itself.

So your service would look like this:

function PeriodDataService (APIService) {
    var periodDataSvc = {};

    // create data object to hold service data
    periodDataSvc.periodData = {
        orders: [],
        period: 1
    };

    periodDataSvc.refresh = function(user) {
        APIService.query({ route:'period', id: user._id }, function(data) {
            periodDataSvc.periodData.orders = data[0].orders
        })
    }

    return periodDataSvc;
}

with the orders array now one level deeper at periodDataSvc.periodData.orders.

And you would have a $scope property pointing to the periodData object rather than to the orders array:

function ProductionCtrl($scope, PeriodDataService) {
    $scope.periodData = PeriodDataService.periodData;
}

So in your service, when you set the orders array equal to the returned array from your APIService, the controller will see that change because it's watching the periodData object, and a property on that object has changed.

And of course, since you changed the $scope property, any markup that was referencing the orders array would also then need to change to reference periodData.orders instead. For example:

<div ng-repeat="order in periodData.orders">{{order.id}}: {{order.item}}</div>

Here's a fiddle showing both approaches.

Upvotes: 1

Related Questions