my error log - sharing its methods across controllers in AngularJS - javascript

This question is about half practical and half conceptual. I've looked at the responses to similar questions, but I'm pretty new to AngularJS, and I'm just not sure the best way (I've seen some varying opinions) to do this (for me, anyway), or really, the actual code I would write to do it, which is why I'm asking the question with the specifics of my own application.
I know there are lots of questions here with similar titles, but I urge you to keep reading.
In short, I have a bunch of controllers, because I have a lot of models I'm pulling together into a single page. When the front end dispatches a request (i.e., user action) to any of my back end controllers, the front end is going to get a response that may look something like this:
{"success":false,"errors":[{"text":"This is an error sent from the PHP controller.","type":"critical"}]}
I want to use AngularJS, however, to create the model and view for my error log (it only has to live on the client side). So in other words, every other controller in the application is going to need to have access to the error log controller in order to add events to the error log.
I guess I'm aware of some of the options, like creating a shared service/factory and broadcasting to the rootscope. I'm also wondering if it makes sense at all to make every other controller a child of the controller that handles errors, alerts, etc., though instinctively, that feels wrong to me.
What's the best way to do it (keeping in mind that the same controller that handles errors might also handle things like alerts and other global-type housekeeping), and would somebody be kind enough to help me out with the actual code based upon this model I mocked up for what the behavior would look like?
Here it is at JSFiddle: http://jsfiddle.net/Ww8sS/2/
And here's the code. There are probably a number of things in here that wouldn't be the best way to do something, but for now, I'm just concerned with the problem I've described.
JS:
var userInterfaceApp = angular.module('user-interface', ['userInterfaceFilters']);
userInterfaceApp.controller('AnotherController', ['$scope', '$http', function($scope, $http) {
$scope.doSomething = function() {
$http({method: "JSONP", url: "http://uatu.net/test.php?action=do_something&callback=JSON_CALLBACK"}).
success(function(data) {
if(!data.success) {
alert("How do I get the errors in data.errors to my error log?");
}
})
.error(function(data, status, headers, config) {
alert("Failure");
// called asynchronously if an error occurs
// or server returns response with an error status.
});
}
}]);
userInterfaceApp.controller('ConsoleEventController', ['$scope', function($scope) {
$scope.errorLog = [];
$scope.errorSortOrder = "-timestamp";
$scope.addToErrorLog = function(errorArray) {
for (var i = 0; i < errorArray.length; i++) {
$scope.errorLog.push({"text" : errorArray[i].text, "type" : errorArray[i].type, "timestamp" : new Date()});
}
}
//Not a real method--just here for demonstration
$scope.createErrorMessage = function() {
$scope.addToErrorLog([{"text" : "This is a sample error.", "type" : "critical"}]);
}
}]);
var userInterfaceFilters = angular.module("userInterfaceFilters", []);
userInterfaceFilters.filter("logTimestamp", function() {
return function(logDate) {
var hours = logDate.getHours();
var minutes = (logDate.getMinutes() < 10) ? "0" + logDate.getMinutes() : logDate.getMinutes();
var seconds = (logDate.getSeconds() < 10) ? "0" + logDate.getSeconds() : logDate.getSeconds();
return hours + ':' + minutes + ":" + seconds;
};
});
I had to use JSONP to make it work on JSFiddle. I wouldn't do that in my actual program, since it will all be on my server.
HTML:
<div ng-app="user-interface">
<div ng-controller="AnotherController">
<input type="button" value="Do Something" ng-click="doSomething()">
</div>
<div ng-controller="ConsoleEventController">
<p><input type="button" value="Create Error Message" ng-click="createErrorMessage()"></p>
<h1>Error Log</h1>
<ul id="error-log">
<li ng-repeat="error in errorLog | orderBy:errorSortOrder" class="error-{{error.type}}"><{{error.timestamp|logTimestamp}}> {{error.text}}</li>
</ul>
</div>
</div>

Sounds like you already know the best approach is to go down the factory/service route. There's no need to broadcast though - they just create a single instance which you can inject wherever necessary.
Here's a quick example of how you can go about it: http://jsfiddle.net/Ww8sS/3/

