How do i handle multiple http requests with $q all angular.js - javascript

I am trying to wrapp my head around $q angular library. In my routeprovider i would like to get all my data from the server and store it in localStorage. But for some reason the resolve dont seem to wait for all http request to finish before routing to selectMedia. From my understanding reading the angular docs this should work but it doesn´t. Have i totally missunderstood the concept or am i my thinking right?
$routeProvider.
when('/', {
redirectTo : '/selectMedia',
resolve: {
data: function ($q, backendApi, localStorage, network, route, loginService){
var prices = function () {
var defer = $q.defer();
backendApi.prices.get(function (data) {
localStorage.setItem("videoPrice", data.VideoPrice);
localStorage.setItem("imagePrice", data.ImagePrice);
localStorage.setItem("prices", data.SliderPrices);
localStorage.setItem("priceSuffix", data.PriceSuffix);
defer.resolve();
}, defer.resolve);
return defer.promise;
};
var validFormats = function () {
var defer = $q.defer();
backendApi.validFormats.get(function (formats) {
localStorage.setItem("validFormats", formats);
defer.resolve();
}, defer.resolve);
return defer.promise;
};
var videoFormats = function () {
var defer = $q.defer();
backendApi.videoFormats.get(function (videoFormats) {
localStorage.setItem("videoFormats", videoFormats);
defer.resolve();
}, defer.resolve);
return defer.promise;
};
var categories = function () {
var defer = $q.defer();
backendApi.categories.get(function (data){
localStorage.setItem("categories", data.Categories);
defer.resolve();
},defer.resolve);
return defer.promise;
};
var renewToken = function () {
var defer = $q.defer();
loginService.renewToken(defer.resolve);
return defer.promise;
};
if(network.isOnline()){
var promises = [renewToken(), categories(), videoFormats(), validFormats(), prices()];
return $q.all(promises);
}
else if(!network.isOnline() && localStorage.length === 0){
route('/error');
}
}
}
});
}]);

I don't see the controller for the route being specified in the route definition. I guess you have set it via ngController at the view level. The resolve block is skipped when you don't have a controller specified in the definition.
4/23 UPDATE
The definition of the resolve property extracted from the official docs:
An optional map of dependencies which should be injected into the
controller. If any of these dependencies are promises, they will be
resolved and converted to a value before the controller is
instantiated and the $routeChangeSuccess event is fired.
According to the definition, the design purpose of the resolve property is to have a way to inject dependencies into the controller associated with a route.

Related

Angular Queue Action From Directive To Service

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...
I think, that your hunch is good. You need a service to handle requests for your images. Remember that browsers are limited to send multiple concurrent requests to single server/proxy (Max parallel http connections in a browser?)
In theory you could create a promise and expose it thru two way bindings or even drive loading with an attribute. Something like:
HTML:
<img mydirective="booleanValue" />
In mydirective:
$scope.$watch('mydirective', function (newVal, oldVal) {
if (newVal === true) {
//perform the load
}
});
But I think, that a service would be better. You could decide what and when to load, from that single point. It wouldn't be limited to UI component, you could load bigger images not only from directives context.
So in the service the essential code would be:
module.factory('$mediaStack', ['$q', function($q) { //first of all you don't need all of these dependencies, keep the list short
var imgDict = {}; //Image dictionary where we're going to keep promises and loaded images
var loadingNow = 0;
function loadImage(img) {
//Send request and return promise
var deferred = $q.defer();
var bigImage = new Image();
bigImage.onload = function() {
deferred.resolve(bigImage);
}
bigImage.src = source.replace("small.", ".");
return deferred.promise;
}
var $mediaStack = function(img) {
var deferred = $q.defer(); //the main deferred task
if (loadingNow > 4) { //if we're loading 5 images or more, defer the task untill one of the Images is loaded
var promises = []; //an array of promises, list of images which are loading now
Object.keys(imgDict).forEach(function(key) {
if (!(imgDict[key] instanceof Element)) { //Check if the item from dictionary is a promise or loaded image
promises.push(imgDict[key]); //if it's a promise, add it to the list
}
});
$q.race(promises).then(function() { //use the list, to create a race: when one of the promises resolves (image is loaded) we can fire our function again (we probably have less than 5 Images loading at the same time)
$mediaStack(img).then(function(data) { //call the function again
deferred.resolve(data); //as we return a promise form that function we have to resolve main deferred object
});
});
}
if (!(img in imgDict)) { //when the image is not loaded yet
loadingNow++; //increase the number of images being loaded
imgDict[img] = loadImage(img).then(function(imgDOM) { //and load the image
imgDict[img] = imgDOM;
deferred.resolve(imgDOM); //once it's loaded resolve the main deferred task
loadingNow--;
});
} else {
deferred.resolve(imgDict[img]);
}
return deferred.promise; //promise to the main deferred task
}
return $mediaStack;
}]);
The general idea behind promises and deferred tasks:
Deferred object is a deferred task, which has a Promise parameter - a entry point of a callback, which is going to be invoked once the deferred task is completed.
Check $q docs for more: angular $q
And how to write a service: https://docs.angularjs.org/guide/services
What is Promise? The general idea and native API
Hope it helps.
Marking Oskar as accepted because it basically allowed me to figure out a solution.
Here is what I actually implemented, however:
.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){
if(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;
if(theImages.length>0)
{
$mediaStack.load();
};
});
}
}
$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;
}])

