Discussion:
[AngularJS] $http in a directive create an infinite lopp
Aimery Marsily
2015-05-29 11:49:07 UTC
Permalink
Hi all !

I have create a directive to get a thumbnail from a Vimeo video Url. But
when calling the Vimeo API with $http I end up with an infinite loop. You
can see it here : http://plnkr.co/edit/oqXxr4GdkaaThUxGNlnW?p=preview
WARNING : the problem is in the getThumbnailUrl() method that create an
infinite loop, you have to uncomment the code in it and paste in the input
a Vimeo video

My directive should find the thumbnail of the video from the Vimeo API and
display it. I know I'm doing something wrong but can't find what. Any help
will be nice

Nota : The Plunker code is a quick example, sorry if it's not clean clean.
--
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+***@googlegroups.com.
To post to this group, send email to ***@googlegroups.com.
Visit this group at http://groups.google.com/group/angular.
For more options, visit https://groups.google.com/d/optout.
Mo Moadeli (CREDACIOUS)
2015-05-29 14:15:23 UTC
Permalink
Basically you have a classic asynchronicity clash. Your main thread
(ng-repeat) can't wait while each of your secondary threads ($http) takes
their time so the ng-repeat loses it's place in the loop and continues
indefinitely. It's somewhat like changing the 'i' within a for loop making
it loop forever.

In more detail, you are calling getThumbnailUrl() within your ng-repeat
(main thread) which completes rapidly. Meanwhile, each getThumbnailUrl()
is really an async $http call (separate thread) that WAITS for a 'success'
callback/promise which takes FAR longer than the entire ng-repeat loop to
complete. By the time ONE $http.success() returns the ng-repeat loop has
lost where it is in it's index. And at that point ng-repeat is corrupt
because it doesn't know what to show or where in the loop it is so it goes
on and on.

The solution is simple too. FIRST retrieve all your thumbnails
(getThumbnailUrl()) and store them in an array and THEN do an ng-repeat.

Hope this helps.
Post by Aimery Marsily
Hi all !
I have create a directive to get a thumbnail from a Vimeo video Url. But
when calling the Vimeo API with $http I end up with an infinite loop. You
can see it here : http://plnkr.co/edit/oqXxr4GdkaaThUxGNlnW?p=preview
WARNING : the problem is in the getThumbnailUrl() method that create an
infinite loop, you have to uncomment the code in it and paste in the input
a Vimeo video
My directive should find the thumbnail of the video from the Vimeo API and
display it. I know I'm doing something wrong but can't find what. Any help
will be nice
Nota : The Plunker code is a quick example, sorry if it's not clean clean.
--
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+***@googlegroups.com.
To post to this group, send email to ***@googlegroups.com.
Visit this group at http://groups.google.com/group/angular.
For more options, visit https://groups.google.com/d/optout.
Aimery Marsily
2015-05-29 15:00:08 UTC
Permalink
Thanks for clarifying the error, I understand now.

When you are saying "*FIRST retrieve all your thumbnails
(getThumbnailUrl()) and store them in an array and THEN do an ng-repeat.*"
You mean that mon controller or link function should do that and not any
call in the template, is that it ?
Post by Mo Moadeli (CREDACIOUS)
Basically you have a classic asynchronicity clash. Your main thread
(ng-repeat) can't wait while each of your secondary threads ($http) takes
their time so the ng-repeat loses it's place in the loop and continues
indefinitely. It's somewhat like changing the 'i' within a for loop making
it loop forever.
In more detail, you are calling getThumbnailUrl() within your ng-repeat
(main thread) which completes rapidly. Meanwhile, each getThumbnailUrl()
is really an async $http call (separate thread) that WAITS for a 'success'
callback/promise which takes FAR longer than the entire ng-repeat loop to
complete. By the time ONE $http.success() returns the ng-repeat loop has
lost where it is in it's index. And at that point ng-repeat is corrupt
because it doesn't know what to show or where in the loop it is so it goes
on and on.
The solution is simple too. FIRST retrieve all your thumbnails
(getThumbnailUrl()) and store them in an array and THEN do an ng-repeat.
Hope this helps.
Post by Aimery Marsily
Hi all !
I have create a directive to get a thumbnail from a Vimeo video Url. But
when calling the Vimeo API with $http I end up with an infinite loop. You
can see it here : http://plnkr.co/edit/oqXxr4GdkaaThUxGNlnW?p=preview
WARNING : the problem is in the getThumbnailUrl() method that create an
infinite loop, you have to uncomment the code in it and paste in the input
a Vimeo video
My directive should find the thumbnail of the video from the Vimeo API
and display it. I know I'm doing something wrong but can't find what. Any
help will be nice
Nota : The Plunker code is a quick example, sorry if it's not clean clean.
--
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+***@googlegroups.com.
To post to this group, send email to ***@googlegroups.com.
Visit this group at http://groups.google.com/group/angular.
For more options, visit https://groups.google.com/d/optout.
Mo Moadeli (CREDACIOUS)
2015-05-29 16:29:56 UTC
Permalink
Aimery, before continuing, please help me understand what you are trying to
do? If you are getting ONE thumbnail for your video, why are your using an
ng-repeat at all?

