Angular Services within Modules - angularjs

Having difficulty injecting a service into another service. I want a service hierarchy, where I can pass/request logic to/from the parent service, for sake of brevity and encapsulation.
So for example, I have a userService, and I want the userService to manage the user's toDoList. So I created a toDoService, and I want controllers to add a toDo for the user by passing the request to the userService, which relays to the toDoService. Here's what I have:
// app.js
angular.module('myApp', [
// other dependencies...
'myApp.myServices'
]);
// services/toDoService.js
angular.module('myApp.myServices', [])
.factory('toDoService', function($http) {
getStuff = function(userId) {
// returns $http call
};
addStuff = function(userId, data) {
// returns $http call
};
});
// services/userService.js
angular.module('myApp.myServices', [])
.factory('userService',
['$http', 'toDoService', function(
$http, toDoService) {
addToDo = function(data) {
toDoService.addStuff(user.uid, data)
.then(function(success) {
// apply bindings
})
.catch(function(error) {
// display error
});
};
getToDos = function(data) {
toDoService.getStuff(user.uid)
.then(function(success) {
// apply bindings
})
.catch(function(error) {
// display error
});
};
}]);
The controller works with the userService, and code from toDoService worked when it was originally in userService. But when I create the toDoService and move that code there and encapsulate it, angular complains about toDoService.
Error: [$injector:unpr] Unknown provider: toDoServiceProvider <- toDoService <- userService
I've checked script references, and all scripts are properly included. e.g. <script src='/[..]/toDoService.js' /> etc...
So I'm wondering is it even possible to inject a service into another service within the same module? Is it an issue with my naming convention?

angular.module('myApp.myServices', [])
This line in userService.js defines the module myApp.services, overwriting the one that has previously been defined in toDoService.js.
Define the module only once (in a separate file). Get a reference to this previously defined module using
angular.module('myApp.myServices')
i.e. without the empty array as second argument.

Related

Angular: load environment properties before config/run