how to make a function reusable between two or more controllers in angularjs

I have a question as I am new to angularjs. I have searched alot but unable to understand the concept. I have a basic concept that there should be one controller for one view but we can have multiple controllers for a single view. I have below two controllers in a single view and there is one function let say for example addComma function which I have to use in both controllers but I want to write it one time to make it reusable between all controllers let say 5,6 controllers on the same view. So basically the question is how to make a function in a controller global between all controllers of same view or other views so that I can use it anywhere in my application. Appologies if it is a dumb question I am having a hard time understanding the concepts of angularjs.
app.controller("GetChargesController", function ($scope, GetService, $rootScope) {
$scope.Title = "Charges Details List";
$rootScope.loading = true;
// Calling Serivce Method here
$scope.GetChargesDetails = GetService.GetAll("CommonApi", "GetChargesDetails").then(function (d) {
$scope.ChargesDetails = d;
$rootScope.loading = false;
});
// add comman function goes here
$scope.addComma = function(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
});
app.controller("GetPaymentsController", function ($scope, GetService, $rootScope) {
$scope.Title = "Payments Details List";
$rootScope.loading = true;
// Calling Serivce Method here
$scope.GetPaymentsDetails = GetService.GetAll("CommonApi", "GetPaymentsDetails").then(function (d) {
$scope.PaymentsDetails = d;
$rootScope.loading = false;
});
// add comman function goes here
$scope.addComma = function (x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
});
Below is the generic service I've written to get any kind of data from database(using asp.net web api). As I've read that angular services can hold data and we don't need to call the database again and again when we are moving back and forth on links let's say for example I have these 3 links on a page. Home Link, PaymentCharges Link, Orders Link. so Home view will open by default. And when I click on PaymentCharges Link the call will go get data from database and render its view but when I click back on Home Link there should be no call going to database to get the data for the home page or when I click forward again on PaymentCharges Link the second time there should be no call going to database but what I see on firebug console option it is calling the function and going to database to get the data.I think we need to use cache to hold the data to not send the call to database.
app.factory("GetService", function ($http) {
var thisService = {};
// get all data from database
thisService.GetAll = function (controllername, methodName) {
var promise = $http({
method: 'GET',
url: '/api/'+controllername + '/' + methodName
})
.then(function (response) {
return response.data;
},
function (response) {
return response.data;
});
return promise;
};
});
Create an utils service and add the addComma function there. Inject the utils service and reuse the addComma in the controllers
app.factory('utils', function() {
return {
addComma: function() {}
}
});
app.controller("GetChargesController", function ($scope, utils) {
$scope.addComma = utils.addComma;
});
app.controller("GetPaymentsController", function ($scope, utils) {
$scope.addComma = utils.addComma;
});
Several ways to manage the service issue:
Re-use the promises by storing them in the service:
// create an object to store promises
var promises = {};
thisService.GetAll = function(controllername, methodName) {
promises[controllername] = promises[controllername] || {};
// if the particular promise doesn't already exist create it as a property of above object
if (!promises[controllername][methodName]) {
promises[controllername][methodName] = $http({...}).then(...;
}
// now return the specific stored promise
return promises[controllername][methodName];
};
or store the data and use $q to return a different promise that resolves with the stored data when it already exists. Make sure to inject $q for this approach
var data = {};
thisService.GetAll = function(controllername, methodName) {
data[controllername] = data[controllername] || {};
if (data[controllername][methodName]) {
// data exists for this controller and method, no need for new request
return $q.resolve(data[controllername][methodName]);
} else {
return $http({...}).then(function(response) {
// store the data for next use
var newData = response.data;
data[controllername][methodName] = newData;
return newData;
});
}
};
Why don't you just store the data in the service?
You could do:
.then(function (resp) {
thisService.controllerName.data = resp.data
}
Then you can reference the variable in your code using GetService.controllerName.data
Instead I would suggest you to use something like this which might make it little more generic. Check the below code
appResources-Factory will contain all the resources that are needed to be exposed.
indexServices-Factory will contain all your services corresponding to your index controller.
(function () {
use strict';
var commonServices = angular.module('commonServices', ['ngResource']);
commonServices.factory('appResources', ['$resource',
function ($resource) {
var indexResource=$resource('/api/index/:id');
return {
indexResource:indexResource
}
}]);
commonServices.factory('indexService',['appResources',
function (appResources){
var getAllIndexes =function(sucesscallback){
appResources.indexResource.query({}).$promise.then(
//success
function( value ){/*success events and Data is present in the 'value'*/
sucesscallback(value);
},
//error
function( error ){/*failure events*/}
)
}
}
]);
});
Controller Method calling the service and getting the value in $scope variables like
(function () {
'use strict';
angular.module('saniehhaApp')
.controller('indexController', indexController);
indexController.$inject = ['$location', 'indexService'];
function indexController($location, index,indexService) {
/* jshint validthis:true */
indexService.getAllIndexes(function(sucesscallback){
$scope.value=sucesscallback;
})
//$locaton.qwets=....
}
})();

manage factory JSON $http data with 2 controllers

I'm trying to get a factory JSON response, save it in a variable, in order to be ready to be called from 2 different controllers.
Here bellow I paste the code I'm using:
storyFactory.js
var story = angular.module('story.services', []);
story.factory('storyAudio', [ '$http', function ($http) {
var json = {};
function getJSON(story_id, callback) {
$http({
url: 'https://api.domain.co/get/' + story_id,
method: "GET"
}).success(function (data) {
json = data;
callback(data);
});
};
return {
getSubaudios: function(story_id, callback) {
getJSON(story_id, function(result) {
callback(result);
});
},
getTopbar: function(callback) {
callback(json);
}
};
}]);
StoryCtrl.js
var storyCtrl = angular.module('story', ['story.services']);
storyCtrl.controller('storyCtrl', [ 'CONFIG', '$stateParams', 'storyAudio', function(CONFIG, $stateParams, storyAudio) {
var data = this;
data.story = {};
storyAudio.getSubvideos($stateParams.story_id, function(response) {
data.story = response;
});
}]);
TopbarCtrl.js
var topbarCtrl = angular.module('topbar', ['story.services']);
topbarCtrl.controller('topbarCtrl', [ 'CONFIG', '$stateParams', 'storyAudio', function(CONFIG, $stateParams, storyAudio) {
var data2 = this;
data2.story = {};
storyAudio.getTopbar(function(response) {
data2.story = response;
});
}]);
The problem is in my TopbarCtrl response I'm receiving an empty data2.story when I call it in the HTML.
The reason is because it doesn't have a callback of the $http response, so it prints the var json with the actual status, that is an empty object.
How could I load the second controller when the variable has content?
Thanks in advice.
I think the best you can do in this case is load the data via getSubaudios and provide a reference to the data for other controllers to use. Something like this...
story.factory('storyAudio', function($http) {
var factory = {
story: {}
};
factory.getSubaudios = function(story_id) {
return $http.get('https://api.domain.co/get/' + story_id).then(function(response) {
return angular.extend(factory.story, response.data);
});
};
return factory;
})
Using angular.extend() instead of directly assigning a value to the factory's story property maintains any references that may be established before the data is loaded.
Then you can load the data via
storyCtrl.controller('storyCtrl', function(storyAudio) {
var data = this;
storyAudio.getSubaudios($stateParams.story_id).then(function(story) {
data.story = story;
});
})
and directly reference the story data by reference in your controller
topbarCtrl.controller('topbarCtrl', function(storyAudio) {
this.story = storyAudio.story;
})
I think I'm understanding correctly, but let me know if not.
There are two issues I'm seeing. The first is that there is a typo in your StoryCtrl.js file. You are calling "storyAudio.getSubvideos" but the function is called "getSubaudios" in your factory.
Even with that typo fixed, the issue could still technically happen. It all really depends on how quickly the promise returns from the first call. Unfortunately, promises are asynchronous, so there is no guarantee that the "json" variable will get set before the second controller tries to get it.
In order to resolve this, you need to ensure that the first call is finished before trying to access the "json" variable you have on the service. There are probably a few different ways to do this, but one that comes to mind is to actually return and store the promise in the service like so...
var dataPromise;
function getSubaudios(story_id){
if(!dataPromise){
dataPromise = $http({
url: 'https://api.domain.co/get/' + story_id,
method: "GET"
});
}
return dataPromise;
}
return {
getSubaudios: getSubAudios
};
Then in your controllers, you can just call the service and use .then to get the data out of the promise when it returns...
storyAudio.getSubaudios($stateParams.story_id).then(function(response){
data.story = response; //or data2.story = response;
});
Here is a plunkr example. I've used the $q library to simulate a promise being returned from an $http request, but it should illustrate the idea.
Similar to Phil's answer. (Angular extend, or angular copy keeps the references the same in both controllers. If you don't want to put watchers in both controllers to keep track if the value changes.) Several methods here:
Share data between AngularJS controllers.
You could also bind the object you are returning directly to the update-function. That way the references stay intact.
storyServices.factory('storyAudio', ['$http', function($http) {
return {
data: { json: '' },
getSubaudios: function(story_id) {
$http.get('http://jsonplaceholder.typicode.com/posts/' + story_id)
.then(function(response) {
this.data.json = response.data.body;
}.bind(this));
}
};
}]);
var storyCtrl = angular.module('story').controller('storyCtrl', ['$scope', 'storyAudio', function($scope, storyAudio) {
$scope.data = storyAudio.data;
storyAudio.getSubaudios(2);
}]);
var topbarCtrl = angular.module('story').controller('topbarCtrl', ['$scope', 'storyAudio', function($scope, storyAudio) {
$scope.data2 = storyAudio.data;
}]);
Plunk here: http://plnkr.co/edit/auTd6bmPBRCVwI3IwKQJ?p=preview
I added some scopes to show what happens.
Sidenote:
I think it's straight out dishonest to name your non-controller "storyCtrl" and then assign it a controller of its own:
var storyCtrl = angular.module(...); // Nooo, this is not a controller.
storyCtrl.controller(...); // This is a controller! Aaaah!
Another sidenote:
.success() is the old way of doing things. Change to .then(successCallback) today! I dare to say it's the standard convention for promises.
https://docs.angularjs.org/api/ng/service/$http#deprecation-notice

Having problems unit testing a promise call in angularjs controller

SupportedLanguagesServices get method returns a promise which is resolved in the controller as follows:
angular.module('App').controller('MyController', ['SupportedLanguagesService',
function(SupportedLanguagesService) {
var self = this;
self.supportedLanguages = [];
SupportedLanguagesService.get().success(
function(response) {
self.supportedLanguages = response;
});
}]);
And here is the test I wrote, but it is not working:
describe('Controller: MyController', function() {
beforeEach(module('App'));
var rootScope, controller;
beforeEach(inject(function($controller, $rootScope, SupportedLanguagesService, $q) {
var deferred = $q.defer();
deferred.resolve([{name:"English", code:"en"},{name:"Portugues", code:"pt_BR"]);
spyOn(SupportedLanguagesService, 'get').andReturn(deferred.promise);
rootScope = $rootScope;
controller = $controller;
}));
it('should get SupportedLanguages', function() {
rootScope.$digest();
var ctrl = controller('MyController');
expect(ctrl.supportedLanguages.length).toEqual(2);
});
});
It throws an exception on the statement: var ctrl = controller('MyController');
Thank you for your assistance.
Intead of success (which is an $http callback), you can change to a then, which is available on all promises. This will allow you to easily mock the promise (and not concern yourself with $httpBackend:
SupportedLanguagesService.get().then(
function(response) {
self.supportedLanguages = response.data;
});
Then, you need to use the controller's constructor and then call a $digest. So, switching to this should get you there:
it('should get SupportedLanguages', function() {
var ctrl = controller('MyController');
rootScope.$digest();
expect(ctrl.supportedLanguages.length).toEqual(2);
});
You can also simplify the setup code using $q.when:
var response = [{name:"English", code:"en"},{name:"Portugues", code:"pt_BR"}];
spyOn(SupportedLanguagesService, 'get').andReturn($q.when(response));

Is it possible to pass GET response data back to same factory?

Question:
From any controller, how can I call the getPages function, return the data back to the controller and replace the empty Page.details.refobject with the GET response data?
is it possible for this all to happen within the factory regardless of which controller calls the function?
app.factory('Pages', function($http, ENV){
var Pages = {};
Pages.details =
{
pages:
{
length: 0,
offsets: []
},
ref:
{
//data goes here on success
},
getPages: function($scope) {
return $http.get(ENV.apiEndpoint + '/' + $scope.storeSlug + '/pages.json?code=' + $scope.promoCode)
.success(function(data){
// I want this Pages.details.ref to be replaced on success of getPages
Pages.details.ref = data;
$scope.handlePagesSuccess(data);
return data;
})
.error(function(data, status){
// console.log('error:' + status);
});
}
}
return Pages;
});
Controllers:
this controller calls the init request
app.controller('RandomCtrl', function($scope, Pages){
var handleSuccess = function (data) {
$scope.data = data;
}
Pages.details.getPages($scope).success(handleSuccess);
})
Controller #2:
this controller just consumes a temp version of the request no relationship between the RandomCtrl. e.g this controller is typically a directive level controller where the theres no bubbling between a parent ctrl
app.controller('OtherCtrl', function($scope, Pages){
$scope.tempPage = Pages.details.ref;
})
it shouldnt matter where getPages is called from. I want ref to be replaced everytime getPages is called.
It seems like you are trying to manage state inside your factory, which probably is not a good idea. Also it is not a good idea to pass around $scope in factories. They should be limited to its own controller. You could instead cache the promise for the previous call made and based on a flag you could either return the cached promise or make the actual service call.
app.factory('Pages', function($http, ENV, $q){
var Pages = {};
var cachedPromise = {};
Pages.details =
{
pages:
{
length: 0,
offsets: []
},
getPages: function(request) {
//Get a request key to make sure you are returning right promise incase multiple product calls are made at the same time.
var reqKey = request.storeSlug + request.promoCode;
//if a call has already been made and there is a promise return it
if(cachedPromise[reqKey]) return cachedPromise[reqKey];
//Store the promise in the cache for lastCall retrieval
return cachedPromise[reqKey] = $http.get(ENV.apiEndpoint + '/' + request.storeSlug + '/pages.json?code=' + request.promoCode)
.then(function(result){
return result.data; //You can alter data and send as well
}, function(data, status){
return $q.reject('some error'); //or return some data
}).finally(function(){
//remove the cache from the map, once promise is resolved.
delete cachedPromise[reqKey];
});
}
}
return Pages;
});
In your first controller do:-
app.controller('RandomCtrl', function($scope, Pages){
//Build your request.
Pages.details.getPages(request).then(function (data) {
$scope.data = data;
});
});
In your second controller just do the same:-
app.controller('OtherCtrl', function($scope, Pages){
//Pass the flag to get the cached data.
Pages.details.getPages(request).then(function (data) {
$scope.tempPage = data;
});
});

Categories

Resources