Thanks,
Mo
Post by Aimery Marsily
Thanks for clarifying the error, I understand now.
When you are saying "*FIRST retrieve all your thumbnails
(getThumbnailUrl()) and store them in an array and THEN do an ng-repeat.*"
You mean that mon controller or link function should do that and not any
call in the template, is that it ?
Post by Mo Moadeli (CREDACIOUS)
Basically you have a classic asynchronicity clash. Your main thread
(ng-repeat) can't wait while each of your secondary threads ($http) takes
their time so the ng-repeat loses it's place in the loop and continues
indefinitely. It's somewhat like changing the 'i' within a for loop making
it loop forever.
In more detail, you are calling getThumbnailUrl() within your ng-repeat
(main thread) which completes rapidly. Meanwhile, each getThumbnailUrl()
is really an async $http call (separate thread) that WAITS for a 'success'
callback/promise which takes FAR longer than the entire ng-repeat loop to
complete. By the time ONE $http.success() returns the ng-repeat loop has
lost where it is in it's index. And at that point ng-repeat is corrupt
because it doesn't know what to show or where in the loop it is so it goes
on and on.
The solution is simple too. FIRST retrieve all your thumbnails
(getThumbnailUrl()) and store them in an array and THEN do an ng-repeat.
Hope this helps.
Post by Aimery Marsily
Hi all !
I have create a directive to get a thumbnail from a Vimeo video Url. But
when calling the Vimeo API with $http I end up with an infinite loop. You
can see it here : http://plnkr.co/edit/oqXxr4GdkaaThUxGNlnW?p=preview
WARNING : the problem is in the getThumbnailUrl() method that create an
infinite loop, you have to uncomment the code in it and paste in the input
a Vimeo video
My directive should find the thumbnail of the video from the Vimeo API
and display it. I know I'm doing something wrong but can't find what. Any
help will be nice
Nota : The Plunker code is a quick example, sorry if it's not clean clean.
--
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+***@googlegroups.com.
To post to this group, send email to ***@googlegroups.com.
Visit this group at http://groups.google.com/group/angular.
For more options, visit https://groups.google.com/d/optout.
Aimery Marsily
2015-05-29 17:19:23 UTC
Permalink
I get one thumbnail per video, but there are many videos. So the ng-repeat
is to display each videos with one thumbnail.
--
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+***@googlegroups.com.
To post to this group, send email to ***@googlegroups.com.
Visit this group at http://groups.google.com/group/angular.
For more options, visit https://groups.google.com/d/optout.
Sander Elias
2015-05-29 18:24:11 UTC
Permalink
Hi Aimery,

