Function call inside nested ng-repeat

16,462 views
Skip to first unread message

Dries De Smet

unread,
Dec 19, 2013, 8:12:39 AM12/19/13
to ang...@googlegroups.com
Hi,

I'm currently generating a horizontal calendar using the following code:

<tr ng-repeat="employee in planning">
                <td> {{employee.firstName}} {{employee.lastName}}</td>
                <td class="calendar-unit" ng-repeat="day in days">
                    {{day.date()}}
                    <div class="assignment" ng-repeat="assignment in getAssignments(employee,day)">
                        {{assignment.type}}
                    </div>
                </td>
            </tr>


Days contains momentjs dates, employee are normal objects. For each day, I want to list the assignments for that day & employee, but using a function call inside a ng-repeat causes $digest errors. (because it creates a new array everytime, as far as I understand?)

Is there a more angular / cleaner way to approach this, or how could I get the getAssignments() to work, so that it uses the ng-repeat values?


Thanks!


Sander Elias

unread,
Dec 19, 2013, 8:25:32 AM12/19/13
to ang...@googlegroups.com

Hi Dries,

sure:

<td class="calendar-unit" ng-repeat="day in days" ng-init='assignments=getAssignments(employee,day)'>
      {{day.date()}}
      <div class="assignment" ng-repeat="assignment in assignments">
          {{assignment.type}}
      </div>
</td>

Dries De Smet

unread,
Dec 19, 2013, 8:32:30 AM12/19/13
to ang...@googlegroups.com
Ahh, yes! Thanks!

Op donderdag 19 december 2013 14:25:32 UTC+1 schreef Sander Elias:

Lovi

unread,
May 6, 2014, 2:57:55 PM5/6/14
to ang...@googlegroups.com
Hi Guys..
I tried this. did not work for me. My second ng-repeat calls a function who has $http request. 

like:
                                        $scope.getAssignments= function(emp, d){
var temp = $http.get('something.php?emp='+emp+'&d='+d).
then(function(response){
return response.data;
});
return temp;
}
any ideas? 
Thanks

Tan Vu

unread,
Jul 8, 2014, 4:41:35 PM7/8/14
to ang...@googlegroups.com
I have the same problem.  Any solution on this?

Thanks,

Tan

Eric Eslinger

unread,
Jul 8, 2014, 4:56:24 PM7/8/14
to ang...@googlegroups.com
I suggest you learn about promises and how they work in angular. This is a pretty classic example of that problem. The var temp in the example isn't response.data, but a promise that will resolve to response.data. 

In oldschool angular (e.g., prior to 1.2), the ui would unwrap such promises and resolve them to their actual data. They don't do that anymore. So now, you need to do something like $scope.assignments = response.data inside the then block on your promise, preferably on the controller or link function IMO, rather than using ng-init (but that's just an IMO).

e


--
You received this message because you are subscribed to the Google Groups "AngularJS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to angular+u...@googlegroups.com.
To post to this group, send email to ang...@googlegroups.com.
Visit this group at http://groups.google.com/group/angular.
For more options, visit https://groups.google.com/d/optout.

Sander Elias

unread,
Jul 9, 2014, 12:48:02 AM7/9/14
to ang...@googlegroups.com
Hi Eric,

You are right, they need to catch up on ansync and promises.
However, simply setting the the data on the scope in your controller will not work in this case! The scope inside the ng-repeat is an nested scope. 
This is a legit use off the ng-init directive. It is simply used to create a temporary var on the scope where you need the data.

However, when you want to resolve async data inside a child scope, it will not work using ng-init anymore.

@Lovi and @Tan, 
This can be solved by using an ng-controller inside the repeat, that resolves your data. I can write sample code if you want.
However, I think it's a really bad idea design wise. You will start 'spamming' your servers with loads of really small requests.
it's better to load all that data in a single request, and do that before looping trough the top-data. 
Does this make sense?

Regards
Sander

Eric Eslinger

unread,
Jul 9, 2014, 9:27:54 AM7/9/14
to ang...@googlegroups.com

Oho, I didn't even notice the day in days ng-repeat. Yeah, in that case, this feels either like time to do one big request or time to do something like explicit controllers or a directive.

Given the repeats, I'd consider a directive (or even nested directives) if you end up wanting to make any complex view state variables per day or per assignment (like expanded or whatever).

E

Tan Vu

unread,
Jul 9, 2014, 1:35:28 PM7/9/14
to ang...@googlegroups.com
Hi Sander,

Yes, I would love to see sample codes.  I'm new to Angular.  Yes, I read a lot about asyn & promise as well but I have found no solution so far.

Thank you so much,

Tan

Tan Vu

unread,
Jul 9, 2014, 4:20:58 PM7/9/14
to ang...@googlegroups.com
I think I found the solution:

