Get signed image url from S3 in Ionic page - angularjs

In ionic, I have an array of image keys. Using these keys we want to get an Amazon S3 signed URL.
I have :
<div *ngFor="let image of item.images">
<div>Key: {{image}} -- Url: {{getImageUrl(image) }}</div>
</div>
In the Javascript part I have following:
getImageUrl(imageKey) {
this.s3.getSignedUrl('getObject', {'Key': imageKey}, (err, url) => {
console.log(url);
return url;
});
}
In the logs I do see the URLs are correctly. However in the html page; there is nothing. I have tried many things; e.g. by having the method getImageUrl create a new variable; this remained undefined.
How should I modify my code to get this working?
I strongly have the impression that this is caused by the asynch nature of angularJS/Ionic/... and that probably I need to work with callbacks. However; I can't get my head around how you would get that callback into the HTML...
I hope this is a beginner's question!
thanks!

The problem is if you want to display an image, you should use the tag, empty div wont show anything, try this
<div *ngFor="let image of item.images">
<div>
<h1> Key: {{image}} -- Url: {{getImageUrl(image) }}
</h1>
<img *ngIf="image" [src]= {{getImageUrl(image) }}>
</div>
</div>

Related

Page render speed in AngularJS using ng-include

I've got an issue where I populate a page in pieces. There are an arbitrary number of categories with an arbitrary number of items. The code is generally something like the below (warning, transposed).
$scope.getItems = function(key) {
$http.get('get-items?key=' + key)
.then(function(res) {
for (let item of res.data) {
$scope.categories[item.category].items.push(item);
}
});
}
let populateCategories = function() {
for (let key in $scope.categories) {
$scope.getItems(key);
}
}
$scope.getCategories = function(next) {
$http.get('get-categories')
.then(function(res) {
$scope.categories = res.data;
next();
});
$scope.getCategories(populateCategories);
}
The idea is to first get what categories will be on the page, and render them, empty (but w/ a busy icon). After that, hit and endpoint one time per category and populate w/ the results. The busy icon is shown via ng-show & a boolean pointing to the size of the items. 1 or more items = no busy icon, an the items should show.
The loading of the categories more or less works. Populating them though, is not so free flowing. Watching the console output, it takes ages for the browser to render. The busy icon goes away somewhat quickly, but I don't see the items until a bunch of them are ready.
Worth noting, (I think) I saw this problem appear when I moved the html that displays each item from a single file, an template and used ng-include, as I'm using it on two different places. Surely that would not be a cause would it?
EDIT: Adding the html - simplified
item-template.html
<div class="row">
<div class="col-xs-2 col">
<img src="{{item.img}}">
</div>
<div class="col-xs-10 col">
<div>{{item.details}}</div>
</div>
</div>
list.html
<body>
<div class ="container-fluid">
<div class="row">
<div ng-repeat="(key, value) in categories">
<div>{{key}}</div>
<div ng-show="value.busy"">
<img ng-src="{{busy_image}}">
</div>
<div ng-repeat="item in value.items track by $index">
<!-- This in fact seems to be the culprit -->
<div ng-include="item-template.html">
</div>
</div>
</div>
</div>
</body>
So, playing around, if I simply paste the contents of template.html into list.html, the response is much, much better. Looking at this issue, the solution seems to be to use a cache service. I'm happy to use something like that but I'm still curious as to why. The template I'm using isn't small (166 lines) but I can't imagine it being that heavy either on a modern computer.
Several things from the top of head:
amount of items to be shown in the HTML. Large lists with x properties = alot of Angular watchers.
if there are alot of items, maybe check for an alternative to ng-repeat
instead of ng-include item-template.html, create a Component
ng-repeat with track by
use bind once
in this case you can replace ng-show with ng-if