Put in a local inside the repeater, and use that.
Something like this:
...
template:
'<input type="text" ng-model="new" maxlength="150"
placeholder="Vimeo video URL">' +
'<input type="submit" ng-click="add()">' +
'<div ng-repeat="video in videos track by $index"
class="video" ' +
'ng-init="local={};getThumbnailUrl(video,local)"' +
'<img ng-src="local.tumbnail" class="thumbnail"
alt="{{ video.source }}" />' +
'</div>',
controller: function($scope, $element, $attrs) {
$scope.add = function() {
var data = parseVideoUrl($scope.new);

if (data === null) { return false; }

$scope.videos.push(data);
$scope.new = "";
};


$scope.getThumbnailUrl = function(video, local) {
// uncomment the bottom part will make an infinite loop
$http.get('http://vimeo.com/api/v2/video/' + video.code +
'.json')
.success(function(data, status) {
local.tumbnail= data[0]['thumbnail_medium'];
});
};
...


Does this help you a bit?
Regards
Sander
--
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+***@googlegroups.com.
To post to this group, send email to ***@googlegroups.com.
Visit this group at http://groups.google.com/group/angular.
For more options, visit https://groups.google.com/d/optout.
Mo Moadeli (CREDACIOUS)
2015-05-29 18:57:56 UTC
Permalink
I spent a few minutes and fixed it for you. Plunkr:
http://plnkr.co/edit/LyhSB7Iu64zdAHIsFQsP?p=preview

The solution is to first remove the getThumbnailURL(video) from with the
ng-repeat which is creating an async clash.

Then simply move that code to your controller's add(). Within the add()
get your thumbnail using $http and upon success THEN update the videos
array (which will then trigger the ng-repeat). The ng-repeat will now
traverse a simple array no longer making clashing $http/async calls.

NOTE: To reiterate, the key here is to push the video object to your array
AFTER the async $http completes.

Mo
Post by Aimery Marsily
I get one thumbnail per video, but there are many videos. So the ng-repeat
is to display each videos with one thumbnail.
--
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+***@googlegroups.com.
To post to this group, send email to ***@googlegroups.com.
Visit this group at http://groups.google.com/group/angular.
For more options, visit https://groups.google.com/d/optout.
Mo Moadeli (CREDACIOUS)
2015-05-29 19:04:17 UTC
Permalink
BTW, the solution is in the last plunkr fork/version. Also, what I
provided was simply a 'fix'. As a general rule $http calls are within
singleton AngularJS services and not inside controllers.
Post by Mo Moadeli (CREDACIOUS)
http://plnkr.co/edit/LyhSB7Iu64zdAHIsFQsP?p=preview
The solution is to first remove the getThumbnailURL(video) from with the
ng-repeat which is creating an async clash.
Then simply move that code to your controller's add(). Within the add()
get your thumbnail using $http and upon success THEN update the videos
array (which will then trigger the ng-repeat). The ng-repeat will now
traverse a simple array no longer making clashing $http/async calls.
NOTE: To reiterate, the key here is to push the video object to your array
AFTER the async $http completes.
Mo
Post by Aimery Marsily
I get one thumbnail per video, but there are many videos. So the
ng-repeat is to display each videos with one thumbnail.
--
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+***@googlegroups.com.
To post to this group, send email to ***@googlegroups.com.
Visit this group at http://groups.google.com/group/angular.
For more options, visit https://groups.google.com/d/optout.
Sander Elias
2015-05-29 19:31:55 UTC
Permalink
hi Mo,
Post by Mo Moadeli (CREDACIOUS)
NOTE: To reiterate, the key here is to push the video object to your array
AFTER the async $http completes.
Not really needed. If you look at my sample, it will just work, and the
table gets enriched with thumbnails as they come in. There is no technical
need to do async outside an repeat. It all depends on use-case.
I do agree that you shouldn't have $http insiside an controller, you are
better of if you put it in a factory. But putting asynchronous in a
factory, doesn't make it work synchronous, so the problem stays the same.
When you make the thing like you propose, the user has to wait for every
thumbnail to arrive, before getting anything on screen. I think that's a
bad idea. Might work fine if you have up to 4 thumbs, but imagine your
search return 100 hits?
(and there you have an reason too be able to cancel $http requests. That's
challenging in angular 1.x ;) )


Regards
Sander
--
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+***@googlegroups.com.
To post to this group, send email to ***@googlegroups.com.
Visit this group at http://groups.google.com/group/angular.
For more options, visit https://groups.google.com/d/optout.
Mo Moadeli (CREDACIOUS)
2015-05-29 20:36:59 UTC
Permalink
There is no technical need to do async outside an repeat.
Sander, the OP, in fact, DOES have an async call ($http) INSIDE an
ng-repeat. THAT is absolutely a no-no . So I'm not clear with what you are
saying.

.. the user has to wait for every thumbnail to arrive

That is absolutely correct, especially with the setup the OP has. Take
note the OP is using a manual 'submit' to populate the table so 'waiting'
is built in anyway. *In the alternate (and possibly more realistic) case,*
that the controller already has an array of videos to request thumbnail
URLs, then they can be sent en masse through a SINGLE $http and ALL the
image URLS will return en masse as well. In this alternate case, the
program can rapidly populate the screen once the async $http returns with a
promise. But this is still synchronous and DOM manipulation can only be
performed after the promise/callback however long it takes. Additionally
that is EXACTLY what promises/callbacks are for.