<td class="calendar-unit" ng-repeat="day in days" ng-init='getAssignments(employee,day)' ng-controller='assignmentCtrl'> {{day.date()}} <div class="assignment" ng-repeat="assignment in assignments"> {{assignment.type}} </div> </td>

In 'assignmentCtrl controller:

   $scope.getAssignments= function(emp, d){
                                                $scope.assignments = [];
var temp = $http.get('something.php?emp='+emp+'&d='+d).
then(function(response){
                                                                $scope.assignments = response.data;
return response.data;
});
return temp;
}


As you can see, you will need to create "nested" controller at ng-repeat top level.  Then in "nested" controller, make sure you assign $scope.assignments so that ng-repeat at child level can access it.

Tan

On Tuesday, May 6, 2014 11:57:55 AM UTC-7, Lovi wrote:

Sander Elias

unread,
Jul 10, 2014, 12:47:38 AM7/10/14
to ang...@googlegroups.com

Hi Tan,

That is what I meant that you needed an extra controller! You don’t even need the ng-Init anymore now.
change your controller a bit so it looks like this:

app.controller('assignmentCtrl', [
    '$scope',
    function($scope) {
        //set up the empty array, so angular start with binding an empty array!
        $scope.assignments = [];
        // you can use the $scope variables from the parent scope in here!
        $http.get('something.php?emp=' + $scope.employee + '&d=' + $scope.day)
            .then(function(response) {
                $scope.assignments = response.data;
            });
    }
]);

and your HTML:

<td class="calendar-unit" ng-repeat="day in days" ng-controller='assignmentCtrl'>
      {{day.date()}}
      <div class="assignment" ng-repeat="assignment in assignments">
          {{assignment.type}}
      </div>
</td>

Regards
Sander

Tan Vu

unread,
Jul 10, 2014, 3:35:03 PM7/10/14
to ang...@googlegroups.com
Hi Sander,

Thank you for the sample codes.  However, it does not work "as is".  I have to modify it for it to work:

