I'm trying to get better understanding of how to code in Angular properly and am writing my own custom module to better understand how things are supposed to work.
I have an HTML markup consisting of images and a directive i.e.
<img ng-if="!post.tooBig" mydirective resize="0" ng-src="/img/@{{post.Content}}">
My directive is here:
.directive('mydirective', [
'$animate','$mediaStack',
function($animate,$mediaStack) {
return {
restrict: 'A',
compile: function(tElement, tAttrs) {
return loader;
}
};
function loader(scope, element, attrs) {
var source = attrs.ngSrc;
var tooLoadBig = parseInt(attrs.resize);
if(tooLoadBig){
var bigImage = new Image();
bigImage.src = source.replace("small.",".");
}
}
}])
The idea is this: if the image has small
appended to its filename, I know it is a thumbnail. I want to load it's big version (same file without the appended small) in the background so it is ready for a lightbox.
This works fine as is, but the problem is because I'm doing all the work in the compile
, when I set bigImage.src = source.replace("small.",".");
it fires off right away, and if I have many small
images on the page, it causes the page to slow down because of all the loading that is going on.
I want to therefore use $q
to make it so that it will load one image at a time.
So move
var bigImage = new Image();
bigImage.src = source.replace("small.",".");
Into a promise. Is it best practice to do this in the directive? My understanding is it wouldn't make sense to do so and that I should use a service, but I'm not sure how to do that. I could play around with it more but I was hoping someone with more experience could instruct me as to best-practices for something like this and a code sample of a similar workflow, thank you.
Edits:
Directive:
.directive('mydirective', [
'$animate','$mediaStack',
function($animate,$mediaStack) {
return {
restrict: 'A',
compile: function(tElement, tAttrs) {
return loader;
}
};
function loader(scope, element, attrs) {
var source = attrs.ngSrc;
var tooLoadBig = parseInt(attrs.resize);
if(tooLoadBig){
/*var bigImage = new Image();
bigImage.src = source.replace("small.",".");*/
$mediaStack.load(source);
}
}
}])
My service:
.factory('$mediaStack', [
'$animate', '$timeout', '$document', '$compile', '$rootScope',
'$q',
'$injector',
function($animate , $timeout , $document , $compile , $rootScope ,
$q,
$injector) {
var OPENED_MEDIA_CLASS = 'media-open';
var theImages = [];
$mediaStack = {};
$mediaStack.load = function(source){
theImages.push(source);
};
$mediaStack.loadRequest = function(theImages) {
deferred.resolve(function(){
var bigImage = new Image();
bigImage.src = theImages[0].replace("small.",".");
});
return deferred.promise;
}
/*var promise = asyncGreet('Robin Hood');
promise.then(function(greeting) {
alert('Success: ' + greeting);
}, function(reason) {
alert('Failed: ' + reason);
}, function(update) {
alert('Got notification: ' + update);
});*/
//above from docs
}
return $mediaStack;
}])
This works in that it gets the image urls into an array in the service, so I have all the images in the array, how do I use $q
properly based off that array. I got started but most of the $mediaStack.loadRequest
is based off the $q
documentation, I'm not sure how to use it effectively in this case.
EDIT 2:
My service as is:
.factory('$mediaStack', [
'$animate', '$timeout', '$document', '$compile', '$rootScope',
'$q',
'$injector',
function($animate , $timeout , $document , $compile , $rootScope ,
$q,
$injector) {
var OPENED_MEDIA_CLASS = 'media-open';
var theImages = [];
var theLoadedImages = [];
var thePromises = [];
var loading = false;
$mediaStack = {};
$mediaStack.load = function(source){
theImages.push(source);
var mainDeferred = $q.defer();
if(loading)
{
thePromises.push(mainDeferred.promise);
console.log(thePromises);
$mediaStack.myRaceFn(thePromises).then(function() {
console.log("Fire!");
loading=true;
$mediaStack.loadRequest(theImages[0]).then(function(bigImage){
console.log(bigImage);
theImages.shift();
theLoadedImages.push(bigImage);
loading = false;
mainDeferred.resolve();
});
});
}
if(!loading)
{
loading = true;
$mediaStack.loadRequest(theImages[0]).then(function(bigImage){
console.log(bigImage);
theImages.shift();
theLoadedImages.push(bigImage);
loading = false;
mainDeferred.resolve();
});
}
}
$mediaStack.loadRequest = function(source){
var deferred = $q.defer();
var bigImage = new Image();
bigImage.src = source.replace("small.",".");
bigImage.onload = function() {
deferred.resolve(bigImage);
}
return deferred.promise;
}
//.race implementation in old angular
$mediaStack.myRaceFn = function (promises){
return $q(function(resolve, reject) {
promises.forEach(function(promise) {
console.log(promise);
promise.then(resolve, reject);
});
});
}
//.race implementation in old angular
return $mediaStack;
}])
I'm very close, I see an array of promises but I never get to the point of being able to fire them again, so I have 10 images, and I get an array of 9 promises. How do I fire them?
I expected it would happen around here:
$mediaStack.myRaceFn(thePromises).then(function() {
console.log("Fire!");
loading=true;
$mediaStack.loadRequest(theImages[0]).then(function(bigImage){
But I never get that console.log()
.
Instead I get console messages in $mediaStack.myRaceFn()
showing me each promise (9 at the end) but they just sit there? I'm missing something.
I think I may be resolving mainDeferred too early...