how to handle JSON file loading with Angular - javascript

I have a few controllers that call the method getData() from a service.
In order to not do extra http calls for the same json file, i'm using something like this:
QuizApp.service('quizService', ['$http', function($http) {
var quizService = {},
quizData;
quizService.getData = function() {
return quizData ? quizData : quizData = $http.get('quiz.json');
};
return quizService;
}]);
...but things don't work properly if I do it like that (the data is used to populate a slider and a thumbnail gallery with angular-slick and some problems arise. For now it maybe doesn't matter, I just want to know if the code above makes sense).
On the other hand, if I write getData() like this:
QuizApp.service('quizService', ['$http', function($http) {
var quizService = {},
quizData;
quizService.getData = function() {
quizData = $http.get('quiz.json');
return quizData;
};
return quizService;
}]);
... which will do various http requests for the same json file (doesn't look like a good practice to me), everything works fine and the slick angular gallery works properly. But not 100% of the times though: kind of randomly things don't work well too (same symptoms. I might describe them but again, I don't think that's the point here)
So, in general, regardless of the context, which one of those versions of getData() looks good and which doesn't and why?
UPDATE
As Mark pointed out, Angular has a built in cache, but it's set to false by default. Here is a post and here is the documentation.
If I cache the result of the http request though I get the same problem (I'm not describing it here) I was getting with my second option, and it has apparently nothing to do with that.
Actually, it seems that if I repeat the http request two times (as in my second snippet of code) things work by chance (90% of the time?).
So, by caching the result, at least I get a consistent result, which means in this case that the slick-angular thing won't work properly (never) and I have to look for a solution somewhere else.

Angular $http has a built in cache, which you could make use of here. You can cache all $http requests, which is probably a bad idea, or specific ones.
On a very simple level, this should work for you
quizService.getData = function() {
return $http.get('quiz.json', {cache: true}).then(quizData => {
return quizData;
});
};
You can find out more in the Angular docs for $http

Related

Javascript \ Angular function help - mental block

I don't know what it is about JS, but I have a mental block. I apologize for the dumb question, but I'm at a loss because no matter how much I read I cannot get the academics into practice. Especially when it comes to nested functions.
I have a controller, lets say FileCtrl. Inside of it I have the the following that listens for file added to an input field via a directive. I'm attempting to inject an Angular JS factory service service called fileReader (a queue service for HTML5 FileReader).
However,I keep getting a undefined error on fileReader. I know why because, it cannot see fileReader, but injecting it at $scope.$on and then again on $scope.$apply doesn't work. Also, adding fileReader as a closure at the end of $scope.$on doesn't work either.
I should add that I can see the args.file and if I remove the fileReader code it will push the file no problem, but I then have no thumbnail. So I it works, just not with the fileReader and that is because Im doing something wrong with injection.
Side note, to Vals comment below I use apply as I found there was a image render sync issue without it which works fine for smaller images, but with larger images it freezes which is why I'm attempting to create and use a $q fileReader service. I suppose another way to solve for it would be to create a watch / directive on the array entry and when img comes back with the 64 encode string populate the html element ... like I said JS mental block :)
myApp.controller('FileController', ['$scope', 'FileReaderService', function($scope, FileReaderService ){
$scope.$on("fileSelected", function (event, args) {
$scope.$apply(function () {
$scope.progress = 0;
fileReader.readAsDataUrl(args.file, $scope)
.then(function(result) {
$scope.imageSrc = result;
});
$scope.files.push(args.file);
});
});
});
In AngularJS not all functions are been processed by Dependency Injection. In Controllers, Directives (in definition of directive and in controller, not on link or compile), Servicies AngularJS inject requested instances, but in some other functions (like event listeners) arguments are passed by position.
In your case you need to put fileReader into definition on controller, not on event listener.
Also you need to remove apply because event listeners added via $on are included into digest loop.
Thanks to all for your replies. Val you made me go back and do a little more research and I found the answer with a little debugging. Not sure I understand why yet, but I have an idea.
If there is an error in your factory service, in my case, FileReaderService angular won't always explode when bootstrapping the service, will only explode when you call the service, which makes kind of makes sense. If something is wrong in the service the entire service will not boot. Also, you won't get any error message when injecting it into the controller. I had to place a watch on the module and noticed there was a reference error. I found I had a missing function.
Purely inexperience on my end, but I kept trying to capture the results form the $q service, which is was doing fine, but then attempting to inject to outside the $q return i.e. I was attempting to capture $scope.imageSrc = result and insert it post the .then, which doesn't work as you have a sync issue. I could see the value in the $scope.files, but it would not console.log or show up in HTML. So I moved all the file manipulation into the .then and it works perfectly. Logical when you think about it :) why have a $q if you not going to use it ... lol.
// problem code
fileReader.readAsDataUrl(args.file, $scope)
.then(function(result) {
$scope.imageSrc = result;
});
// cannot and should not try to work the results outside the return promise
$scope.files.imgSource = $scope.imageSrc;
$scope.files.push(args.file);
//Fixed and working code
myApp.controller('FileController', ['$scope', 'FileReaderService', function($scope, FileReaderService ){
var reader;
$scope.files = [];
//listen for the file selected event
$scope.$on("fileSelected", function (event, args) {
$scope.progress = 0;
var file = args.file;
FileReaderService.readAsDataUrl(file, $scope)
.then(function(result) {
file.imgSource = result;
$scope.files.push(file);
});
});
});

how to remove include module error in angular ?

I make a simple demo in my Pc which is working fine .But when I make fiddle to ask Question say
Uncaught Error: [$injector:nomod] Module 'myapp' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
can you please tell why it is occur ? I am getting data n my pc.Actually my real Question how to refresh or call same webservice after some tome mean after 1 minutes.As in jquery we have setinterval function .how I will achieve in this angular ?
here is fiddle
http://jsfiddle.net/acboLcv2/1/
var app=angular.module("myapp");
app.factory('test', function($http) {
//This whole object is returned when the Service runs. It is a singleton
//and its properties can be accessed from any controller
return {
stationDashBoard: function(callback,error) {
$http.get('http://184.106.159.143:8180/FGRailApps/jservices/rest/a/departure?crsCode=VIC').success(callback).error(error);
}
}
});
function departureContrl($scope,test){
$scope.loading=true;
test.stationDashBoard(function(data){
console.log(data);
$scope.data=data.data;
$scope.loading=false;
//alert(data);
},function(error){
alert('error')
}) ;
}
Thanks
So there's a few things with your site you need to focus on:
Getting rid of the module error:
I think it's fixed, but heuristically I suggest:
Ensure that the declaration of an angular module includes the empty array as as already been mentioned. This is necessary, thusly:
angular.module("test", []);
Ensure you reference the angular app in the html. I suggest body for most applications:
CORS error
You're trying to load data from a different domain with $http.get(...). This won't work unless you do some kind of CORS hackery or you get the data from the same domain. Ie, you'd have to host this code on http://184.106.159.143:8180 (in this example).
Polling request
You're asking about fetching data from a server every n seconds. This is quite easy and the method you suggest with setTimeout() would work but would need to be integrated into the angular Digest Loop:
I suggest using $timeout because it will work with angular's rendering something like this (this is pseudocode, not tested):
var fetchFromServer(cb){
$timeout(function(){
$http.get(...).then(function(data){
//Do something with the retrieved data
cb(fetchFromServer); //Recurse
});
}, 15000);
};
fetchFromServer(fetchFromServer);
Otherwise you can use setTimeout as you normally would in javascript, but don't forget to call the $scope.$apply() method to render things in angular if you do it outside the digest loop or else it will appear as if there has been no effect.

AngularJS : filter with AJAX

I have been working on AngularJS project recently and came over an interesting problem while trying to create a filter which is using data loaded via AJAX request.
First about the problem:
AngularJS filter is a synchronous piece of code (function) that returns a string which is inserted into your DOM. And in most of the cases it works perfectly fine e.g. following filter that capitilizes first letter:
angular.module('myApp.filters', []).filter('capitilize', function() {
return function (word) {
return word.charAt(0).toUpperCase() + word.substr(1);
}
});
And this works great. Now the question is what if I can't return the desired result right away? Say I need to load some data via AJAX request to get the desired result. If I make an AJAX request my return statement will return an empty result before I get my data. So really the question is how do I notify filter to update itself when my data is loaded?
Solution:
It turned out that the solution was right there in front of me, but it took me some time to figure out how the magic is happening. Say I need a filter that retrieves artist's biography based on their name (yeah, a little bit crazy example but it proves the point):
angular.module('myApp.filters', []).filter('biography', function($q, $http) {
var pending = {};
return function (artist) {
if ( !(artist in pending) ) {
pending[artist] = null;
$http.get('http://developer.echonest.com/api/v4/artist/biographies?api_key=FILDTEOIK2HBORODV&name='
+ artist + '&format=json&results=1&start=0&license=cc-by-sa')
.then(function(response){
pending[artist] = response.data.response.biographies[0].text;
});
}
return pending[artist] || '';
}
});
It works, but how? I made a get request, got my result, but how does it force filter to update itself. The key here is the angular's $q (A promise/deferred implementation inspired by Kris Kowal's Q.)
from angular documentation:
$q is integrated with the $rootScope.Scope Scope model observation mechanism in angular, which means faster propagation of resolution or rejection into your models and avoiding unnecessary browser repaints, which would result in flickering UI.
This means that whenever the promise is resolved it causes the update, in fact here is the code from angular:
function done(status, response, headersString, statusText) {
if (cache) {
if (isSuccess(status)) {
cache.put(url, [status, response, parseHeaders(headersString), statusText]);
} else {
// remove promise from the cache
cache.remove(url);
}
}
resolvePromise(response, status, headersString, statusText);
if (!$rootScope.$$phase) $rootScope.$apply();
}
Hope this was helpful for you. Here is the example, keep in mind that the example is making cross domain ajax calls, you will need to disable cross domain policy of your browser:
http://jsfiddle.net/pJuZ9/8/
IMPORTANT: Keep in mind not to overwhelm filter with same ajax request, otherwise you might end up with this:
Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: []
thank you for that post, at least I knew I'm not completely wrong. I have a very similar scenario:
1) a simple translate filter
module.filter('translate', ['Localization', function (localization) {
var translateFilter = function (key) {
return localization.get(key) || "[" + key + "]";
};
translateFilter.$stateful = true;
return translateFilter;
}]);
2) a localization service which stores a JS dictionary. That is updated via XHR. That means that there are multiple possible states of the dictionary:
no localization before init => empty
default localization after init => non-empty
user-defined localization after default is loaded => non-empty
My problem was that the filter result wasn't updated (re-rendered) after the XHR call ended, even though a digest was performed. I had to add
translateFilter.$stateful = true;
to make it work. Even changing filter dependency from service to value did not help. Someone might find this helpful, or event better, tell me, what I was doing wrong :-)