Unable to provide image names dynamically to {% static " in AngularJS

<div class="container" ng-repeat="item in itemslist">
<img ng-src="{% static "img/new/item.imagename" %}" alt="" />
</div>
item - > is an object, below is the object definition
var firstItem = {};
firstItem.id = 0;
firstItem.name = "testfirstname";
firstItem.imagename = "cart.png";
var secondItem = {};
secondItem.id = 2;
secondItem.name = "testsecondname";
secondItem.imagename = "home.png";
itemslist- > [firstitem, seconditem]
In the runtime, item.imagename is not getting replaced by its value (cart.png). coming out of the iteration. The request still looks with the variable name (item.imagename) and not the value (cart.png).
Page not found (404)
http://example.localhost.com:8000/static/img/new/item.imagename
How do I get this working?
The order in which a page renders with Django is:
A request is made from the client (browser) to the server (ultimately Django).
Django matches a view using the url, and from the view renders the template in server.
The client gets the response and renders the page. At this stage, the client knows nothing about Django, its templating system etc.
Angular is a JS technology that operates client-side. It has absolutely no way to know about static files, Django templates etc.
Therefore, if you require to load an image in client-side, then you have to properly specify the url beforehands. Since you know the name, you need only to provide the rest of the url to angular.
To do that, you need to include settings.STATIC_URL to your context, and render it into a js variable in your template. This variable will travel through to client, and then in angular use it to properly build the url.
Be sure to take care of angular's security considerations about interpolation though, but that's a different matter entirely.
The server-client confusion is a common one when beginning web development.
I think this is what you need
<div class="container" ng-repeat="item in items>
<img ng-src="{{static + '/img/new/' + item.image}}" alt="" />
</div>
I consider static = base url, according to your comment
For anyone who has this issue in the future,
What ever you're static_url is in the django settings. For example:
STATIC_URL='/static/'
then in your template do this:
<div class="container" ng-repeat="item in items>
<img ng-src="static/img/new/{{item.image}}" alt="" />
</div>
I faced same issue, but in the context of using Mustache and Django templates. Here is what worked for me.
settings.py:
STATIC_URL = '/static/'
Mustache template part of html:
<div class="avatar"><img alt="" src="{% verbatim %}{{ userAvatar }}{% endverbatim %}" /></div>
Javascript that renders the Mustache template sends the "userAvatar" in the following format:
"userAvatar" : "/static/images/avatars/" + avatarNameFromServer

Open all links using system browser inside an ionic app

I need that all links inside a certain section of my app open in the system browser. The trick is that those links come from an external source (an API) so I can't add the ng-click function that helps me to open the links externally.
I'm using in-app-browser plugin (ng-cordova). In fact I have other links that open externally but in this case the links can be in any part of the content so my question would be how could I add the ng-click directive to all links after they are loaded? or if it's possible, how to config in-app-browser plugin to open ALL links in system browser?
By the way, the simple links don't open even in the inappbrowser: I tap on them and nothing happens.
Thanks for the help
AFAIK there isn't a way of doing that automatically, you must use the in app browser js code to open links externally consistently in every platform.
Your question doesn't give a clear example of what the server returns so I'm assuming you are getting a full block of html and are just rendering it on the screen. Assuming a request return something basic like :
<div id="my-links">
<a href='http://externallink.com'> External Link 1 </a>
<a href='http://externallink.com'> External Link 2 </a>
<a href='http://externallink.com'> External Link 3 </a>
</div>
And your request looks like:
$http.get('givemelinks').success(function(htmlData){
$scope.myContent = htmlData;
})
If you have access to the server side and can make changes:
Add a "inappbrowser" parameter to your request to detect if it should return inappbrowser compatible links and change the response from your server to be something like:
if (inappbrowser) {
<div id="my-links">
<div ng-click='openExternal($event)' data-external='http://externallink.com'> External Link 1 </div>
<div ng-click='openExternal($event)' data-external='http://externallink.com'> External Link 2 </div>
<div ng-click='openExternal($event)' data-external='http://externallink.com'> External Link 3 </div>
</div>
} else {
<div id="my-links">
<a href='http://externallink.com'> External Link 1 </a>
<a href='http://externallink.com'> External Link 2 </a>
<a href='http://externallink.com'> External Link 3 </a>
</div>
}
And have a generic openExternal method:
$scope.openExternal = function($event){
if ($event.currentTarget && $event.currentTarget.attributes['data-external'])
window.open($event.currentTarget.attributes['data-external'], '_blank', 'location=yes');
}
If you can't change the server side
Parse the response and replace the links with ng-clicks:
$http.get('givemelinks').success(function(htmlData){
htmlData = htmlData.replace(/href='(.*)'/,'ng-click="openExternal($event)" data-external="$1"').replace(/<a/,"<div").replace(/a>/,"div>")
$scope.myContent = htmlData;
})
And use the same openExternal method as above.
I'm replacing the anchors with divs to prevent changing the app routes. That might not be necessary in every app.
To make this even better you should bundle it in a open-external directive so you can use it in multiple controllers and keep them cleaner.
Because the HTML is already rendered when it comes to Angular, and the inAppBrowser plugin only works if called by explicit Javascript, there is nothing you can do that doesn't involve manually changing the HTML or using plain javascript.
Changing the HTML is just a Bad Idea®, especially if you try to do it by using regex matching.
That leaves javascript:
Restangular.all('stories').getList().then(function(stories){
$scope.stories = stories;
updateLinks();
});
function updateLinks(){
//use $timeout wait for items to be rendered before looking for links
$timeout(function(){
var $links = document.querySelectorAll(".stories .story a");
for(var i =0; i < $links.length; i++) {
var $link = $links[i];
var href = $link.href;
console.log("Hijacking link to ", href);
$link.onclick = function(e){
e.preventDefault();
var url = e.currentTarget.getAttribute("href");
window.cordova.inAppBrowser.open(url, "_system");
}
}
});
}
Install next plugin:
cordova plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-inappbrowser.git
Now, you can use _system, _blank or _self for destination:
window.open(url, '_blank');
More info: https://www.thepolyglotdeveloper.com/2014/07/launch-external-urls-ionicframework/
You can override the default link tag functionality as seen here:
https://www.thepolyglotdeveloper.com/2014/12/open-dynamic-links-using-cordova-inappbrowser/
Best,
<ul>
<li> <a href="#" ng-click='openlink($event)' data-link='https://www.link1.com'> Link 1 </a></li>
<li> <a href="#" ng-click='openlink($event)' data-link='https://www.link2.com'> Link2 </a></li>
<li> <a href="#" ng-click='openlink($event)' data-link='https://www.link3.com'> Link 3 </a></li>
</ul>
In controller -
angular.module('app', [])
.controller('LinkCtrl', function($scope) {
$scope.openlink = function($event)
{
if ($event.currentTarget && $event.currentTarget.attributes['data-link'])
{
window.open($event.currentTarget.attributes['data-link'], '_system', 'location=yes');
}
}
})

Angularjs get data filtered in ng-repeat from controller

I have tried everything but I can't get it.
What i need is to access data filtered in ng-repeat from controller.
Data in ng-repeat is taken from a $http call.
$http.get('http://localhost:/test/test.php').success(function(data) {
$scope.registros = data;
});
and this is the view
<div ng-repeat="registro in (filteredregistros = (registros| filter:userSearch | filter:datefilter | filter:greaterThan('ID', greatersearch) | orderBy:'-ID'))">
{{registro.ID}}{{registro.date}}
<div class="rowboxdata ng-animate">
<div class=""><div class="name-business">{{registro.Name}}</div></div>
<div class=""><div class="name-business">{{registro.Phone}}</div></div>
<div class=""><div class="name-business">{{registro.Email}}</div></div>
<div class=""><div class="name-business">{{registro.Name}}</div></div>
<div class=""><div class="name-business">{{registro.City}}</div></div>
<div class=""><div class="name-business">{{registro.Service}}</div></div>
</div>
</div>
I have tried access data filtered from my controller with $scope.filteredregistros as i look in other post, but it didn't work to me.
What I'm trying to do, is to get data filtered and then, send it through ajax to php.
Would be nice some help thanks.
EDITED
Finally, I have found what I needed with this example;
In the View:
<input type="button" ng-click="results(filteredregistros)" />
In the Controller:
$scope.results = function (filteredregistros) {
console.log(filteredregistros);
// your ajax code
};
And if you want to get it only in the view, then do it as Per Hornshøj-Schierbeck says:
filteredregistros: {{filteredregistros | json}}
Thanks for the help Per Hornshøj-Schierbeck
Try adding
filteredregistros: {{filteredregistros | json}}
in your HTML somewhere. It should be clear if it contains data or not. Piping it through json will display the data in json format, which is nice for debugging the value inside.
The reason why you might not see if, when you do console.log is, that depending on when you console.log it, your http request might or might not have run and the data might not be filtered yet.

AngularJS ng-src path to image

Regarding the use of ng-src in order to display an image, this code works during runtime - but not on the initial page load:
<div class="imageHolder" ng-click="openWidgetSettings(widget);" ng-show="widget.showInitImage">
<img ng-src="../../Images/{{widget.initImage}}" />
<div class="caption">Click to configure</div>
</div>
on my initial page load I get the error:
GET http://localhost:33218/Images/ 403 (Forbidden)
Yet during runtime, when I drag and drop an image onto my dashboard, the front end doesn't complain anymore.
I do realize that the dashboard framework I'm using is dynamically adding a div onto my page, and then rendering the image; however, why does it NOT complain at this time ?
In other words, I'm trying to avoid using the full path like this:
<img ng-src="http://localhost:33218/Images/{{widget.initImage}}" />
**** UPDATE ****
This bit of code works, and I did not need to specify ".../../" relative path.
<div class="imageHolder" ng-click="openWidgetSettings(widget);" ng-hide="widget.gadgetConfigured">
<img ng-src="Images/{{widget.initImage}}" />
<div class="caption">Click to configure</div>
</div>
In addition, my {{widget.initImage}} was coming back empty upon reload - an application bug !
Change you code to following.
You need to check widget.initImage is initialized or not. Before passing it to ng-src .
Use ng-if on widget.initImage
<div class="imageHolder" ng-click="openWidgetSettings(widget);" ng-show="widget.showInitImage">
<img ng-src="../../Images/{{widget.initImage}}" ng-if="widget.initImage" />
<div class="caption">Click to configure</div>
</div>
I'd suggest you to use ng-init directive like this...
<div class="imageHolder" ng-click="openWidgetSettings(widget);" ng-show="widget.showInitImage" ng-init="getImgUrl()">
<img ng-src="{{myImgUrl}}" />
<div class="caption">Click to configure</div>
</div>
In your controller,
$scope.getImgUrl=function()
{
$scope.myImgUrl= //get your img url whatever it is...
// You can also set widget.showInitImage variable here as well...
}

Resources