Angualrjs ng-src async image loading from a function call - angularjs

I am wondering if it's possible to assign a result of a function call to an ng-src in an image?
Basically, I need to retrieve a set of thumbnails for a set of data from an aspx page, there may or may not be a thumbnail. So I need to show a different preset image for a case when there is nothing returned from the thumbnail page.
Something like this:
<img ng-src="getThumbnail({{ item.Id }})" />
Where getThumbnail is a function which makes a http request to a url such as
http://localhost/Srv/getThumbnail.aspx?id=111
I've tried this but Angular is producing this:
<img ng-src="getThumbnail(111)" src="getThumbnail(111)">
Instead of calling the function. Is there a better way for doing this?

I've commented on your question but I'll offer my suggestion as a solution after looking at the AngularJS documentation. The ng-src directive will correctly set the src attribute of img. This directive can contain Angular expressions and these can call functions contained in your controller and reachable in the current scope. Hence instead of:
<img ng-src="getThumbnail({{ item.Id }})" />
Try this:
<img ng-src="{{ getThumbnail(item.Id) }}" />
In the former declaration, the function isn't available to the Angular scope, but it is in the latter. (If I am not mistaken, as I haven't tried the code myself).
Make sure the getThumbnail function is in a controller that is reachable to the current scope.

Related

Why does an un-rendered element behave as if it has been rendered?

I am slightly confused by this behaviour of Angular JS.
Angular.js' ng-if will not render an element if the expression evaluates to false, if the documents are anything to go by. I have this piece of code in my html template:
<div ng-if="false">
<img src="{{ imgPath }}" />
<p>This block is not rendered</p>
</div>
// In the controller
$scope.imgPath = "/invalid/image/path";
When this template is rendered, I cannot, as expected, see the img element or the p element on developer tools:
However... when I look at the network tab, there is a request to fetch the image:
I thought that if the element is not rendered, it wouldn't function in any way or form since it doesn't exist... Why does the browser fetch the image in this case?
You can see the working code on plnkr here, you'll have to hit F12 to watch the error on the console.
I know that using ng-src= {{ }} instead of src={{ }} would solve the issue of img src being fetched with unresolved expression before the data is bound, but, this question deals more with why ng-if isn't stopping the request in the first place
It takes AngularJS a small amount of time to process your markup. So, intially when your page loads, the browser does it's thing trying to process the markup. It sees this:
<div ng-if="false">
<img src="{{ imgPath }}" />
<p>This block is not rendered</p>
</div>
But, so far, AngularJS has not been loaded, and the AngularJS directives have no meaning. The browser attempts to load an image located at the literal URL of : {{ imgPath }}, which the URL encoder will translate to %7B%7B%20imgPath%20%7B%7B, which will fail (obviously). Still, AngularJS has not been loaded.
Once AngularJS finally loads, it goes through the DOM and applies the ngIf directive and removes the node.
This is why you want to use ngSrc. This will prevent the image request, since the browser doesn't understand the ng-src directive and won't treat it like a src attribute.

AngularJS expressions not working in <img-crop>