app.controller('assignmentCtrl', function($scope){....}

As you can see, I do not inject $scope at the controller level.  I am not knowledgeable enough to even tell when $scope needs to be injected.  

Tan

Sander Elias

unread,
Jul 11, 2014, 1:52:51 AM7/11/14
to ang...@googlegroups.com
Hi Tan,

While I did not run the example, I wonder why it would not work for you? Did you get an error in the console?
I always use the array notation, because I always minimize my code. I do it by hand, but there are tools that can do the same.
And yes, you are in fact injecting $scope. Unminimized my declaration, and yours are exactly the same.

Knowing when to inject something is easy. do you need/use it? inject!
You already did that!

Regards
Sander

Tan Vu

unread,
Jul 14, 2014, 12:13:40 PM7/14/14
to ang...@googlegroups.com
Hi Sander,

I take it back.  Your codes work.  But my codes also work.  The difference is I did not inject '$scope' in assignment controller.  And that's why this confuses me.  I know that if you need something, you have to inject it.  But why it still works if I did not inject $scope before I use it? 

app.controller('assignmentCtrl' function($scope) { //set up the empty array, so angular start with binding an empty array! $scope.assignments = []; // you can use the $scope variables from the parent scope in here! $http.get('something.php?emp=' + $scope.employee + '&d=' + $scope.day) .then(function(response) { $scope.assignments = response.data; }); } 
); 

Lovi

unread,
Aug 29, 2014, 3:32:59 PM8/29/14
to ang...@googlegroups.com
Thank you guys for your help...
 i was able to do this with one request rather than few small requests like @Sander said. But i learnt a lot in factory, services, promises and resolving data. 
cool stuff.
thnx

Bala krishnan

unread,
Jul 13, 2016, 4:03:10 AM7/13/16
to AngularJS
Hi Lovi,

As I am facing the same issue, Please share your code it will be very helpful,

Sander Elias

unread,
Jul 13, 2016, 5:07:25 AM7/13/16
to AngularJS
Hi Bala,

There is a lot of code shared in this thread already. What is so specific to your case that this did not provide a solution?

Regards
Sander

Anka Wirawan

unread,
Jul 15, 2016, 5:13:27 AM7/15/16
to AngularJS
Hi All,

i am new in angularjs. i have same problem call controller function inside ng-repeat. 

views
<div class="application-category-list col-md-9">
<ul>
<li ng-repeat-start="version in ctrl.applicationVersion | orderBy: 'id'" ng-repeat-end ng-init="ctrl.getApplicationByVersion(version.id)">
<div class="row">
<div class="application-category">
<h4>{{version.name}}</h4>
</div>
</div>
<div class="row">
<ul class="application-card-list">
<li class="col-xs-12 col-sm-3 col-md-2" ng-repeat-start="application in ctrl.applicationByVersion" ng-repeat-end>
<div class="application-card-box">
<div class="application-card-image">
<a href=""><img src="assets/img/application/apps_1.png" alt=""></a>
</div>
<div class="application-card-body">
<a href=""><p class="application-card-title">{{application.name}}</p></a>
<p class="application-card-subtitle">{{application.version.name}}</p>
<p class="application-card-version">{{application.number}}</p>
</div>
</div>
</li>
</ul>
</div>  
</li>
</ul>
</div>


controller
app.controller('ApplicationController', ['$scope', 'config', 'ApplicationService', function ($scope, config, ApplicationService) {
var vm = this;
var countApplicationByVersion = 6;
var countApplicationTopUsing = 5;
vm.title = 'App Center';
vm.applicationTopUsingtitle = 'Top Using Apps';
vm.assetsUrl = config.assetsUrl;
vm.pathApplication = config.pathApplication;
vm.pathApplicationVersion = config.pathApplicationVersion;
vm.applicationVersion = [];
vm.applicationByVersion = [];
vm.applicationTopUsing = [];
vm.getApplicationVersion = function () {
ApplicationService.getApplicationVersion()
.then(
function (applicationVersion) {
vm.applicationVersion = applicationVersion;
},
function (data) {
console.log(data)
}
);
}
vm.getApplicationByVersion = function (id) {
if(id != null){
ApplicationService.getApplicationByVersion(id, countApplicationByVersion)
.then(
function (applicationByVersion) {
vm.applicationByVersion = applicationByVersion;
console.log(vm.applicationByVersion);
console.log(applicationByVersion.length);
},
function (data) {
console.log(data)
}
);
}
}
vm.getApplicationTopUsing = function () {
ApplicationService.getApplicationTopUsing(countApplicationTopUsing)
.then(
function (applicationTopUsing) {
vm.applicationTopUsing = applicationTopUsing;
}, 
function (data) {
console.log(data);
}
);
};
vm.getApplicationVersion();
vm.getApplicationByVersion();
vm.getApplicationTopUsing();
}]);

service
app.service('ApplicationService', ['$http', '$q', 'config', function ($http, $q, config) {
var factory = {
applicationVersion: [],
applicationByVersion: [],
applicationTopUsing: [],
getApplicationVersion: getApplicationVersion,
getApplicationByVersion: getApplicationByVersion,
getApplicationTopUsing: getApplicationTopUsing
};
function getApplicationVersion() {
var defer = $q.defer();
var data = {
page: 0,
size: 0
};
var cfg = {
params: data,
header: {
'enctype': 'multipart/form-data',
'accept': 'application/json'
}
};
$http.get(config.apiUrl + 'version', cfg)
.then(
function (response) {
//success callback
if(response.status == 200) {
factory.applicationVersion = response.data.data.versions;
defer.resolve(response.data.data.versions);
}
},
function (response) {
//failure callback
defer.reject('Failed to get data');
}
);
return defer.promise;
}
function getApplicationByVersion(versionId, size) {
var defer = $q.defer();
var data = {
page: 0,
size: size
};
var cfg = {
params: data,
header: {
'enctype': 'multipart/form-data',
'accept': 'application/json'
}
};
$http.get(config.apiUrl + 'application/version/' + versionId, cfg)
.then(
function (response) {
//success callback
if(response.status == 200) {
factory.applicationByVersion = response.data.data.applications;
defer.resolve(response.data.data.applications);
}
},
function (response) {
//failure callback
defer.reject('Failed to get data');
}
);
return defer.promise;
}
function getApplicationTopUsing(size) {
var defer = $q.defer();
var data = {
page: 0,
size: size
};
var cfg = {
params: data,
header: {
'enctype': 'multipart/form-data',
'accept': 'application/json'
}
};
$http.get(config.apiUrl + 'application/top/using', cfg)
.then(
function (response) {
//success callback
if(response.status == 200) {
factory.applicationTopUsing = response.data.data.applications;
defer.resolve(response.data.data.applications);
}
},
function (response) {
//failure callback
defer.reject('Failed to get data');
}
);
return defer.promise;
};
return factory;
}]);


output


my problem is why controller return same value for variable "applicationByVersion"  ?

Thank you before

Sander Elias

unread,
Jul 16, 2016, 2:55:39 AM7/16/16
to AngularJS
the service returns a lot of separate values, but you store all results in a single variable, each one overwriting the next in vm.applicationVersion.


Regards
Sander

Paijo RX

unread,
Jul 21, 2016, 11:05:18 PM7/21/16
to AngularJS
sorry about my code.
i am a android programmer.
usually on android i just call "new" object to create the instance inside the loop to create isolated "object-variable" for every loop.

so, how do that in angularjs? how to isolated the variable from every loop ?

Thank you,

Sander Elias

unread,
Jul 22, 2016, 10:38:53 AM7/22/16
to AngularJS
Hi Paijo.

Every row has it's own 'scope' (dont' use that!!) Most of what you need is taken care of automatically. But you have only 1 controller, and if you want to store multiple variants for multiple rows, you can store it in the data of the row itself, or keep an parallel array.

Regards
Sander

Anka Wirawan

unread,
Jul 24, 2016, 9:06:47 PM7/24/16
to ang...@googlegroups.com
finally i am using array. thanks

--
You received this message because you are subscribed to a topic in the Google Groups "AngularJS" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/angular/Qb84aYryMG0/unsubscribe.
To unsubscribe from this group and all its topics, send an email to angular+u...@googlegroups.com.

To post to this group, send email to ang...@googlegroups.com.

Paijo RX

unread,
Jul 25, 2016, 8:57:56 PM7/25/16
to AngularJS
this is my updated code :

on controller just add this function :

function Version(id, app){
this.id = id;
this.app = app; 
}

and update this function :

vm.getApplicationByVersion = function (id) {
if(id != null){
ApplicationService.getApplicationByVersion(id, countApplicationByVersion)
.then(
function (applicationByVersion) {
vm.applicationByVersion.push(new Version(id, applicationByVersion));
},
function (data) {
console.log(data)
}
);
}

view for the category :

<div class="application-category-list col-md-9">
<ul>
<li ng-repeat-start="version in ctrl.applicationVersion | orderBy: 'id'" ng-repeat-end ng-init="ctrl.getApplicationByVersion(version.id)">
<div class="row">
<div class="application-category">
<h4>{{version.name}}</h4>
</div>
</div>
<application-per-version></application-per-version>
</li>
</ul>
</div>

view for the details :

<div class="row">
<ul class="application-card-list" ng-repeat-start="version in ctrl.applicationByVersion | filter: { id: version.id}" ng-repeat-end>
<li class="col-xs-12 col-sm-3 col-md-2" ng-repeat-start="application in version.app" ng-repeat-end>
<div class="application-card-box">
<div class="application-card-image">
<a ng-href="{{ctrl.pathApplicationDetail + application.id}}"><img ng-src="{{ctrl.assetsUrl + application.image}}" alt=""></a>
</div>
<div class="application-card-body">
<a ng-href="{{ctrl.pathApplicationDetail + application.id}}"><p class="application-card-title">{{application.name}}</p></a>
<p class="application-card-subtitle">{{application.version.name}}</p>
<p class="application-card-version">{{application.number}}</p>
</div>
</div>
</li>
</ul>
</div>  


i think this is not the best solution. but for this time i just found this way...



Pada Senin, 25 Juli 2016 08.06.47 UTC+7, Paijo RX menulis:
finally i am using array. thanks
On Fri, Jul 22, 2016 at 9:38 PM, Sander Elias <sande...@gmail.com> wrote:
Hi Paijo.

Every row has it's own 'scope' (dont' use that!!) Most of what you need is taken care of automatically. But you have only 1 controller, and if you want to store multiple variants for multiple rows, you can store it in the data of the row itself, or keep an parallel array.

Regards
Sander

--
You received this message because you are subscribed to a topic in the Google Groups "AngularJS" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/angular/Qb84aYryMG0/unsubscribe.
To unsubscribe from this group and all its topics, send an email to angular+unsubscribe@googlegroups.com.

Sander Elias

unread,
Jul 25, 2016, 11:55:31 PM7/25/16
to AngularJS
Hi Paijo,

There are a couple (better?) ways to solve this. If you want some more help with this, create a plunk, and open up a new thread.

Regards
Sander

Anka Wirawan

unread,
Jul 26, 2016, 5:20:10 AM7/26/16
to ang...@googlegroups.com
Ok, tomorrow i will upload my code on plunker and create new thread.

--
You received this message because you are subscribed to a topic in the Google Groups "AngularJS" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/angular/Qb84aYryMG0/unsubscribe.
To unsubscribe from this group and all its topics, send an email to angular+u...@googlegroups.com.

Bala krishnan

unread,
Jul 30, 2016, 3:39:58 AM7/30/16
to AngularJS
Hi Paiji,

I am waiting for your update using plunker for last three days, please share your plunker as soon as possible. 

Ayan Misra

unread,
Dec 4, 2018, 3:32:38 AM12/4/18
to Angular and AngularJS discussion
Hi  Eric,

Is there any way to do it with out reloading ng-controller? don't want to write ng-controller inside repeat.
Reply all
Reply to author
Forward
0 new messages