Im trying to create a custom service in angular 2 but i can't seem to find any documentation on angular 2 services in es5 (which is what im writing my code in) i've tried using this
(function(app){
app.database=ng.core.Injectable().Class({
constructor:[function(){
this.value="hello world";
}],
get:function(){return this.value},
});
ng.core.Injector.resolveAndCreate([app.database]);
ng.core.provide(app.database,{useClass:app.database});
})(window.app||(window.app={}));
however when i inject it into my component it throws the error no provider for class0 (class10 -> class0) i can't seem to figure out how to create the custom service. does anyone know how to do this in es5?
Here is a complete sample of dependency injection with ES5. (service into component, service into service). Don't forget to specify your service when bootstrapping your application or within the providers attribute of components.
var OtherService = function() {};
OtherService.prototype.test = function() {
return 'hello';
};
var Service = ng.core.Injectable().Class({
constructor: [ OtherService, function (service) {
this.service = service;
}],
test: function() {
return this.service.test();
}
});
var AppComponent = ng.core
.Component({
selector: 'my-app',
template: '<div>Test: {{message}}</div>',
})
.Class({
constructor: [Service, function (service) {
this.message = service.test();
}],
});
document.addEventListener('DOMContentLoaded', function () {
ng.platform.browser.bootstrap(AppComponent, [
OtherService, Service
]);
});
In your case, I think that your forgot to add app.database in providers. Something like:
document.addEventListener('DOMContentLoaded', function () {
ng.platform.browser.bootstrap(AppComponent, [
app.database
]);
});
You could also have a look at this question:
Dependency Injection in Angular 2 with ES5
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 service that looks like that:
angular.module('app').service('MyService' , function (dependency1, dependency2, dependency3 ...) {
function funcToTest() {
// Do something
}
}
How could I inject a specific dependency to the service? For example, I want to inject only dependency2 to my service and I don't care about the other dependencies.
Unlike unit-testing Angular controllers, we have no way of directly passing dependencies. This is where $provide service comes to rescue!
Here's a sample example:
beforeEach(module('myApp', function ($provide) {
mockDependecy2 = {
mockFunction: function() {}
};
$provide.value('dependency2', mockDependecy2);
}));
Then, you can write your specs normally:
beforeEach(inject(function(_MyService_, ...) {
...
MyService = _MyService_;
}));
describe("...", function() {
it("...", function() {
MyService.funcToTest();
// write expect statements here
})
})
As seen in the example, you can (optionally) enclose them with underscores which are ignored by the injector when the reference name is resolved.
This will automatically inject dependency2 in your service wherever it is used
var dependency2;
beforeEach(function () {
inject(function (dependency2){
dependency2 = dependency2;
});
}
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/
I have a simple service implemented like this
sameRoof
.factory('dbService', function (localStorageService, backendUpdate) {
return {
checkProfileAndFlat: function () {
return (localStorageService.get('profile') && localStorageService.get('flatshare'));
}
};
});
LocalStorage are ngModules installed with bower.
I am writint unit test
'use strict';
describe('Service: service taking care of asking the local database', function () {
var localStorageService;
var fakeDB = {'profile' : 'testProfile', 'flatshare' : 'flatshare'};
// load the service's module
beforeEach(module('frontApp'));
// instantiate service
var dbService;
beforeEach(inject(function (_dbService_, _localStorageService_) {
dbService = _dbService_;
localStorageService = _localStorageService_;
//mock localStorageService get/add
spyOn(localStorageService,'get').andCallFake(function(key){
return fakeDB[key];
});
}));
it('should check profile and flatshare', function () {
console.log(localStorageService.get('profile'));
expect( dbService.checkProfileAndFlat() ).toBe(false);
});
});
but i am having problems here,
TypeError: 'undefined' is not a function (evaluating 'spyOn ...)
seems like i am implementing in wrong way the spyOn
the answer is
//mock localStorageService get/add
spyOn(localStorageService,'get').and.callFake(function(key){
return fakeDB[key];
});
as i am using jasmine 2.3.4 and jasmine API has changed compared to the one 1.3
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);
});
});