Resolving resource dependency of another resource

276 Views Asked by At

I have a resource whose data depends on another resource. Transaction is tied to a SplitTransaction which has a list of transactions. From a transaction, I need to know the total of transactions of the SplitTransaction.

This is my resource:

.factory('Transaction', ['$resource', '$http', '$rootScope', 'SplitTransaction', '$q', function($resource, $http, $rootScope, SplitTransaction, $q){
    var Transaction = $resource('/api/v1/transaction/:id', {}, {
        query: {
            method: 'GET',
            isArray: true,
            transformResponse: tastypieDataTransformer($http).concat(function (data, headersGetter) {
                for (var idx in data) {
                    var transaction = data[idx];

                    if (transaction.installment_of) {
                        var split = transaction.installment_of.split('/');
                        var installmentId = split[split.length-1];

                        SplitTransaction.get({id: installmentId}).$promise.then(function (installment) {
                            transaction.installment_total = installment.transactions.length;
                        });
                    }
                }
                return data;
            })
        }
    });

This is the html:

<tr class="transaction-row" ng-repeat="transaction in group.transactions">
    <td ng-bind="transaction.installment_total"></td>
</tr>

It shows nothing on the rendered html.

I tried to use promises:

transformResponse: tastypieDataTransformer($http).concat(function (data, headersGetter) {
    for (var idx in data) {
        var transaction = data[idx];

        if (transaction.installment_of) {
            var split = transaction.installment_of.split('/');
            var installmentId = split[split.length-1];

            var deferred = $q.defer();

            SplitTransaction.get({id: installmentId}).$promise.then(function (installment) {
                var installment_total = installment.transactions.length;
                deferred.resolve(installment_total);
            });
            transaction.installment_total = deferred.promise;
        }
    }
    return data;
})

Now the binding seems to work, but it only shows [object Object] on the html.

What am I doing wrong?

Edit:

If I set the transaction.installment_total outside the SplitTransaction.get callback, it shows on the HTML, so the bindings are ok.. Like this:

if (transaction.installment_of) {
    var split = transaction.installment_of.split('/');
    var installmentId = split[split.length-1];

    transaction.installment_total = 0;  // shows "0" on the html


    SplitTransaction.get({id: installmentId}, function (installment) {
        ...
    });
}

For some reason what happens inside the callback does not reflect on the bindings...

Temporary solution:

I removed the code from the transformResponse to where I load the Transaction's:

Transaction.query(filter).$promise.then(function (result) {                      

    $.each(result, function (idx, transaction) {                                 
        if (transaction.installment_of) {                                        
            var split = transaction.installment_of.split('/');                   
            var installmentId = split[split.length-1];                           
            transaction.installment_total = 0;                                   

            SplitTransaction.get({id: installmentId}, function (installment) {   
                transaction.installment_total = installment.transactions.length; 
            });                                                                  
        }                                                                        
    });                                                                          

    $scope.allTransactions = result;                                             
    $scope.transactionGroups = groupTransactions($scope.groupBy);                

    window.transactions = $scope.transactionGroups;                              

}).finally(function () {$scope.loading = false;});

Can't say why this works. Maybe the objects get copyed after the transformResponse, making my reference to transaction on the callback scope useless...

1

There are 1 best solutions below

2
On

It is not quite obvious, what is getting bound to transaction.installment_total in your html. Usually it may be property of $rootScope or $scope of bound controller and I do not see any controller here.

Also, not obvious, what is group.transactions. If it's empty, then nothing will be rendered.

But if you managed somehow to bind transaction.installment_total to your html, then the problem is here:

transaction.installment_total = deferred.promise;

You are binding Promsie object to your html and you need to bind result of this promise:

deferred.promise.then(function(result) {
   transaction.installment_total = result;
});

That is if you want to use promises. But there is another way.

ngResource's method return promise, which you can assign to your view and when data will be available, this promise will be replaced with this data.

This is from documentation:

Having an empty object results in no rendering, once the data arrives from the server then the object is populated with the data and the view automatically re-renders itself showing the new data. This means that in most cases one never has to write a callback function for the action methods

In another words...

var result = SplitTransaction.get({id: installmentId}, function() {
   transaction.installment_total = result.transactions.length
});

...should also work.