Why isn't this function executing on page load? - angularjs

I have a sidebar that contains a feed from various social medias, along with a service in AngularJS that queries my API for the data. Below is the controller, along with the service (in that order). Why isn't it being executed on page load, and how should I rewrite my code to make it load the data when the page is rendered to the client?
angular.module('HomeCtrl', ['MediaServ']).controller('HomeController', [
'$scope',
'MediaServ',
'$rootScope',
function($scope, $rootScope, MediaServ){
if ($rootScope){
$scope = $rootScope;
}
function handleRes(response){
console.log('hello!');
}
angular.element(document).ready(function () {
$scope.SocialMedia = function(){
MediaServ.feed()
.then(handleRes, handleRes);
}
});
}]);
angular.module('MediaServ', []).service('MediaServ', [
'$http',
function($http){
this.feed = function(){
return $http.get('/api/social/feed');
}
}]);

You can only use things (be it services, factories, filters, etc) from another module if you have first injected that module into your current one. As the code above is written your modules don't know about each other, and so you can't inject MediaServ into HomeController.
To fix it, inject the MediaServ module into the HomeCtrl module like this:
angular.module('HomeCtrl', ['MediaServ'])...
I will also suggest not shortening names (minifiers should shorten things, developers should not) and not using the same name for services and apps. The last one in particular can cause a lot of confusion in a large project. Personally I prefer to name modules things like "media.services" and services "MediaService" but that is personal taste, just keep a clear naming convention so that you always know what is what.

You shouldn't need to wrap your code in angular.element(document).ready(function () { });
The controller will execute on page load automatically, provided it is reference in the page.

So, UncleDave and Erik Honns' responses both led me to realize what was wrong (on top of having a typo in handleRes). Here is my new code:
angular.module('HomeCtrl', ['MediaServ']).controller('HomeController', [
'$scope',
'$rootScope',
'MediaServ',
function($scope, $rootScope, MediaServ){
if ($rootScope){
$scope = $rootScope;
}
function handleRes(response){
if (response.data.tweets){
$scope.SocialMedia = response.data.tweets;
}
}
MediaServ.feed()
.then(handleRes, handleRes);
}]);
It is now working. Thank you everybody for your help. I won't pick a best answer, and instead will let others vote. Feel free to flag this question to be deleted, as the errors were more on my side (kind of being lazy about reading through my code, but I thought that this was one of those Angular things you just kind of have to learn).
Thanks everyone for your help!

Related

Angular JS - Sending data from a directive to a parent controller

I have an app where I use the ngCart directive in order to store the items added to a basket. The problem is that this directive just has the functionality of sending the information about the items added by the user, but I would need also to send some information that I would get from a form.
So in order to send it in a single object, I need first to extract the data stored in the directive to my main scope, and then merge it with the data I get from the form.
For that I need to modify the ngCart.js directive. I tried to make a service, as adviced here, but I don't get to get it working. The code I added to the directive is this
.service('ngCartData', ['ngCart', function(ngCart){
return {
data:ngCart;
};
}])
, but I get an error saying Module 'ngCart' is not available!
I'm totally new to services and factories in angular, so I don't know exactly where to look to make it work. I made a plunkr with my code (I tried modifying the ngCart.js file with the code above, but the plunkr shows the directive without any modification). I just need to be able to send the data stored in the directive in the scope ngCart so that I can listen to it in the parent controller (see the checkout section in the plunkr).
Any help would be much appreciated. Thanks!
did you load the js file like this :
<script src="pathto/angular/angular.js"></script>
<script src="pathto/ngCart.js"></script> or ngCart.min.js
did you load the module in your declaration module like this ? :
var myApp = angular.module('myApp',['ngCart']);
You actually have this backward. You can't inject a directive into a service. You must inject the service into the main controller and into the directive so that you can use it as a bridge between the two. Services are singletons so if you modify the properties of a service those modifications will be available anywhere else it is asked for.
Your service will look something like this:
.service('ngCartData', [function(){
return {
data:[],
addData: function(newData){
this.data.push(newData);
},
getData: function(){
return this.data;
}
};
}])
then in your controller and directive use the ngCartData api, which would look something like this:
$scope.someData = ngCartData.getData();
$scope.someFunction = function(dataToStore){
ngCartData.addData(dataToStore);
};
You had the right idea in mind, and I'm surprised it didn't work for you.
I have edited your app in the following way (in script.js)
app.controller('myCtrl', function($scope, ngCart, myCart) {
$scope.names = [...];
...
console.log(myCart.cart);
})
.factory('myCart',function(ngCart){
return {
cart: ngCart.$cart
};
})
and it logged {shipping: 30, taxRate: null, tax: null, items: Array[2]}, which I think is what you need (I added 2 items before it logged).
Notice that adding a the service is redundant; The data is accessible whenever and wherever you need. Just inject ngCart to your controller/service/etc. and the updated data will be available to you.
Therefore, the following code is equivalent:
app.controller('myCtrl', function($scope, ngCart) {
$scope.names = [...];
...
console.log(ngCart.$cart);
});
A possible reason for the getting the error you got might be that, while editing the ngCart module, you had some sort of error (like a typo), which led to ngCart being invisible to angular.

Why shouldn't I access the factory function through a provider.$get in the config phase?

First of all, this is an honest question. And I'm looking for honest and justified answers on why I shouldn't be doing this...
angular
.module('X', ['Y'])
.config(function (myFactoryProvider, myServiceProvider) {
myFactoryProvider.$get().myFn();
myServiceProvider.$get().myFn();
});
angular
.module('Y', [])
.factory('myFactory', ['$location', function ($location) {
return {
myFn: function () {
console.log('factory');
console.log($location.absUrl());
}
}
}])
.service('myService', ['$location', function ($location) {
this.myFn = function () {
console.log('service');
console.log($location.absUrl());
}
}]);
Here's a JSFiddle: http://jsfiddle.net/1vetnu6o/
This is working as you can see above and it solves a few problems for me. But I shouldn't probably be doing this and I want to understand why. I really need good reasons to not do this. Despite the fact that I really want to.
tl;dr;
Here's the context of the problem...
I have this internal framework used by multiple products where there's this one service (which happens to be a factory) that basically contains a group of related helper methods. In this case device related like isMobileDevice, isAndroid, getDeviceType (with returns mobile, tablet or desktop), and few others...
This service must be injected into the config() phase of the application using the framework because we need access to the getDeviceType function. The thing is, we need to get the deviceType to load proper templates with $routeProvider. It's in the config() phase that we are building the correct template paths to be used for all the routes. Some of them depend on the deviceType, while others have a generic template independent of the device.
Since this is a service, we cannot inject it directly into the config() phase but we can call that method using the technique I mentioned earlier in the post.
How I'm currently solving this? The helper service is actually a provider and all the methods are exposed both in the provider section as well as in the factory function. Not ideal, but it works. I consider this a work-around, but I'd rather have a work-around in the application and not the framework, thus the technique first mentioned.
Thoughts?
I didn't knew but actually you can invoke on config phase.
The problem is that myService will be instantiated twice :/
angular
.module('X', [])
.config(function(myServiceProvider) {
myServiceProvider.$get().myFn();
})
.run(function(myService) {
myService.myFn();
})
.service('myService', ['$location', function($location) {
console.log('myService!');
this.myFn = function() {
console.log($location.absUrl());
}
}]);
output:
"myService!"
"location"
"myService!"
"location"
This is dangerous if you call $get on config of a service that has a big nested dependency tree :/
I think the correct approach, if you need a utility service (with no dependency), is to use a constant (in this way you can inject it everywhere). Otherwise if you need dependencies use a service and stick to the run() block.
The config() block it's the place to instruct your services how they should work with the help of their providers.
The run() block it's the perfect place to do some logic when your app starts (aka main method).

AngularJS: unknown provider until after page loads?

