I have a Rails app and access two models through the JSON API using ng-resource.
In my template I display a list of the first model "orders":
<li ng-repeat="order in orders">
Order {{order.id}}:
Product: {{showProduct(order.product_id)}}
</li>
Each "order" contains a product_id
. Now I would like to access the second model ("products") and display inside the ng-repeat
the correct "product" with the ID product_id
of the corresponding "order".
To achieve that I thought of using a function showProduct()
and use order.product_id
as an argument. However when I do this it causes an infinite loop that keeps making GET requests to my database.
Here the important part of my app.js
var app = angular.module('shop', ['ngResource']);
app.factory('models', ['$resource', function($resource){
var orders_model = $resource("/orders/:id.json", {id: "@id"}, {update: {method: "PUT"}});
var products_model = $resource("/products/:id.json", {id: "@id"}, {update: {method: "PUT"}});
var o = {
orders: orders_model,
products: products_model
};
return o;
}]);
app.controller('OrdersCtrl', ['$scope', 'models', function($scope, models){
$scope.orders = models.orders.query();
$scope.showProduct = function(product_id){
return models.products.get({id: product_id});
};
}])
These are the errors in my console (which make sense since it's an infinite loop):
Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: []
Uncaught Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: []
The GET request in the Rails console seems fine. Maybe I'm trying to think much more complicated than I need to.
Yes because you have showProduct
in the view, the function is called on every digest, returning each time a different promise so it breaks:
I suggest you to do:
models.orders.query().$promise
.then(function(orders){
$scope.orders = orders;
//using lodash here, I recommend you use it too
_.each(orders, function(order){
models.products.get({id: order.product_id }).$promise
.then(function(product){
order.product = product;
})
});
});
And in the view:
<li ng-repeat="order in orders">
Order {{order.id}}:
Product: {{order.product}}
</li>
But using this code will trigger one query for each order which is quite bad: its a N+1 anti pattern.
2 solutions: