Writing an angular directive with asynchronous data - angularjs

I'm writing a small app that uses the Instagram API. I'm trying to write a directive to display a list of the logged in users images. I have a back end api that calls the Instagram API and returns the results.
.directive('userPhotos', function($http)
{
return {
restrict: 'E',
template: '<ul class="user-photos">'
+'<li ng-repeat="image in images">'
+'<img src="{{image.images.low_resolution.url}}">'
+'</li>'
+'</ul>',
replace: true,
controller: function($scope, $element, $attrs){
$scope.images;
$scope.serviceURL = "/api/photos/"+$attrs.photosPerLoad;
$scope.loadPhotos = function()
{
$http.get(this.serviceURL)
.success(function(response){
$scope.images = response.data;
})
.error(function(response){
// show error
});
}
},
link: function($scope, element, attribute){
$scope.loadPhotos();
}
}
})
This works in that it displays the images as desired once the result of the API call is complete. However, as I have defined a src attribute for the img tag in the template during the initial phase of the Angular process this is called and I get the following.
GET http://myapp.dev/%7B%7Bimage.images.low_resolution.url%7D%7D 500 (Internal Server Error)
I'm still a bit vague on the process Angular goes through as it's putting a directive together but I think I understand that initially, for the ng-repeat region there is a copy of the contents of this region made to be filled once the images binding is populated.
Anyhow, can anyone advise how I could get around this?

So to clarify... this was solved by changing the template part of the directive to...
template: '<ul class="user-photos">'
+'<li ng-repeat="image in images">'
+'<img ng-src="{{image.images.low_resolution.url}}">'
+'</li>'
+'</ul>',
Thanks! #madhead.

Related

How do I load an Image from $templateCache using Angular 1

I have a plnkr and the story is long but it comes down to I need to be able to use an image from the templateCache. I try to do this using ng-src like this...
$templateCache.put('img/help/copy_icon', '://www.drupal.org/files/issues/sample_7.png')
...
template: '<img ng-src="{{url}}">'
However this doesn't work and I get...
copy_icon':1 GET https://run.plnkr.co/ZyTH7LFhPLhdQpCI/'img/help/copy_icon' 400 ()
So it isn't trying to get it from templateCache. Is there a way to do this?
Update
I tried ...
$http.get('://www.drupal.org/files/issues/sample_7.png').success(function (t) {
$templateCache.put('img/help/copy_icon', t);
});
But that didn't work either
I do not know exactly what you try to achieve by using $templateCache, which in your case just saves image URL to the cache, but I modified your code a bit, so it displays an image, I added controller:
app.run(function($templateCache){
$templateCache.put('img/help/copy_icon', 'http://www.drupal.org/files/issues/sample_7.png')
})
app.directive('ngImg', function(){
return {
rectrict: "E",
scope: {
url: "#url"
},
template: '<img ng-src="{{ imgUrl }}">',
controller: function ($scope, $templateCache) {
$scope.imgUrl = $templateCache.get($scope.url);
}
}
})
plunker: https://plnkr.co/edit/q5a9ptq8rd1G4uGkkNMe?p=preview
BTW, using ng prefix for custom directives names is not recommended and should be reserved for angular core directives/components

Sharing data between a directive and a pagination controller in Angular during an API request

I'm sure similar questions have been asked many times on Stack Overflow, but I am still confused.
I have an Angular app that shows a list of articles. The list is long, so it needs to be paginated. For pagination, I'm using UI Bootstrap.
So the app is currently organized like this:
there is a directive ("home directive") that draws the list of articles
there is a UI Bootrstrap directive for pagination with its own controller
or, schematically:
Now, when I change the page in the paginator, I need to send the request to the the server, get a new batch of articles and show them in the home directive:
So the paginator's controller is sending the request, but the home directive needs to be aware of the new response and to use it for drawing a new list of articles.
How is this done in Angular? I've read about services/factories, but am not entirely clear how the home directive becomes aware of the new response from the server. Will I need watchers in the home directory, or is this done differently? Could you please explain?
UPDATE:
OK, here is my (failing) attempt to write a service:
home-directive.js
angular.module('myApp')
.directive("homeDirective", [ 'ArticleService', function(ArticleService){
return {
restrict: 'E',
templateUrl: "home.html",
scope: { },
link: function(scope, element, attrs){
ArticleService.getArticles();
scope.$watch('ArticleService.articles', function () {
console.log(ArticleService.articles);
scope.articles = ArticleService.articles;
});
}
}]);
article-service.js
angular.module('myApp')
.service('ArticleService', ['$http', function($http){
var that = this;
this.getArticles = function(){
$http.get('/path/to/api').success(function(data) {
console.log('working');
that.articles = data;
console.log(that.articles);
});
};
}]);
The call to ArticleService.getArticles() from the directive starts the getArticles() function, as I can see from the logger in the console. The server sends its response. that.articles changes. However, the directive fails to register the change of ArticleService.articles and doesn't get the data.
UPDATE 2
OK, if in the directive, before setting a watcher, I add this line in the directive:
scope.ArticleService = ArticleService;
then it will work.

Using scope in directive and service

I am trying to create a developer console in my Angular app and I have the following problem.
On every page of my application I want my developer console to be available (like a floating thing in a corner of the screen). I have HTML template and I am switching content with ui-view. My controllers can include consoleService and then call something like myConsole.log(...). This function should add message to some array and then display it on web. For my console I have directive with HTML template, where I want to use ng-repeat to display all messages.
And now my problem. How should I design this? I was thinking like this:
First solution
Well, I can create global JS variable, store all messages there and then use <script> and write em out in my directive template.
WRONG! Not respecting Angular-way of doing things
Second solution
Ok, I`ll use $scope and put my messages there, and then I can use that magical Angular data bind to just let it work on its own. Ok, but here I have a problem, how can I inject $scope into my service?
WRONG! Injecting $scope into service is nonsense, because it would not make any sense, and again, it kinda oppose the Angular-way.
Third solution
Fine, lets store my messages on console service, since all services in Angular are singletons it should work pretty fine, but... there is no $scope inside a service, so I need to return this array of messages to a controller and then assign it to $scope in controller.
??? What do you think? My problem with this solution is that in every controller I need to assign message array to $scope and I just dont want to do that, since I need this console everywhere, on every page.
I hope that my explanation of what I want to do is clear, so I am just hoping for a hint, or advice on how to design it in Angular.
Use the third solution. Your service will have an array with all the messages and each controller can use one of it's functions (service.log(...)) which will basically just add one message to the service list.
Then on your directive, you just assign the getMessages to the directive scope:
var app = angular.module('plunker', []);
app.service('log', function(){
var messages = [];
return {
getMessages: function() { return messages; },
logMessage: function(msg) { messages.push(msg); }
}
})
app.controller('MainCtrl', function($scope, log) {
$scope.msg = '';
$scope.addMessage = function() {
log.logMessage($scope.msg);
};
});
app.directive('console', function(log) {
return {
restrict: "E",
scope: {},
replace: true,
template: "<div><div ng-repeat='msg in messages'>{{msg}}</div></div>",
link: function(scope, el, attr) {
scope.messages = log.getMessages();
}
};
});
plunker: http://plnkr.co/edit/Q2g3jBfrlgGQROsTz4Nn?p=preview

directive scope not binding with $resource

I'm experiencing a very strange issue and cannot see what I'mn doing wrong...
I have a pretty simple directive:
var widgets = angular.module('widgets', []);
widgets.directive('wdgtImageThumbnail', function ($compile) {
return {
restrict: 'E',
replace: true,
templateUrl: '/utilities/widgets/wdgtImageThumbnailPrtl.html',
scope: {
item: '=',
imageSettings: '=',
gotoItem: '&',
showName: '='
}, controller: function ($scope) {
// get basePath and placeholderImage from plsConfig via controller
// and configure item.image property as required
var imageBasePath = $scope.imageSettings.imageBasePath;
var placeholderImage = $scope.imageSettings.placeholderImage;
$scope.thumbnailImage = imageBasePath + ($scope.item.image || placeholderImage);
}
};
});
Here is the markup partial for the directive:
<div data-ng-click="gotoItem()">
<div title="Click to view {{item.name}}'s details">
<h4 class="logo-label" data-ng-show="showName">{{item.name}}</h4>
<img data-ng-src="{{thumbnailImage}}" class="img-thumbnail logo-pic"/>
</div>
</div>
When I'm on the URL and refresh my browser, the directive works as required and displays item.image.
However, if I navigate away from the page to a list of "items" and then navigate back to an "item", {{item.image}} is not binding in the markup.
I have checked using Batarang and, in both cases "item" is on the directive's scope.
So for some reason, in the second scenario, the binding is not occurring. Why would this happen?
EDIT:
I'm having trouble simulating this issue in Plunker, because the issue is related to the way calls are made to our api, which isn't public yet.
However, we have established that the issue we're facing has something to do with Angular resourcesw. For some reason, when we refresh the browser, the directive's controller knows what $scope.item is, but when we navigate away from the page and then back again, $scope.item is undefined. In our test to date, we're using $resource.
This is the controller function calling our datacontext service:
function getSubscriber() {
vm.subscriber = subscriberCtx.getSubscriberById($routeParams.subscriberId);
}
and this is the function in the datacontext:
function getSubscriberById(id) {
return $resource('/api/v1/Subscribers/:id').
get({id:id});
}
vm.subscriber is the object being passed into the directive as the item attribute.
What i'm finding really strange is that if I log $scope.item form the directive in both the passing and failing cases, the object is logged and in each case the logs are identical.
So, why can the directive not see $scope.item in cases when the browser is not refreshed?
If I pass a simple json object in, the directive works perfectly, but we're struggling to ge3t it working with both $resource and restangular.

Execution flow of a directive in angular js and using its attributes

hello i would like to know the steps of execution of directives and some fundamental details about how the processing happens this would be very helpful for more customization of directives and exploit its features...
i have a sample template with a directive and the attributes needed and few html tags as shown
template1:-
<directive1 s-web-service-path="object.WebServicePath" >
<h1>any html content</h1>
</directive1>
my directive is i.e. it calls a web service to get its content
widgetsModule.directive("directive1", function ($http) {
try {
return {
restrict: "E",
replace: true,
transclude: true,
scope: {
sWebServicePath: "="
},
template: '<div ng-transclude><h1>My content {{result }}</h1> </div> ',
link: function (scope, element, attrs) {
var request = '<request struct>';
$http({ method: "POST", url: scope.sWebServicePath, data: request }).
success(function (data, status) {
scope.result = data;
}).
error(function (data, status) {
alert("Service Call Error");
})
}
}
}
catch (e) {
alert(e);
}
});
What is the difference between attrs and $scope in the link function... in above case
$scope.sWebServicePath gives me the value of object.WebServicePath i.e. something like "http://anypath.asmx"
but
attrs.sWebServicePath give me object.WebServicePath... why is this difference and how is it useful?
I know that putting "ng-transclude" would enable me to have
<h1>any html content</h1>
in the specified div of my template with in the directive but how does the execution happens?
and why is that return is to be written in a directive it returns a link function alright and it can be used for DOM manipulation but any example of when do i use it?
i know these might sound very fundamental but please do throw some light on the execution flow of the directive...or some good source of reference...thank you! and advice/tips on the usage of the parameters of the directive would be very helpful
this link has the best documentation on usage of directives they should have put this link under directive definition ... js came across this page while reading the angular documentation
http://docs.angularjs.org/api/ng.$compile
if you use http request in directive you dont forget for $watch because http calls are asynchronous.
Diference between attr and scope is:
scope - is own scope of directive
attr - are attribs of directive
i think so data passed through attr not have to always same as your own scope, because you can make your new own scope for directive but cant change attr.
may be helpful for you
about-those-directives
egghead.io
onehungrymind.com/angularjs-directives-basics/
2013 - angularjs-directives-using-isolated-scope-with-attributes
many sources
AngularJS-Learning

Resources