I am new to angular js, so bear with me.
First what I am trying to do is have my angular app be divided into modules with two common services going between them, one which will hold the config, and another api-service to communicate with the backend server. Also most of the config, like theme, user permissions, e.t.c I wish to fetch from the server instead of coding it right within the app.
I am using https://github.com/keshavos/generator-angularjs-cordova as a basis for my app.
To test things out, I wrote(generated) two services,
/app/modules/core/services/api-service.js
'use strict';
angular
.module('core')
.service('ApiService', [
function() {
//alert(SessionConfig.getConst("SERVER_URL"));
this.config = {
'site':'http://localhost:1337'
}
this.getConfig = function() {
return this.config;
};
}
]);
The session config is supposed to contain the actual configurations
/app/modules/core/services/session-config.js
'use strict';
angular
.module('core')
.service('SessionConfig', [
function() {
this.config_const = {
"SERVER_URL" : "http://localhost:1337",
};
this.config_vars = {
"user" : {},
"theme": "default"
};
this.getConst = function() {
return true;
};
}
]);
Then I try to include them in the home controller like
/app/modules/core/controllers/home.js
'use strict';
angular
.module('core')
.config(['$sailsProvider', function ($sailsProvider) {
$sailsProvider.url = 'http://localhost:1337';
}])
.controller('HomeController', ['$scope','$sails', 'SessionConfig',
function($scope,$sails,SessionConfig) {
The above throws error Error: [$injector:unpr] Unknown provider: SessionConfigProvider <- SessionConfig
However when I try including the ApiService in a similar way, it works ok
])
.controller('HomeController', ['$scope','$sails', 'ApiService',
function($scope,$sails,ApiService) {
What am I doing wrong?
Try abstract your services into separate modules, then inject those into your app. So you could have
var apiService = angular.module('apiService', []);
apiService.factory('$apiService', function() {
return
{
someFunction: function() {
//do some stuff
}
}
});
Do the same for your SessionConfig
var sessionConfig = angular.module('sessionConfig', []);
sessionConfig.factory('$sessionConfig', function() {
return
{
someFunction: function() {
//do some stuff
}
}
});
Then when you initialise your app you can go
var coreApp = angular.module('coreApp', ['apiService', 'sessionConfig']);
Then your controller
coreApp.controller('myCtrl', function($apiService, $sessionConfig) {
$apiService.someFunction();
$sessionConfig.someFunction();
});
The services essentially return an object on which you call the methods you defined within them, using dot notation. It's essentially what you have, this is just how I would go about organising it as I personally find it a bit clearer!
Hope that helps somewhat!
Edit to show keeping both services in same module
var app = angular.module('app', []);
app.factory('$serviceOne', function(){
return {
serviceOneFunction: function() {
//do something
}
}
});
app.factory('$serviceTwo', function(){
return {
serviceTwoFunction: function() {
//do something
}
}
});
app.controller('myCtrl', function($serviceOne, $serviceTwo) {
$serviceOne.serviceOneFunction();
$serviceTwo.serviceTwoFunction();
});
Here's a working fiddle of what your trying to implement
http://jsfiddle.net/HB7LU/19285/
Related
I am working on a application originally created with backbone and jQuery, however due to client requirement, new modules are built with angular. Routing of the application is handled with backbone route and we have successfully integrated angular modules.
The actual problem is, I need to retrieve the current instance of a module in angular and execute a function from the controller of that module based on actions handled by a backbone controller.
Here is what my angular module and controller looks like:
//In chat.module.js
( function () {
angular
.module( 'chat.module', [] );
})();
//In chat.controller.js
(function () {
angular
.module('chat.module')
.controller('chat.controller', ['profileFactory', '$filter', '$q', '$timeout', 'Position', 'Chat', chat]);
function chat(profileFactory, $filter, $q, $timeout, Position, Chat) {
var vm = this;
vm.initChatFlag = false;
vm.initChat = initChat;
vm.setInformation = setInformation;
function setInformation() {
//handle business logic here
}
...
In backbone, the module is created as follows:
chatmodule: function () {
var self = this;
var element = angular.element(document.querySelector('#modalCallback'));
var chat = angular.element(document.querySelector('#chatModule'));
var isInitializedChat = chat.injector();
var isInitialized = element.injector();
if (!isInitialized) {
angular.bootstrap($('#modalCallback'), ['app']);
}
if (!isInitializedChat) {
angular.bootstrap($('#chatModule'), ['app']);
}
//TODO: chat.controller.setInformation() get access to fields like chat.controller.initChatFlag etc
The main app module is defined thus:
(function(){
angular
.module('app',[
'callback',
'ui.bootstrap',
'720kb.datepicker',
'ngLocale',
'directives.module',
'interceptor',
'directive.loading',
'angularUtils.directives.dirPagination',
'blog.module',
'profile.module',
'filters.module',
'chat.module',
'ui.toggle',
]);
})();
The AngularJS $injector is where a lot of the magic happens, so if you expose that outside of the AngularJS code you can hook it up to non-AngularJS code like the following:
//A simple AngularJS service:
app.service('myService', function() {
this.message = "This is my default message.";
});
//Expose the injector outside the angular app.
app.run(function($injector, $window) {
$window.angularInjector = $injector;
});
//Then use the injector to get access to the service.
//Make sure to wrap the code in a `$apply()` so an
//AngularJS digest cycle will run
function nonAngularEventHandler() {
angularInjector.invoke(function(myService, $rootScope) {
$rootScope.$apply(function() {
myService.message = "Now this is my message."
});
});
}
Edit: Alternatively, simplify the call like so.
//Instead of exposing the $injector directly, wrap it in a function
//which will do the $apply() for you.
app.run(function($injector, $window, $rootScope) {
$window.callInMyAngularApp = function(func) {
$rootScope.$apply(function() {
$injector.invoke(func);
});
}
});
//Then call that function with an injectable function like so.
function nonAngularClick() {
callInMyAngularApp(function(myService) {
myService.message = "Now this is my message."
});
}
//And remember if you're minifying, you'll want the minify-safe
//version of the injectable function like this
function nonAngularClick() {
callInMyAngularApp(['myService', function(myService) {
myService.message = "Now this is my message."
}]);
}
Update: (last one I promise!)
The above will work fine, but you might want to consider exposing a well-defined API instead of a generic injectable interface. Consider the following.
//Now I have a limited API defined in a service
app.service("myExternalApi", function($rootScope, myService) {
this.changeMyMessage = function(message) {
$rootScope.$apply(function() {
myService.message = message;
});
};
});
//And I just expose that API
app.run(function($window, myExternalApi) {
$window.myExternalApi = myExternalApi;
});
//And the call from outside of angular is much cleaner.
function nonAngularClick() {
myExternalApi.changeMyMessage("Now this is my message.");
}
I was able to get access to the controller using answer from this post - https://stackoverflow.com/a/21997129/7411342
var Chat = angular.element(document.querySelector('#chatModule')).scope();
if(!Chat) return;
if(Chat.chatCtrl.initChatFlag) {
Chat.chatCtrl.setInformation();
}else{
console.log('Chat has not been initialized');
}
I have created notification factory and pass inside controller,Inside controller when assign the factory to the scope getting error.
alertsManager
MyApp.factory('alertsManager', function() {
return {
alerts: {},
addAlert: function(message, type) {
this.alerts[type] = this.alerts[type] || [];
this.alerts[type].push(message);
},
clearAlerts: function() {
for(var x in this.alerts) {
delete this.alerts[x];
}
}
};
});
var LoginController = function($scope,$rootScope,alerts,alertsManager)
{
$scope.alerts = alertsManager.alerts;
// getting error.
**angular.js:11594 TypeError: Cannot read property 'alerts' of undefined**
}
LoginController.$inject = ['$scope', '$rootScope','alerts','alertsManager'];
**why factory not able to access inside controller.*
Try something like below .
code:
var myApp = angular.module('myApp', []);
myApp.factory('alertsManager', function() {
return {
alerts: {'alert':"i'm from factory service"},
addAlert: function() { //code },
clearAlerts: function() { //code }
}
});
myApp.controller('MyCtrl',['$scope','alertsManager', function($scope, alertsManager) {
$scope.test = alertsManager.alerts.alert;
}]);
Note : Inject factory service into Controller
working sample here .
No need to inject 'alerts' as a dependency in controller.
Sorry ..very stupid question .. Are you sure Do you include these files in Index.html?
like this:
<script src="app/services/alertsManager.js"></script>
I'm trying to import JSON data into an angularJS application. I split my app into a controller and the import-service, but both in different files. I'm also using bower, grunt and yeoman (that's due to work, I'm not quite used to these, maybe there's also a problem.)
The strange behavior is:
I wanted to retrieve the JSON data with a $http.get() and resolve it - all within a service, so that I can hand out the data object from there to the main controller and won't have to resolve it there.
Strangely, I didn't get any data, it was empty or not readable. Then I handed out the promise which I the $http.get() mehtod gives back and resolved it in the controller. That's not what I wanted, but now it works.... but why?
I guess it's a schmall misstake somehwere but neither me nor my team members can find one. Strangely, doing a little test-app without grunt, yeoman and bower it worked.
I'd appreciate every hint or idea...
Jana
Here's my code from the NOT working version, first the main module with controller:
/** Main module of the application. */
(function () {
'use strict;'
angular.module('angularRegelwerkApp', [])
.controller('RegelwerkCtrl', function ($scope, CategoryFactory) {
$scope.categories = CategoryFactory.getCategories();
$scope.subcategories = CategoryFactory.getSubCategories();
}
);
})();
Service-part:
(function () {
'use strict';
var app = angular.module('angularRegelwerkApp')
.service('CategoryFactory',
function ($http) {
var categories = [];
var subcategories = [];
$http.get("../mockdata/categories.json").then(function (response) {
categories = response.data;
})
$http.get('../mockdata/subcategories.json').then(function (response) {
subcategories = response.data;
})
return {
getCategories: function(){
return categories;
},
getSubCategories: function(){
return subcategories;
}
}
}
);
})();
Here's my code from the WORKING version, first the main module with controller:
/** Main module of the application. */
(function() {
'use strict;'
angular.module('angularRegelwerkApp', [])
.controller('RegelwerkCtrl', function ($scope, CategoryFactory) {
$scope.categories = [];
$scope.subcategories = [];
CategoryFactory.getCategories().then(function(response) {
$scope.categories = response.data;
});
CategoryFactory.getSubCategories().then(function(response) {
$scope.subcategories = response.data;
});
}
);
}
)();
Service-part:
(function () {
'use strict';
var app = angular.module('angularRegelwerkApp')
.service('CategoryFactory',
function ($http, $q) {
var categoryURL = "../mockdata/categories.json";
var subcategoryURL = '../mockdata/subcategories.json';
function getSubCategories() {
return $http.get(subcategoryURL);
}
function getCategories() {
return $http.get(categoryURL);
}
return {
getCategories: getCategories,
getSubCategories: getSubCategories
}
}
);
})();
This is destroying your reference, so loop over the data from the server and push it into the variables you need:
$http.get("../mockdata/categories.json").then(function (response) {
for(var x = 0; x < response.data.length; x++){
categories.push(response.data[x]);
}
});
$http call is by default asynchronous.
So in your first version, when you write like $scope.categories = CategoryFactory.getCategories();
you get empty categories, since by the time you access categories, it may not have been loaded with response data.
your app flows like this -
you load the controller
you call the service
service calls $http
you try to access categories (but data will not be available until response is returned from server)
$http.then loads data to $scope.categories
You are storing your data in Angular primitives and these don't update. instead store all your data in an object and it shoudl work (you'll also need to update controller)
(function () {
'use strict';
var app = angular.module('angularRegelwerkApp')
.service('CategoryFactory',
function ($http) {
var data = {};
$http.get("../mockdata/categories.json").then(function (response) {
data.categories = response.data;
})
$http.get('../mockdata/subcategories.json').then(function (response) {
data.subcategories = response.data;
})
return {
getCategories: function(){
return data.categories;
},
getSubCategories: function(){
return data.subcategories;
}
}
}
);
})();
I found many similar questions but I couldn't apply the solutions to my problem.
So in my angular app I am drawing nvd3 charts.
I am doing a get request in the service and I can see from the network in my browser that I am ALWAYS getting the chart data as I am supposed to.
The problem is that, when I am running grunt serve to start my angular app, I am still getting the data through the api, but for some reason they are not shown.
That just happens only when I run grunt serve. However, if I hit refresh, after running grunt serve, the data are shown correctly.
Thanks in advance for any help.
this is what I am trying to do:
'use strict';
angular.module('myApp')
.service('mainService', function($http) {
this.getData = function() {
return $http({
method: 'GET',
url: '/rest/api/config',
});
}
})
.controller('MainCtrl', function($scope, $http, mainService) {
var name = 'main';
var model;
mainService.getData().then(function(d) {
model = d.data;
$scope.modelling();
});
$scope.modelling = function() {
if(!model) {
console.log("no model");
// set default model for demo purposes
model = {
title: "about",
structure: "12/4-4-4",
};
}
console.log(model);
$scope.name = name;
$scope.model = model;
$scope.collapsible = true;
}
});
Try something like this. Initially, in your example, $scope.model is going to be undefined.
.controller('MainCtrl', function($scope, $http, mainService) {
var name = 'main';
mainService.getData().then(function(d) {
$scope.modelling(d.data);
});
$scope.modelling = function(data) {
//do something with data here, or set
$scope.model = data;
}
$scope.name = name;
$scope.collapsible = true;
}
});
That might work, depends on how you set up the nvd3 charts.
I am using an AngularJS service in my app. I've learned that the service I've created is unreliable. For that reason, I want to build some unit tests around it. While the service works fine in my AngularJS app, I'm having problems getting it to work with Jasmine. Its like I can't load any modules. I've strip down the code to the bare bones. I do not understand what I'm doing wrong. My code looks like this:
myService.js
'use strict';
angular.module('customModule', [])
.factory('$serviceName', [function () {
return {
isAvailable: function () {
return true;
}
};
}]
);
myService.spec.js
describe('customModule', function() {
beforeEach(function() {
console.log('loading module...');
module('customModule');
});
describe('$serviceName', function () {
var myService = null;
beforeEach(inject(function ($serviceName) {
console.log('loading service...');
myService = $serviceName;
}));
it('should work', function () {
console.log('testing');
var isAvailable = myService.isAvailable();
console.log(isAvailable);
expect(1 + 2).toEqual(3);
});
});
})
gruntfile.js
'use strict';
module.exports = function (grunt) {
grunt.initConfig({
jasmine: {
unitTests: {
src: 'test/*.js',
}
}
});
require('load-grunt-tasks')(grunt);
grunt.registerTask('default', ['jasmine:unitTests']);
};
My jasmine tests are running. However, its like myService.js isn't being loaded. I'm not sure how to do that. I'm also not sure how to get 'angular' (used in myService.js) in the tests either.
Thank you for your help.
Do you load you Angular application in myService.spec.js? Otherwise $serviceName is not available for dependency injection.
You should add something like this in your test file:
beforeEach(module('angularApp'));
(of course replace angularApp with the name you are using)
And it is also a good idea to use another beforeEach block to handle the dependency injection so you can use it in all tests in myService.spec.js. In total you should have something like this:
describe('serviceName', function () {
beforeEach(module('angularApp'));
var iMyService
// Initialize the service
beforeEach(inject(function ($serviceName) {
iMyService = $serviceName;
}));
it('should check if available', function () {
var isAvailable = iMyService.isAvailable();
console.log(isAvailable);
expect(1 + 2).toEqual(3);
});
});