But putting asynchronous in a factory, doesn't make it work synchronous,
so the problem stays the same.
That is correct but again, however, I'm not clear how it relates to this
topic. I merely mentioned it as an architectural concern As you imply,
the venue for the service vs controller is irrelevant to the notion of
asynchronicity.

To conclude, the concept is nuanced but crucial and universal: Once you
invoke $http, then you have spun off a separate thread from your main
thread (main UI) and, as asynchronicity dictates, you can only take action
on the DOM (main thread) once (if ever) the promise calls back successfully
(or not).

More on this:
https://github.com/angular/angular.js/issues/3196
http://stackoverflow.com/questions/20137350/angular-service-containing-http-get-call-not-working-with-ng-repeat

Mo
hi Mo,
Post by Mo Moadeli (CREDACIOUS)
NOTE: To reiterate, the key here is to push the video object to your
array AFTER the async $http completes.
Not really needed. If you look at my sample, it will just work, and the
table gets enriched with thumbnails as they come in. There is no technical
need to do async outside an repeat. It all depends on use-case.
I do agree that you shouldn't have $http inside an controller, you are
better of if you put it in a factory. But putting asynchronous in a
factory, doesn't make it work synchronous, so the problem stays the same.
When you make the thing like you propose, the user has to wait for every
thumbnail to arrive, before getting anything on screen. I think that's a
bad idea. Might work fine if you have up to 4 thumbs, but imagine your
search return 100 hits?
(and there you have an reason too be able to cancel $http requests. That's
challenging in angular 1.x ;) )
Regards
Sander
--
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+***@googlegroups.com.
To post to this group, send email to ***@googlegroups.com.
Visit this group at http://groups.google.com/group/angular.
For more options, visit https://groups.google.com/d/optout.
Aimery Marsily
2015-05-29 22:54:18 UTC
Permalink
Thanks to both of you I understand my error and how I should handle it. Big
thanks guys !
--
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+***@googlegroups.com.
To post to this group, send email to ***@googlegroups.com.
Visit this group at http://groups.google.com/group/angular.
For more options, visit https://groups.google.com/d/optout.
Sander Elias
2015-05-30 03:50:15 UTC
Permalink
Hi Mo,

It seems we disagree. No problem, I just want to explain something a little
deeper.
However, you seem to work under the idea that the OP is iterating over the
result of the async. That is not the case in OP's question. So your links
are not relevant. The $http's are inside the viewed rows. That is a
different thing.
There is no technical need to do async outside an repeat.
Sander, the OP, in fact, DOES have an async call ($http) INSIDE an
ng-repeat. THAT is absolutely a no-no . So I'm not clear with what you are
saying.
Well, I'm saying that it is an real possibility.* Definitively not a no-no*!
The sample of the OP is perhaps more suited to your solution, but that does
not make it the only option. If your UI becomes more complex (and they tend
to...), you can have the situations that you need to resolve some promises
inside a repeater. For example, in this example, the result table has an
'expand' button that only exposes the thumbnail when the user takes some
action (clicks the expand button...). So you only need the thumbnail if it
is view-able. You solution would mean that every thumbnail is fetched,
whether it is viewed or not. Or the result is paginated, do you still need
all the thumbnails?

As a side-note, I seldom see an API that can deliver multiple URL's at
once. I'm fairly sure the big providers like google, vimeo, MS, will never
provide such an interface for thumbnails (Technically, it becomes very hard
to spread such a call over multiple server instances, so this would create
a very big demand on a single instance, and they do not like that. For good
reasons!)

Another note, async has noting to do with threads. Don't believe me, google
around, or read some browsers code. Do not refer to this as it being
another thread, or if you do, do say you can use it as an thinking model.
It has similarities alright, but it is not how it works in most browsers.

Regards
Sander
--
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+***@googlegroups.com.
To post to this group, send email to ***@googlegroups.com.
Visit this group at http://groups.google.com/group/angular.
For more options, visit https://groups.google.com/d/optout.
Mo Moadeli (CREDACIOUS)
2015-05-30 21:36:23 UTC
Permalink
Post by Sander Elias
However, you seem to work under the idea that the OP is iterating over the
result of the async. <clip>
You are incorrect. Without reiterating the obvious further, note, that I
took the OPs code and with with minor modifications (i.e. removing long
running/async $http invocations within the ng-repeat) have it working under
the exact circumstances the OP had provided in plunker. I am not 'under'
any idea other than the OPs.