Difference between resource.save() with callback and $promise in ngResource using AngularJS

This is my factory:
// Factory
app.factory('BuildingListService', ['$resource', function($resource) {
return {
selectBuilding: function(building, callback) {
return $resource(window.appSettings.context+'/requests/building')
.save(building, callback);
}
};
}]);
Now, I thought that these two bits of code should behave the same:
First:
$scope.selectBuilding= function(building) {
BuildingListService.selectBuilding(building, function() {
$state.go('requests.detail.analystReview');
});
});
Second:
$scope.selectBuilding= function(building) {
BuildingListService.selectBuilding(building).$promise.then(function() {
$state.go('requests.detail.analystReview');
});
};
However in the first state I get a race condition where the server has not finished the request prior to changing state. I am a little confused as to why that is. The second way I mentioned has no such issue. Is there a reason that the $state.go('requests.detail.analystReview'); would be called in the first bit of code before the server has finished? This one has really confused me.
Also note that solution one is working fine in IE and FireFox, I am starting to suspect a caching issue
Update:
So I have solved my issue, the issue was caching related, IE caches a little aggressively sometimes I should have see that. However I am not sure why the first call would make IE more prone to caching the requests than the second. Thank you to anyone that looked at this.
Might it be that your first example isn't relying on BuildingListService at all? Shouldn't it be something like this?:
$scope.selectBuilding = function(building) {
BuildingListService.selectBuilding(building, function() {
$state.go('requests.detail.analystReview');
});
};