For me make more sense using a messaging paradigm (broadcast) rather than use factory that create a variable that after you bind to scope of the controller, because it does that the controller and the service/factory are coupled so you could never change the variable of the service/factory due that your controller would lose the link with the service/factory variable.
For example, if you would like to create a new method in the service/factory that clear the log's array so you create new array rather than empty the current array then that change wouldn't be reflected in that controller's scope because the scope's variable point to the old one log's array; take a look the example: http://jsfiddle.net/Ww8sS/6/
var userInterfaceApp = angular.module('user-interface', ['userInterfaceFilters']);
userInterfaceApp.factory('errorLogs', function () {
return {
logs: [],
addToErrorLog: function (errorArray) {
for (var i = 0; i < errorArray.length; i++) {
this.logs.push({"text": errorArray[i].text, "type": errorArray[i].type, "timestamp": new Date()});
}
},
clearLogs: function () {
this.logs = [];
}
}
});
userInterfaceApp.controller('AnotherController',
['$scope', '$http', 'errorLogs', function ($scope, $http, errorLogs) {
$scope.doSomething = function () {
$http({method: "JSONP", url: "http://uatu.net/test.php?action=do_something&callback=JSON_CALLBACK"}).
success(function (data) {
if (!data.success) {
errorLogs.addToErrorLog(data.errors);
}
})
.error(function (data, status, headers, config) {
alert("Failure");
// called asynchronously if an error occurs
// or server returns response with an error status.
});
}
}]);
userInterfaceApp.controller('ConsoleEventController',
['$scope', 'errorLogs', function ($scope, errorLogs) {
$scope.errorLog = errorLogs.logs;
$scope.errorSortOrder = "-timestamp";
//Not a real method--just here for demonstration
$scope.createErrorMessage = function () {
errorLogs.addToErrorLog([
{"text": "This is a sample error.", "type": "critical"}
]);
}
$scope.clearLogs = function () {
errorLogs.clearLogs();
};
}]);
var userInterfaceFilters = angular.module("userInterfaceFilters", []);
userInterfaceFilters.filter("logTimestamp", function () {
return function (logDate) {
var hours = logDate.getHours();
var minutes = (logDate.getMinutes() < 10) ? "0" + logDate.getMinutes() : logDate.getMinutes();
var seconds = (logDate.getSeconds() < 10) ? "0" + logDate.getSeconds() : logDate.getSeconds();
return hours + ':' + minutes + ":" + seconds;
};
});
If you use the messaging paradigm, it uncouple controllers with the service, moreover that the service is independent and any controller can listen its events; http://jsfiddle.net/Ww8sS/5/
var userInterfaceApp = angular.module('user-interface', ['userInterfaceServices', 'userInterfaceFilters']);
userInterfaceApp.controller('AnotherController', ['$scope', '$http', 'logger', function($scope, $http, logger) {
$scope.doSomething = function() {
$http({method: "JSONP", url: "http://uatu.net/test.php?action=do_something&callback=JSON_CALLBACK"}).
success(function(data) {
if(!data.success) {
logger.addToErrorLog(data.errors);
//alert("How do I get the errors in data.errors to my error log?");
}
})
.error(function(data, status, headers, config) {
alert("Failure");
// called asynchronously if an error occurs
// or server returns response with an error status.
});
}
$scope.clearLog = function() {
logger.clearLog();
}
}]);
userInterfaceApp.controller('ConsoleEventController', ['$scope', function($scope) {
$scope.errorSortOrder = "-timestamp";
$scope.errorLog;
$scope.$on('logger.newErrors', function (evt, errArray) {
$scope.errorLog = errArray;
});
$scope.$on('logger.clearLog', function (evt) {
$scope.errorLog = [];
});
//Not a real method--just here for demonstration
$scope.createErrorMessage = function() {
// $scope.addToErrorLog([{"text" : "This is a sample error.", "type" : "critical"}]);
}
}]);
var userInterfaceFilters = angular.module("userInterfaceFilters", []);
userInterfaceFilters.filter("logTimestamp", function() {
return function(logDate) {
var hours = logDate.getHours();
var minutes = (logDate.getMinutes() < 10) ? "0" + logDate.getMinutes() : logDate.getMinutes();
var seconds = (logDate.getSeconds() < 10) ? "0" + logDate.getSeconds() : logDate.getSeconds();
return hours + ':' + minutes + ":" + seconds;
};
});
var userInterfaceServices = angular.module('userInterfaceServices', []);
userInterfaceServices.service('logger', ['$rootScope', function ($rootScope) {
var errorLog = [];
this.addToErrorLog = function(errorArray) {
for (var i = 0; i < errorArray.length; i++) {
errorLog.push({"text" : errorArray[i].text, "type" : errorArray[i].type, "timestamp" : new Date()});
}
$rootScope.$broadcast('logger.newErrors', errorLog);
};
this.clearLog = function () {
errorLog = [];
$rootScope.$broadcast('logger.clearLog', '');
};
}]);
Anyway, both solutions have some advantages and drawbacks.