So this is really weird, maybe it has a simple answer I'm missing. The following code gives an unknown provider error:
var foo = angular.module('foo', [ 'ngRoute', 'ngAnimate', 'ngCookies' ]);
foo.factory('fooApi', function ($scope, $http) {
var url = '/api/';
var factory = {};
factory.action = function (fields) {
fields.userid = $scope.userid;
fields.token = $scope.token;
console.log(JSON.stringify(fields));
return $http.post(url, { data: fields });
};
return factory;
})
.controller('loginController', function ($scope, fooApi) {
// do stuff
});
It's all loading together in the same file, and I'd think the factory being first would resolve and the injector would be able to find it when referenced below. But it gives an unknown provider error.
However, if I comment out the controller and wait for the page to load and then do the exact same controller declaration in the Chrome JS console it works fine.
Anyone run into this before and know how to deal with it? I haven't been able to find this same exact issue anywhere.
Like #tasseKATT said, you can not inject $scope into a service, particularly a factory. Maybe your confusion is because $scope can be injected in controllers, so you tried to injected into a factory.
An interesting thing is that the $scope that you see being injected into controllers is not a service - like the rest of the injectable stuff -, but is a Scope object.
The main purpose of $scope is a king of glue between views and controllers, it doesn't make much sense to pass a $scope into a service.
The services only have access to the $rootScope service.
If you need to pass the $scope of a specific controller to a service always you can pass it like parameter of a function in the service. This approach is not recommended because starting to break the SoC and the single responsibility principle, but maybe could fit you.
Good luck :-)

angular factory not passing data to controller

I've got a simple factory passing data gathered from Mongolab into one controller, but I can't get it in another.
app.factory('User', function ($mongolabResourceHttp) {
return $mongolabResourceHttp('users');
});
I've also got a Goal factory just like this. Both work fine as-is.
The above factory is working fine in my userCtrl, but I can't add it into my goalCtrl. I've got a userguid that I want to attach to goals. Basically: click link to user goals, create goals, userguid becomes part of saved goal.
app.controller('goalCtrl', function ($scope, $location, goals, users) {
$scope.goals = goals;
$scope.users = users;
$scope.new = function () {
$location.path('/');
};
...
});
Any clues would be much appreciated. Thanks.
(I asked a similar question earlier today, but I think it had too many issues going on and it didn't get any responses. I have since worked out one of the issues and this question is to narrow it down further. So this isn't a complete repeat. Sorry if it bugs anyone though.)
You need to inject the service into your controller using the actual service name of 'User':
app.controller('goalCtrl', function ($scope, $location, goals, User) {
$scope.users = User;
I'd also read up on angular service and dependency injection when you get a chance.

inject common providers to angularjs controllers

There are set of commonly used providers across my angularjs controllers. Eg. $scope, $rootScope, $http and my customer services. I would like to inject those common providers to all controllers without defining each of them in controllers
Eg,
Instead of,
function HomeCtrl($scope, $rootScope, $http){
.....
}
function MenuCtrl($scope, $rootScope, $http, MyService){
.....
}
Do this,
function HomeCtrl(){
...
// $scope, $rootScope, $http are available here
...
}
function MenuCtrl(MyService){
...
// $scope, $rootScope, $http are available here
...
}
So, in this example $scope, $rootScope, $http are automatically injected to all controllers. But I'm not sure how to do this. Anybody knows a way? Thanks.
I don't think spraying all your controllers with the same dependencies is a good idea. It suggests that the application does not have a good structure and you should probably address this.
Taking your example there are a few things that stand out. You don't need to depend on $scopeand $rootScope, you can get the root scope from $scope using $scope.$root
If you are wrapping logic up in services and reusing code amongst controllers then I would suggest that your controllers don't need to depend on $http, instead move this code into the services that you depend on. Try and name those services appropriately so that you can tell what they do. For instance prefer AccountCreator over something more generic like AccountsService. In fact never name something xxxService the service suffix is a throw a way line that really adds no meaning.
In terms of your actual question, no I don't think there is a way of doing this out of the box. I have some ideas of how you could hack it in, but I think pandoras box should be left locked.

Resources