RequireJS define Browser Race Condition

We have a client-side plug-in framework that is constructed of modules (AMD) and utilizes require.js. In this framework we expose a public object that consists of configuration properties and common framework functionality. All of the required functionality for the public object is contained in one file (albeit separated into modules); the only file required by the end-user to add to their page.
The issue we are seeing is most prevalent in Safari but also shows itself occasionally in IE and Chrome. 100% of the time in Safari with an empty cache we encounter a race condition. Consider this example client code which is in the body of the client’s page.
<script type=”text/javascript”>
Me.subscribe(‘someEvent’, someHandler);
</script>
‘Me’ is always available to the page as its global and outside of any define call. However, ‘Me.subscribe’ is wrapped in ‘define’ and results in ‘undefined’ with the conditions I stated above.
We can’t tell the client to use any third-party frameworks to work around this issue. The code block above must stay exactly as it is.
I’ve been playing with the idea of allowing certain public function binding to be deferred without any additional work required by the client. So far, this is what I’m considering adding to the framework:
Me.deferred = function (fn, name) {
if (fn) return fn;
fn = this;
return function () {
var args = Array.prototype.slice.call(arguments);
setTimeout(function () {
fn[name].apply(this, args);
}, 0);
};
};
Then, in the framework near the top, I can add items I want deferred like this:
Me.subscribe = Me.deferred(Me.subscribe,'subscribe');
My questions are these: Am I missing something that is already out there? Is there an existing pattern that I am not aware of to handle this exact case? Is this just a bad idea in general?
If possible, make sure the client puts requireJS and all dependencies in the head. 'Me' can include an on-demand call which executes on creation if that is not possible.

Categories

Resources