Related

AngularJS Service Singleton?

I'm fairly new to angularJS but I've read that services should be singleton. However, it wont' work.
Here is my service:
.factory('TrainingPlan', function($http, SERVER){
var o = {
exercises: [],
planID : 0
};
o.init = function(){
if(o.exercises.length == 0)
return o.getAllExercises();
};
o.getAllExercises = function(){
$http({
method: 'GET',
url: SERVER.url + 'listener.php?request=getAllExercises&planID=' + o.planID
}).then(function(data){
o.exercises = angular.copy(o.exercises.concat(data.data));
console.log("Exercises in Trainingplan Services");
console.log(o.exercises);
return o.exercises;
})
;
};
o.getExercise = function(exerciseID){
for(var i = 0; i < o.exercises.length; i++){
if(o.exercises[i].exerciseID == exerciseID)
{
return o.exercises[i];
}
}
};
return o;
})
And I have two Controllers:
.controller('TrainingDetailCtrl', function($scope, $stateParams, TrainingPlan, $timeout) {
TrainingPlan.init();
$timeout(function(){
$scope.exercises = TrainingPlan.exercises;
$scope.numberOfUnfishedExercises = $scope.exercises.length;
button.innerHTML = "asdf";
}, 250);
})
(I haven't copied the whole controller, but it works so far...)
.controller('TrainingHistoryEditCtrl', function($scope, $stateParams, TrainingPlan, $timeout) {
var exerciseID = $stateParams.exerciseID;
$scope.currentExercise = TrainingPlan.getExercise(exerciseID);
console.log($scope.currentExercise);
})
So actually I go from TrainingDetailCtrl where I have all the exercises of 'TrainingPlan'. However, when I change the sites, TrainingPlan has no exercises anymore when I wont to use them in TrainingHistoryEditCtrl.
That is because your $http issues an async call. Even if you call init, actually when the code runs to the line $timeout(function(){.., the result may not arrive yet.
Please check this demo: JSFiddle. Wait for 10 seconds then the value is not empty.
Solution: return a promise from the factory. Inside the controller use then to pass in callback function.

Waiting for function to complete in Angular JS

I have the following situation: When my app first runs, in the .run function, I issue an http request to get a list of "monitors". Once received, it populates a monitors array in a service. This service is used by many controllers to share data. This service is called ZMDataModel. ZMDataModel offers a function called isMonitorsLoaded(). When this returns 1, I know the monitors array is populated (and that the http call is complete)
Now, I have a page called Monitors, the controller for which is zmApp.MonitorCtrl, shown below. What I need to do, in this MonitorCtrl is to basically, right at the start, do an equivalent of:
while (ZMData.isMonitorsLoaded()!=1);
Now I obviously can't do that because it locks my browser up, and the browser never gets a chance to set isMonitorLoaded to 1 in the first place, so it becomes an endless loop.
I understand I need to put in a timeout somehow, but can't quite follow what I need to do in the controller. My controller code is below:
angular.module('zmApp.controllers').controller('zmApp.MonitorCtrl', function($scope, $http, ZMHttpFactory, ZMDataModel) {
$scope.monitors = [];
console.log("***Waiting for Monitors to load before I proceed");
// I can't do a tight loop waiting for ZMDataModel.isMonitorsLoaded
// so some timeout?
$scope.monitors = ZMDataModel.getMonitors();
console.log("I GOT " + $scope.monitors);
$scope.doRefresh = function() {
console.log("***Pull to Refresh");
$scope.monitors = [];
ZMHttpFactory.getMonitors().then(function(data) {
$scope.monitors = data;
$scope.$broadcast('scroll.refreshComplete');
console.log("I GOT " + $scope.monitors);
});
};
});
You can use $rootScope.$emit('eventName') which works like a broadcast of events for anyone who is subscribe to them whit $rootScope.$on('eventName'):
// In your monitor loaded method:
onload: function(){
//Here you can pass optional information to the listeners
// for Example an array of monitor or an object
$rootScope.$emit('MONITORS_LOADED',{
monitors: getMonitors()
});
}
// In your controller:
angular.module('zmApp.controllers').controller('zmApp.MonitorCtrl', function($rootScope, $scope, $http, ZMHttpFactory, ZMDataModel) {
$scope.monitors = [];
$rootScope.$on('MONITOR_LOADED', function(event, data) {
$scope.monitors = data;
// or
// $scope.monitors = ZMDataModel.getMonitors();
console.log("I GOT " + $scope.monitors);
}
$scope.doRefresh = function() {
//...
});
};
});
Why not using a promise which will resolve when your monitor servers is loaded? You can set up your service as:
angular.module('myApp')
.service ('ZMDataModel', ['$http', function ($http) {
function MyServices () {
var _deferred;
var _isLoading;
var me = this;
this.isLoaded = false;
this.load = function (reload) {
if (!_deferred || (!_isLoading && reload)) {
this.isLoaded = false;
_deferred = $q.defer();
_isLoading = true;
// make your call
$http ({get : 'http://your-site.com'})
.then (
function success (rawData) {
me.isLoaded = true;
// parse your data
me.monitors = rawData;
_deferred.resolve(me);
},
function fail (error) {
_deferred.reject(error);
_deferred = null;
if (onFail) {
me.monitors = [];
}
}
)
.finally (
function () {
_isLoading = false;
}
);
}
return _deferred.promise;
};
}
return MyServices;
}
]);
Now you can use your service everywhere like this:
angular.module('zmApp.controllers').controller('zmApp.MonitorCtrl', ['$scope', 'ZMDataModel', function($scope, ZMDataModel) {
$scope.monitors = [];
console.log("***Waiting for Monitors to load before I proceed");
// I can't do a tight loop waiting for ZMDataModel.isMonitorsLoaded
// so some timeout?
ZMDataModel.load ().then (
function (response) {
$scope.monitors = ZMDataModel.monitors;
}
);
$scope.doRefresh = function() {
console.log("***Pull to Refresh");
$scope.monitors = [];
ZMDataModel.load (true).then (
function (response) {
$scope.monitors = ZMDataModel.monitors;
}
);
};
}]);
It doesn't matter if an other controller loads the service first. As long as you use the load function without the 'refresh' flag set to true, the service won't load again

Ajax with external parameter Angular

I am new to angular and what I am willing to do is replace a piece of code I wrote in the past in jquery to angularjs.
The goal is to take a string from a span element, split it in two and pass the two strings as parameters in a GET request.
I am trying to learn best coding pratices and improving myself so any comments of any kind are always welcome.
Working Code in jquery:
//Get Song and Artists
setInterval(function () {
var data = $('#songPlaying').text();
var arr = data.split('-');
var artist = arr[0];
var songTitle = arr[1];
//Request Lyrics
$.get('lyricsRequester.php', { "song_author": artist, "song_name" : songTitle},
function(returnedData){
console.log(returnedData);
$('#refreshLyrics').html(returnedData);
});
},10000);
Code in Angular
var app = angular.module("myApp", []);
app.factory('lyricService', function($http) {
return {
getLyrics: function($scope) {
//$scope.songArr = $scope.currentSong.split('-'); <-- **undefined currentSong**
//$scope.artist = $scope.songArr[0];
//$scope.songTitle = $scope.songArr[1];
return
$http.get('/lyricsRequester.php', {
params: {
song_author: $scope.artist,
song_name: $scope.songTitle
}
}).then(function(result) {
return result.data;
});
}
}
});
app.controller('lyricsController', function($scope, lyricService, $interval) {
$interval(function(){
lyricService.getLyrics().then(function(lyrics) {
$scope.lyrics = lyrics; <-- **TypeError: Cannot read property 'then' of undefined**
console.log($scope.lyrics);
});
}, 10000);
});
index.html (just a part)
<div class="col-md-4" ng-controller="lyricsController">{{lyrics}}</div>
<div class="col-md-4"><h3><span id="currentSong" ng-model="currentSong"></span></h3><div>
You need to be careful with your return statement when used in conjunction with newlines, in these lines:
return
$http.get('/lyricsRequester.php',
If you don't, JS will automatically add a semicolon after your return, and the function will return undefined.
Move the $http.get statement to the same line as your return statement.
return $http.get('/lyricsRequester.php', ...
Refer to the following docs:
MDN return statement
Automatic Semicolon Insertion
As for your second issue, you $scope is not really something you inject into your services (like $http). Scopes are available for use in controllers.
You need to refactor your code a bit to make things work.
eg. Your getLyrics function can take a song as a parameter. Then in your controller, you call lyricsService.getLyrics(someSong). Scope access and manipulation are only done in your controller.
app.factory('lyricService', function($http) {
return {
getLyrics: function(song) {
var songArr = song.split('-');
var artist = songArr[0];
var songTitle = songArr[1];
return $http.get('/lyricsRequester.php', {
params: {
song_author: artist,
song_name: songTitle
}
}).then(function(result) {
return result.data;
});
}
}
});
app.controller('lyricsController', function($scope, lyricService) {
$scope.currentSong = 'Judas Priest - A Touch of Evil';
$interval(function(){
lyricService.getLyrics($scope.currentSong).then(function(lyrics) {
$scope.lyrics = lyrics;
console.log($scope.lyrics);
});
}, 10000);
});
You also have some other issues, like using ng-model on your span. ng-model is an angular directive that is used in conjunction with form elements (input, select etc.), not a span as you have. So you might want to change that into an input field.
$http does not use .then, it uses .success and .error. the line that you have where it says then is undefined, should be replaced with a success and error handler instead. Below is a sample from the docs:
// Simple GET request example :
$http.get('/someUrl').
success(function(data, status, headers, config) {
// this callback will be called asynchronously
// when the response is available
}).
error(function(data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
See Link:
https://docs.angularjs.org/api/ng/service/$http

undefined function in timeout angularjs

I have the following controller :
app.controller('ListeSASController', function($scope, $rootScope, $routeParams, $location, userService, RefreshSASServices, $timeout){
this.IsUserLogged = function()
{
return userService.user().isLogged;
};
var promise = $timeout(RefreshSASServices.RafraichirSAS(), 100);
this.getSAS = function(){
return RefreshSASServices.getSAS();
};
$scope.$on('$locationChangeStart', function(){
RefreshSASServices.ArreterLesRafraichissements();
});
});
with the following service :
app.service('RefreshSASServices', function($http, userService, serverConfigService, $q, $timeout, $translate, constantsServices) {
var listeSAS = [];
var $this = this;
var promiseRefreshSAS;
// Getters
this.getSAS = function()
{
return listeSAS;
};
//Setters
this.clearDatas = function()
{
listeSAS = [];
};
// Communication with the server
$this.getServerUri = function()
{
return serverConfigService.getServerUri()+"majsvc/";
};
// Fonctions de rafraichissement
$this.ArreterLesRafraichissements = function()
{
if(promiseRefreshSAS !== undefined)
$timeout.cancel(promiseRefreshSAS);
};
$this.GetSASFromServer = function()
{
var promises;
if(userService.user().isLogged)
{
var uri = $this.getServerUri() + "getAllSAS/"+userService.user().UserObject._id;
promises = $http.get(uri)
.success(function(data, status, headers, config) {
// this callback will be called asynchronously
// when the response is available
return data;
}).
error(function(data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
return "";
});
}else{
promises = $q.when(!userService.user().isLogged)
}
return promises;
};
$this.RafraichirSAS = function () {
// functions that call
$this.GetSASFromServer()
.then(function(promise){
if(promise !== undefined && promise.data !== undefined)
{
listeSAS = promise.data;
//alert('refreshing the SAS list:' + JSON.stringify(listeSAS));
}else listeSAS = [];
promiseRefreshSAS = $timeout($this.RafraichirSAS, 3000);
})
.catch(function(error)
{
console.error("Error :", error);
promiseRefreshSAS = $timeout($this.RafraichirSAS, 7000);
});
};
});
When I load my page using routes :
.when('/listeSAS', {
templateUrl : './includes/sas/liste_sas.html',
controller : 'ListeSASController',
controllerAs : 'controller'
})
everything works fine, if my data changes on the server it gets updated on the UI, My UI is also displaying what I want. Everything is OK except that when the pages loads I get the following error :
TypeError: undefined is not a function
at file:///includes/libs/angular.js:14305:28
at completeOutstandingRequest (file:///includes/libs/angular.js:4397:10)
at file:////includes/libs/angular.js:4705:7
which is the function "timeout" of angular, and the line 14305 is :
try {
deferred.resolve(fn());
} catch(e) {
deferred.reject(e);
$exceptionHandler(e);
}
finally {
delete deferreds[promise.$$timeoutId];
}
Why angular is throwing this exception ? What did I do wrong ?
To be known :
On my login page I set 2 timeouts which I don't stop because they refresh "global" variables such as the number of private messages. Despite the error both timeout are still working.
I use node webkit with my application and it crashes maybe one in three times when I open this route (after 5-10 seconds).
Thank you for your help.
Is it that you're calling RafraichirSAS(), which returns undefined instead of passing in the function?
E.g, instead of
$timeout(RefreshSASServices.RafraichirSAS(), 100);
Do
$timeout(RefreshSASServices.RafraichirSAS, 100);

Caching an array in an angularjs Service

I'm new to AngularJS and am still trying to wrap my head around using services to pull data into my application.
I am looking for a way to cache the result of a $http.get() which will be a JSON array. In this case, it is a static list of events:
[{ id: 1, name: "First Event"}, { id: 2, name: "Second Event"},...]
I have a service that I am trying to use to cache these results:
appServices.service("eventListService", function($http) {
var eventListCache;
this.get = function (ignoreCache) {
if (ignoreCache || !eventListCache) {
eventListCache = $http.get("/events.json", {cache: true});
}
return eventListCache;
}
});
Now from what I can understand I am returning a "promise" from the $http.get function, which in my controller I add in a success callback:
appControllers.controller("EventListCtrl", ["$scope", "eventListService",
function ($scope, eventListService) {
eventListService.get().success(function (data) { $scope.events = data; });
}
]);
This is working fine for me. What I'd like to do is add an event to the eventListService to pull out a specific event object from eventListCache.
appServices.service("eventListService", function($http) {
var eventListCache;
this.get = function (ignoreCache) { ... }
//added
this.getEvent = function (id) {
//TODO: add some sort of call to this.get() in order to make sure the
//eventListCache is there... stumped
}
});
I do not know if this is the best way to approach caching or if this is a stupid thing to do, but I am trying to get a single object from an array that may or may not be cached. OR maybe I'm supposed to call the original event and pull the object out of the resulting array in the controller.
You're on the right track. Services in Angularjs are singeltons, so using it to cache your $http request is fine. If you want to expose several functions in your service I would do something like this. I used the $q promise/deferred service implementation in Angularjs to handle the asynchronus http request.
appServices.service("eventListService", function($http, $q) {
var eventListCache;
var get = function (callback) {
$http({method: "GET", url: "/events.json"}).
success(function(data, status) {
eventListCache = data;
return callback(eventListCache);
}).
}
}
return {
getEventList : function(callback) {
if(eventListCache.length > 0) {
return callback(eventListCache);
} else {
var deferred = $q.defer();
get(function(data) {
deferred.resolve(data);
}
deferred.promise.then(function(res) {
return callback(res);
});
}
},
getSpecificEvent: function(id, callback) {
// Same as in getEventList(), but with a filter or sorting of the array
// ...
// return callback(....);
}
}
});
Now, in your controller, all you have to do is this;
appControllers.controller("EventListCtrl", ["$scope", "eventListService",
function ($scope, eventListService) {
// First time your controller runs, it will send http-request, second time it
// will use the cached variable
eventListService.getEventList(function(eventlist) {
$scope.myEventList = eventlist;
});
eventListService.getSpecificEvent($scope.someEventID, function(event) {
// This one is cached, and fetched from local variable in service
$scope.mySpecificEvent = event;
});
}
]);
You are on the right track. Here's a little help:
appServices.service("eventListService", function($http, $q) {
var eventListCache = [];
function getList(forceReload) {
var defObj = $q.defer(), listHolder;
if (eventListCache.length || forceReload) {
listHolder= $http.get("/events.json", {cache: true});
listHolder.then(function(data){
eventListCache = data;
defObj.resolve(eventListCache);
});
} else {
defObj.resolve(eventListCache);
}
return defObj.promise;
}
function getDetails(eventId){
var defObj = $q.defer();
if(eventId === undefined){
throw new Error('Event Id is Required.');
}
if(eventListCache.length === 0){
defObj.reject('No Events Loaded.');
} else {
defObj.resolve(eventListCache[eventId]);
}
return defObj.promise;
}
return {
eventList:getList,
eventDetails:getDetails
};
});
Then, in your controller, you handle it like this:
appControllers.controller("EventListCtrl", ["$scope", "eventListService",
function ($scope, eventListService) {
var eventList = eventListService.getList();
eventList.then(function(data){
$scope.events = data;
});
$scope.getEventsList = function(reloadList){
eventList = eventListService.getList(reloadList);
eventList.then(function(data){
$scope.events = data;
});
};
$scope.getEventDetails = function(eventID){
var detailsPromise = eventListService.getDetails(eventID);
detailsPromise.then(function(data){
$scope.eventDetails = data;
}, function(reason){
window.alert(reason);
});
}
}
]);
This way, your events are loaded when the controller first loads, and then you have the option to request a new list by simply passing in a boolean. Getting event details is also handled by an internal promise to give you some error handling without throwing a disruptive error.

Categories