Thanks!,
Mo
Post by Sander Elias
Hi Mo,
It seems we disagree. No problem, I just want to explain something a
little deeper.
However, you seem to work under the idea that the OP is iterating over the
result of the async. That is not the case in OP's question. So your links
are not relevant. The $http's are inside the viewed rows. That is a
different thing.
There is no technical need to do async outside an repeat.
Sander, the OP, in fact, DOES have an async call ($http) INSIDE an
ng-repeat. THAT is absolutely a no-no . So I'm not clear with what you are
saying.
Well, I'm saying that it is an real possibility.* Definitively not a
no-no*! The sample of the OP is perhaps more suited to your solution, but
that does not make it the only option. If your UI becomes more complex (and
they tend to...), you can have the situations that you need to resolve some
promises inside a repeater. For example, in this example, the result table
has an 'expand' button that only exposes the thumbnail when the user takes
some action (clicks the expand button...). So you only need the thumbnail
if it is view-able. You solution would mean that every thumbnail is
fetched, whether it is viewed or not. Or the result is paginated, do you
still need all the thumbnails?
As a side-note, I seldom see an API that can deliver multiple URL's at
once. I'm fairly sure the big providers like google, vimeo, MS, will never
provide such an interface for thumbnails (Technically, it becomes very hard
to spread such a call over multiple server instances, so this would create
a very big demand on a single instance, and they do not like that. For good
reasons!)
Another note, async has noting to do with threads. Don't believe me,
google around, or read some browsers code. Do not refer to this as it being
another thread, or if you do, do say you can use it as an thinking model.
It has similarities alright, but it is not how it works in most browsers.
Regards
Sander
--
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+***@googlegroups.com.
To post to this group, send email to ***@googlegroups.com.
Visit this group at http://groups.google.com/group/angular.
For more options, visit https://groups.google.com/d/optout.
Sander Elias
2015-05-31 16:07:16 UTC
Permalink
Hi Mo.

Well, then I was thrown of by the links you included, which point to
another problem ;)

Regards
Sander

On Saturday, May 30, 2015 at 11:36:23 PM UTC+2, Mo Moadeli (CREDACIOUS)
Post by Sander Elias
However, you seem to work under the idea that the OP is iterating over the
Post by Sander Elias
result of the async. <clip>
You are incorrect. Without reiterating the obvious further, note, that I
took the OPs code and with with minor modifications (i.e. removing long
running/async $http invocations within the ng-repeat) have it working under
the exact circumstances the OP had provided in plunker. I am not 'under'
any idea other than the OPs.
Thanks!,
Mo
Post by Sander Elias
Hi Mo,
It seems we disagree. No problem, I just want to explain something a
little deeper.
However, you seem to work under the idea that the OP is iterating over
the result of the async. That is not the case in OP's question. So your
links are not relevant. The $http's are inside the viewed rows. That is a
different thing.
There is no technical need to do async outside an repeat.
Sander, the OP, in fact, DOES have an async call ($http) INSIDE an
ng-repeat. THAT is absolutely a no-no . So I'm not clear with what you are
saying.
Well, I'm saying that it is an real possibility.* Definitively not a
no-no*! The sample of the OP is perhaps more suited to your solution,
but that does not make it the only option. If your UI becomes more complex
(and they tend to...), you can have the situations that you need to resolve
some promises inside a repeater. For example, in this example, the result
table has an 'expand' button that only exposes the thumbnail when the user
takes some action (clicks the expand button...). So you only need the
thumbnail if it is view-able. You solution would mean that every thumbnail
is fetched, whether it is viewed or not. Or the result is paginated, do you
still need all the thumbnails?
As a side-note, I seldom see an API that can deliver multiple URL's at
once. I'm fairly sure the big providers like google, vimeo, MS, will never
provide such an interface for thumbnails (Technically, it becomes very hard
to spread such a call over multiple server instances, so this would create
a very big demand on a single instance, and they do not like that. For good
reasons!)
Another note, async has noting to do with threads. Don't believe me,
google around, or read some browsers code. Do not refer to this as it being
another thread, or if you do, do say you can use it as an thinking model.
It has similarities alright, but it is not how it works in most browsers.
Regards
Sander
--
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+***@googlegroups.com.
To post to this group, send email to ***@googlegroups.com.
Visit this group at http://groups.google.com/group/angular.
For more options, visit https://groups.google.com/d/optout.
Loading...