Javascript \ Angular function help - mental block - javascript

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);
});
});
});

Related

variable not found / undefined after grunt build (uglify)

I am using AngularJS for building a simple app with a map. As the main ctrl had too many logic I build a second controller for the navbar. Until here everything worked fine. Now I outsourced the map.on('zoomend' ... ) function when refactoring the main controller.
The problem now is, that when the navbar controller file is minified (through grunt build uglify) I get the following error:
Cannot read on of undefined
That means, map is undefined even though it is declared at the top of the file AND I do not have the problem on localhost (grunt serve).
Navbar Ctrl:
'use strict';
angular.module('angularMapApp').controller('navbarController', navbarController);
navbarController.$inject = ['$scope', '$mdSidenav', 'helper', 'RespondService', 'shipTypes'];
function navbarController($scope, $mdSidenav, helper, RespondService, shipTypes) {
var map = RespondService.getMap();
map.on('zoomend', function() {
timestamp = RespondService.getTimestamp();
selectedShipTypes = RespondService.getSelectedShipTypes();
selectedShipState = RespondService.getSelectedShipState();
showGrid = RespondService.getShowGrid();
helper.loadAndShowShipMarkers(timestamp, selectedShipTypes, selectedShipState, showGrid, map).then(function(results) {
$scope.numberOfShips = results;
RespondService.setNumberOfShips($scope.numberOfShips);
});
});
So this is a short version of my controller. The grunt file is still the same as created with yeoman. I too logged the map value at the top of the file, and there it has a value. However using 'map.on' might not work.
Maybe anyone can help me with that.
Your $inject seems to be correct, so minify should work fine for angular injection. It looks like var map is loading data from RespondService.getMap(). What do you expect to get from that function? You might want to put a break point and see if what you expecting is being returned.

how to handle JSON file loading with Angular

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

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.

What's the correct way to indicate to your scope that your google apis have loaded?

This is a pretty newbie question I'm afraid. I've been reading through a lot of tutorials about using app engine, but unfortunately not a lot of them use AngularJS.
I am trying to use an app engine api endpoint and connect into it with a javascript client. When I load my endpoint using
function informAngularOfGAPIReady() {
// SCOPE IS NOT DEFINED HERE
$scope.$apply(function() {
$scope.google_backend_ready = true;
});
}
// This function is the callback for when the Google apis are ready.
function init() {
gapi.client.load('helloworld', 'v1', informAngularOfGAPIReady, '/_ah/api');
}
I find that $scope is undefined for informAngularOfGAPIReady.
On the tutorial by google (https://cloud.google.com/developers/articles/angularjs-cloud-endpoints-recipe-for-building-modern-web-applications), in tip #3 (at the very bottom of the page) they recommend using this approach, but i don't understand how $scope can be defined.
I know that you can use the document object to search for a view which you can then extract the scope from, but this doesn't seem like a very clean approach. Some examples I saw just passed null for the load callback but this doesn't make a lot of sense because you can't have your UI reflect that it's not ready for requests yet.
How do people usually do this? I would think this is a problem for anyone using app engine to host apis.
Thanks in advance.
The sample code in the tutorial is meant to be placed in a controller (ie. inside of an Angular app). They demonstrate it declared in the view as:
<div ng-controller="GuestbookCtrl" class="container" ng-show="is_backend_ready">
…. guestbook UI...
</div>
Your controller code might look something like:
.controller('MyController', function($scope) {
$scope.informAngularOfGAPIReady = function() {
$scope.$apply(function() {
$scope.google_backend_ready = true;
});
}
$scope.init = function() {
gapi.client.load('helloworld', 'v1', $scope.informAngularOfGAPIReady, '/_ah/api');
}
});
... where the scope is injected into the controller and your callback functions is defined on it. You still do need to use $apply since it will be fired from outside of the Angular framework.

initial $broadcast from service is received in controller under <head> but not under <body>

I have a directive that reads sync data from <title> tag. Then it trigger a service which $broadcast the data to all controllers.
But the controllers under <body> tag are not receiving this. However if I move ng-app attr from html to body, and move the directive with the controller from head to body. Then all the controllers will work properly.
Here is my sample code: http://jsbin.com/oBAMOs/4/edit?html,js,console,output
From the code I believe you can pretty much guess what I am trying to do. So why is this happening and is there a better way to achieve this?
navCtrl doesn't exist at the point at which you send your broadcast. You can confirm that by putting log statements at the beginning of each controller and the send. You'll see you send before navctrl is created. ("title" happens then "send" then "nav")
An easy way to resolve this is to push your $broadcast till after the browser finishes all current queued up tasks (which will include rendering the rest of the DOM, and thus the instantiation of navCtrl). You can accomplish this by placing the broadcast within a $timeout that has a delay of 0. As follows:
$timeout (function() {
$rootScope.$broadcast('processed');
},0);
and make sure to pass timeout in:
.factory('syncPageid', ['$rootScope','$timeout', function($rootScope,$timeout){
This is a by product of the single threaded nature of javascript. For a discussion on why timeout works here and the underlying issue you can check out: setTimeout with zero delay used often in web pages, why? and http://ejohn.org/blog/how-javascript-timers-work/
Instead of just
$rootScope.$broadcast('processed');
you can have
$rootScope.$broadcast('processed', "data_you_need_to_pass_around");
and catch that like
$scope.$on('processed', function (e, args){
$scope.title = args;
console.log('titleCtrl: ' + args);
});
http://jsbin.com/oBAMOs/11/edit
And then ofcourse your syncPageid factory can expose it to all other code bits that is interested in pageid. But having a factory just to facilitate passing data between places bits makes no good sense.

Categories

Resources