I'm developing a angular app, and this app has about a 10 configurable properties (depending on the environment and client).
I had those properties in json config files, but this is really troublesome: there must be specific builds per env/company. So I would like to retrieve those properties once from the backend on app load.
So in order to do this I created a Provider
var app = angular.module('myApp', [...]);
app.provider('environment', function() {
var self = this;
self.environment;
self.loadEnvironment = function (configuration, $http, $q) {
var def = $q.defer();
$http(...)
.success(function (data) {
self.environment = Environment.build(...);
def.resolve(self.environment);
}).error(function (err) {
...
});
return def.promise;
};
self.$get = function(configuration, $http, $q) {
if (!_.isUndefined(self.environment)) {
return $q.resolve(self.environment);
}
return self.loadEnvironment(configuration, $http, $q);
};
}
app.config(... 'environmentProvider', function(... environment) {
...
//The problem here is that I can't do environment.then(...) or something similar...
//Environment does exists though, with the available functions...
}
How to properly work with this Provider that executes a rest call to populate his environment variable?
Thanks in advance!
This is an excelent scenario to explore angularjs features.
Assuming that you really need the environment data loaded before the app loads, you can use angular tools to load the environment and then declare a value or a constant to store your environment configs before the app bootstraps.
So, instead of using ng-app to start your app, you must use angular.bootstrap to bootstrap it manually.
Observations: You mustn't use ng-app once you are bootstrapping the app manually, otherwise your app will load with the angular default system without respecting your environment loading. Also, make sure to bootstrap your application after declare all module components; i.e. declare all controllers, servieces, directives, etc. so then, you call angular.bootstrap
The below code implements the solution described previously:
(function() {
var App = angular.module("myApp", []);
// it will return a promisse of the $http request
function loadEnvironment () {
// loading angular injector to retrieve the $http service
var ngInjector = angular.injector(["ng"]);
var $http = ngInjector.get("$http");
return $http.get("/environment-x.json").then(function(response) {
// it could be a value as well
App.constant("environment ", response.data);
}, function(err) {
console.error("Error loading the application environment.", err)
});
}
// manually bootstrap the angularjs app in the root document
function bootstrapApplication() {
angular.element(document).ready(function() {
angular.bootstrap(document, ["myApp"]);
});
}
// load the environment and declare it to the app
// so then bootstraps the app starting the application
loadEnvironment().then(bootstrapApplication);
}());

Browserify AngularJS Module factory method extend another factory method

I'm currently attempting to implement Browserify over an existing application.
I have a requirement where I have a BaseService that contains a bunch of standard functionality e.g. setting standard headers on requests etc.
In my factories I use loadash to extend the BaseService. For this to work I need to have a reference to BaseService in any factory that tries to extend it. I can't figure out how to pass through this dependency now I have started to use browserify.
I've added sample code below.
Module declaration:
'use strict';
var angular = require('angular');
module.exports = angular.module('todoApp.services', [require('../secure').name])
.factory('AuthService', ['$q', 'ConsumerConfig', require('./auth-service')])
.factory('BaseWebService', ['$http', '$q', 'Encryption', 'nativeCrypto', require('./base-web-service')]);
AuthService:
'use strict';
var _ = require('lodash');
module.exports = function($q, ConsumerConfig) {
return _.extend({
config: ConsumerConfig,
authenticate: function (options) {
var deferred = $q.defer();
this.callService({
user: options.user,
url: "/AuthenticateUser",
type: "GET"
}).then(function (response) {
deferred.resolve(response.data.userAuthResponse.responseMessage.Token);
}, function (error) {
deferred.reject(error);
});
return deferred.promise;
}
}, BaseWebService);
};
I would ideally like to use DI to inject the dependency but when I try this I keep getting an unknown provider error. Does anyone know how I can get this working?
For anyone looking - I solved this by creating a new "Core" module and then having my services module require the core module.
This way everything was loaded in the correct order

AngularJS: Custom Providers, undefined functions?

I have a provider like so:
angular.module('myApp').provider('test', function(){
this.$get = function($http){
return {
test2: function(){
}
}
};
});
I then use the provider in my app.config:
app.config(function($urlRouterProvider, $stateProvider, testProvider){
testProvider.test2();
$urlRouterProvider.otherwise('/home');
$stateProvider.state('home', {url: '/home', templateUrl: 'template/guide.html'})
.state('cost', {url:'/cost', templateUrl:'template/cost.html'})
});
Im trying to use a provider to get all my pages from a database and return them to the stateProvider... (Which I cant do in app.config because I cant inject the $http service)
This is the error I'm getting:
Failed to instantiate module myApp due to: TypeError: undefined is
not a function
You are getting the error, because yu are trying to call test2() on the provider (TestProvider).
test2() is not a method of the provider, but of the provided service (and thus is only accessible at "runtime", not during config).
If I understand correctly what you are trying to do, here is a possible approach:
Create a DelayedRoutes service (using provider()), which has a method for fetching the routes from the server and then registering them with the routing module (be it ngRoute or ui.router or whatever).
This configRoutes() methods needs to be invoked at runtime (so it has access to $http etc).
It also needs access to the routing module's provider (e.g. $routeProvider, $stateProvider etc), which are only available during the configuration time.
Your provider will have a method (setRouteProvider()), which get's executed during config and stores the $whateverProvider for later use (i.e. when the configRoutes() gets executed at runtime).
Here is a sample implementation (using ngRoute for simplicity). You can modify it according to your needs:
app.provider('DelayedRoutes', function () {
var routeProvider;
// This will be called during `config`
this.setRouteProvider = function (rp) {
routeProvider = rp;
};
// This will return the actual service
this.$get = function ($http, $route) {
// This will be called at runtime, to fetch the routes
// from the server and configure client-side routing
function configRoutes() {
return $http.get('/gimme/ma/routes').then(
function onResponse(response) {
var routes = response.data.routes;
angular.forEach(routes, function (config, path) {
if (path === 'otherwise') {
routeProvider.otherwise(config);
} else {
routeProvider.when(path, config);
}
});
$route.reload();
},
function onError(error) {
// Handle the error...
}
);
}
return {
configRoutes: configRoutes
};
};
});
// During `config`, store `$routeProvider` for later use
app.config(function ($routeProvider, DelayedRoutesProvider) {
DelayedRoutesProvider.setRouteProvider($routeProvider);
});
// At runtime, config the routes
app.run(function (DelayedRoutes) {
DelayedRoutes.configRoutes();
});
See, also, this short demo.

Angular.JS share a single JSON object between controllers

I´m trying to code a CRUD app with Angular.JS, and I need your help to move on.
This is the scenario:
View 1 (index) gets JSONP data from a remote API and stores it.
View 2 (master) shows data filtered on a grid
View 3 (detail) shows an specific item selected on View 2
I did it already, but requesting the very same JSON object on each view, , but I think one only api call is enough.
I can´t figure out how to properly share this JSON object for all the controllers. I tried several tutorials on ngResource, $http, factories and services but still have not a clear path to go through.
How can I do this?
Any snippet or code sample you may share will be very useful to keep on tryin this thing...
Thanks in advance,
Ariel
You can implement a base controller to store common functionality that's shared between the controllers. I wrote a blog post about it recently, here's the code snippet showing how it works:
'use strict';
angular.module('Diary')
// base controller containing common functions for add/edit controllers
.controller('Diary.BaseAddEditController',
['$scope', 'DiaryService',
function ($scope, DiaryService) {
$scope.diaryEntry = {};
$scope.saveDiaryEntry = function () {
DiaryService.SaveDiaryEntry($scope.diaryEntry);
};
// add any other shared functionality here.
}])
.controller('Diary.AddDiaryController',
['$scope', '$controller',
function ($scope, $controller) {
// instantiate base controller
$controller('Diary.BaseAddEditController', { $scope: $scope });
}])
.controller('Diary.EditDiaryController',
['$scope', '$routeParams', 'DiaryService', '$controller',
function ($scope, $routeParams, DiaryService, $controller) {
// instantiate base controller
$controller('Diary.BaseAddEditController', { $scope: $scope });
DiaryService.GetDiaryEntry($routeParams.id).success(function (data) {
$scope.diaryEntry = data;
});
}]);
Using services to cache and share the data across controllers would be the way to go. Since services in angular are singleton, the same copy of data can be shared. A service such as
angular.module('myApp').factory('dataService', function($q, $resource) {
var items=[];
var service={};
service.getItems=function() {
var itemsDefer=$q.defer();
if(items.length >0)
itemsDefer.resolve(data);
else
{
$resource(url).query({},function(data) {
items=data;
itemsDefer.resolve(data)
});
}
return itemsDefer.promise;
}
return service;
});
Now in the controller you can inject the dataService and call the getItems method. This method returns a promise, which is either resolved using the cached data or by making remote request.
And the controller code would look something like
angular.module('myApp').controller('MyCtrl', function($scope,dataService) {
dataService.getItems().then(function(items) {
$scope.items=items;
}
});

Wire up AngularJS controller to express controller

I'm getting started with node/express/Angular by using the MEAN stack at mean.io.
I don't understand how the Angular controller calls the express controller to fetch data.
What I have is public/js/controllers/index.js:
angular.module('mean.system').controller('IndexController', ['$scope', 'Global', 'Tabs',
function ($scope, Global, Tabs) {
$scope.global = Global;
Tabs.query(function(tabs) {
$scope.tabs = tabs;
});
}]);
But I'm confused what exactly 'Tabs' is. I know that somehow, magically, eventually this method is called - I think this is the Express controller?
app/controllers/tabs.js:
exports.all = function(req, res) {
Tab.find().sort('artist').select("-content").populate('user').exec(function(err, tabs) {
if (err) {
res.render('error', {
status: 500
});
} else {
res.jsonp(tabs);
}
});
};
But I don't understand how it gets called. What I want to do is call a different method in app/controllers/tabs.js instead - namely, this:
exports.newest = function(req, res) {
Tab.find().sort('-created').limit(10).select("-content").exec(function(err, tabs) {
...
But I don't understand how to "wire up" the AngularJS controller with the express controller.
i.e. what do I have to do so that I can do something like this in my controller:
angular.module('mean.system').controller('IndexController', ['$scope', 'Global', 'Tabs',
function ($scope, Global, Tabs) {
$scope.global = Global;
Tabs.newest(function(tabs) { // this won't work
$scope.tabs = tabs;
});
}]);
In MEAN, The Articles service is an angular service that returns a $resource object you can usually find in the public/js/services folder.
$resource is a wrapper around $http AJAX service that comes with angularjs, it enables you to connect with RESTful endpoints if your REST service is built in a specific manner.
The connection to the the node.js controller happens using the routes.js object found in the config folder that binds routes to specific module methods.
further reading:
http://docs.angularjs.org/api/ngResource.$resource
http://expressjs.com/api.html#app.VERB

Resources