I am trying to modify the project ngImgCrop (https://github.com/alexk111/ngImgCrop) for allowing crop multiple images in the same page, but I do not know how many images I would need, this is created dynamically. So, I need to associate to the 'image' field of a dynamic value, and at the same time I put this variable in my scope. The problem is that this label is not evaluating the angular code.
<div class="cropArea" id="{{'person'+person.Id}}">
<img-crop image="{{'person'+person.Id}}" result-image="myCroppedImage"></img-crop>
</div>
Even when they have the same code, when the page is loaded the html code shows:
<div class="cropArea" id="person12345">
<img-crop image="{{'person'+person.Id}}" result-image="myCroppedImage"></img-crop>
</div>
In my scope since the beginning the variable $scope.person12345 is created, but It is impossible to make the binding without this part.
What can I do?
Note:
In my init() function I create all the variables:
angular.forEach(persons, function (person, index) {
$scope['person'+person.Id]='';
});
I actually can see the variable $scope.person12345 when the page is loaded. In any case why does the expression worked for the div and not for the img-crop?
Please put your expression as a function which will execute in the Controller. string(appending string) will return by a function like below.
<div class="cropArea" id="{{'person'+person.Id}}">
<img-crop image="getImagePath(person.Id)" result-image="myCroppedImage"></img-crop>
</div>
Controller like below:
$scope.getImagePath = function(id){return 'person'+id+'.png';};
Some how there is no parser available in the directive of image, that's why you need to give parsed expression via a controller.

Use of ng-src vs src

This tutorial demonstrates the use of the directive ngSrc instead of src :
<ul class="phones">
<li ng-repeat="phone in phones" class="thumbnail">
<img ng-src="{{phone.imageUrl}}">
</li>
</ul>
They ask to:
Replace the ng-src directive with a plain old src attribute.
Using tools such as Firebug, or Chrome's Web Inspector, or inspecting the
webserver access logs, confirm that the app is indeed making an
extraneous request to /app/%7B%7Bphone.imageUrl%7D%7D (or
/app/{{phone.imageUrl}}).
I did so and it gave me the correct result:
<li class="thumbnail ng-scope" ng-repeat="phone in phones">
<img src="img/phones/motorola-xoom.0.jpg">
</li>
Is there a reason why?
From Angular docs
The buggy way to write it:
<img src="http://www.gravatar.com/avatar/{{hash}}"/>
The correct way to write it:
<img ng-src="http://www.gravatar.com/avatar/{{hash}}"/>
Why? this is because on load of page, before angular bootstrapping and creation of controllers, browser will try to load image from http://www.gravatar.com/avatar/{{hash}} and it will fail. Then once angular is started, it understands that that {{hash}} has to be replaced with say logo.png, now src attribute changes to http://www.gravatar.com/avatar/logo.png and image correctly loads. Problem is that there are 2 requests going and first one failing.
TO solve this we should use ng-src which is an angular directive and angular will replace ng-src value into src attribute only after angular bootstrapping and controllers are fully loaded, and at that time {{hash}} would have already been replaced with correct scope value.
<img ng-src="{{phone.imageUrl}}">
This gives you expected result, because phone.imageUrl is evaluated and replaced by its value after angular is loaded.
<img src="{{phone.imageUrl}}">
But with this, the browser tries to load an image named {{phone.imageUrl}}, which results in a failed request.
You can check this in the console of your browser.
The src="{{phone.imageUrl}}" is unnecessary and creates an extra request by the browser. The browser will make at least 2 GET requests attempting to load that image:
before the expression is evaluated {{phone.imageUrl}}
after the expression is evaluated img/phones/motorola-xoom.0.jpg
You should always use ng-src directive when dealing with Angular expressions. <img ng-src="{{phone.imageUrl}}"> gives you the expected result of a single request.
On a side note, the same applies to ng-href so you don't get broken links till the first digest cycle kicks in.
Well actually it makes 100% sense because HTML gets processed sequentially and when this HTML page is being processed line by line, by the time it gets to this image, the line and processing the image, our phone.imageUrl is not yet defined yet.
And in fact, Angular JS has not yet processed this chunk of HTML, and hasn't yet looked for these placeholders and substitute these expressions with the values. So what ends up happening is that the browser gets this line and tries to fetch this image at this URL.
And of course this is a bogus URL, if it still has those mustache and curly braces in it, and therefore it gives you a 404, but once of course Angular takes care of this, it substitutes this URL for the proper one, and then we still see the image, but yet the 404 error message remains in our console.
So, how can we take care of this? Well, we can not take care of this using regular HTML tricks. But, we can take care of it using Angular. We need somehow to tell the browser not to try to fetch this URL but at the same time fetch it only when Angular is ready for interpretation of these placeholders.
Well, one way of doing it is to put an Angular attribute on here instead of the standard HTML one. And the Angular attribute is just ng-src. So if we say that now, go back, you'll see that there's no errors anymore because the image only got fetched once Angular got a hold of this HTML and translated all the expressions into their values.

Image ng-Src with dependancy

I have an image on my page like so:
<img ng-src='http://someurl.com/image.png?parameter={{selected.id}}' />
Is there any way in Angular to prevent ng-src resolution until after {{selected.id}} is set? Basically i do not want to load image until selected.id is set. To set selected.id I need to make Ajax call. What I am experiencing is two calls for the image.
First one with url like so http://someurl.com/image.png?parameter=
Then the second (correct) call after selected.id is set
It seems that this should be easy to do but I cannot think of a way.
Thank You,
Victor
You can use ng-if so the img tag will actually be removed until selected.id is truthy. This means the request for the image won't be made until selected.id is set.
<img ng-src='http://someurl.com/image.png?parameter={{selected.id}}' ng-if="selected.id" />
You can use ng-show directive
<img ng-src='http://someurl.com/image.png?parameter={{selected.id}}'
ng-if="selected.id" />
You can find some demo here :http://jsbin.com/fatote/5/edit?html,js,output

AngularJS: Updating a view with a template from a controller?

I have been working with routing and I have seen how I can update the ng-view using routing and a view template.. But the problem I have is that I am doing a REST call and depending what I get back from the response I wish to update part of the DOM with a view template but I don't want to involve routing.
Does anyone know how I can do this? Or any examples would be great
Thanks in advance
Another answer. Based on your description in the comment, it sounds like you wish to display part of the DOM conditionally.
When you want to display part of the DOM conditionally, you have the following choices:
Use an ng-show and ng-hide directive.
Based on what returns from the RESTful call, you can set up a model that will identify the DOM that needs to be displayed. An example:
<div ng-show="status">
This text will be shown only when the status is truthy
</div>
<div ng-hide="status">
This text will be shown only when the status is false.
</div>
Inside your controller, you could then set the status to true or false based on your RESTful calls and based on which part of the DOM you wish to display post RESTful call.
You can use ng-switch directive
While the ng-show and ng-hide directives will display the content of your DOM conditionally, that is anybody could simply open the source file and see the contents for both, ng-switch directive will load the contents only based on which case fulfills the swtich. An example:
<div ng-switch on="status">
<div ng-switch-when="true">
This text will be shown only when the status is truthy.
Else this is completely hidden and cannot be seen even
when looking at the source.
</div>
<div ng-switch-when="false">
This text will be shown only when the status is false.
Else this is completely hidden and cannot be seen even
when looking at the source.
</div>
</div>
The first child div is shown when the status is true else it is not shown at all. The advantage over ng-show or ng-hide is that the DOM will not contain the child elements if the case is not fulfilled.
$location.path() can be used here.
So, in your Parent Controller, you can make the REST call. Once you have the data with you, you can then decide which route to take. The route value goes into the path() function.
As an example, let us say that if your REST call returns with cherries, you need to take the /foo path (which, based on your $routeProvider will load the template associated with that route). You can then write the following:
$location.path('/foo');
and it will loads the /foo path - $routeProvider will then take care of loading the template associated with that path.
Reference: $location

Resources