I have this controller here that calls a service, it works fine, however I needed to call a method that is inside that service in another module. How can I do this?
BaSiderbarService:
(function() {
'use strict';
angular.module('BlurAdmin.theme.components')
.provider('baSidebarService', baSidebarServiceProvider);
/** #ngInject */
function baSidebarServiceProvider() {
var staticMenuItems = [];
this.addStaticItem = function() {
staticMenuItems.push.apply(staticMenuItems, arguments);
};
/** #ngInject */
this.$get = function($state, layoutSizes) {
return new _factory();
function _factory() {
var isMenuCollapsed = shouldMenuBeCollapsed();
this.getMenuItems = function() {
var states = defineMenuItemStates();
var menuItems = states.filter(function(item) {
return item.level == 0;
});
menuItems.forEach(function(item) {
var children = states.filter(function(child) {
return child.level == 1 && child.name.indexOf(item.name) === 0;
});
item.subMenu = children.length ? children : null;
});
return menuItems.concat(staticMenuItems);
};
this.shouldMenuBeCollapsed = shouldMenuBeCollapsed;
this.canSidebarBeHidden = canSidebarBeHidden;
this.setMenuCollapsed = function(isCollapsed) {
isMenuCollapsed = isCollapsed;
};
// I need call this method in another controller
this.getStorage = function(){
console.log(localStorage.user);
}
this.isMenuCollapsed = function() {
return isMenuCollapsed;
};
this.toggleMenuCollapsed = function() {
isMenuCollapsed = !isMenuCollapsed;
};
this.getAllStateRefsRecursive = function(item) {
var result = [];
_iterateSubItems(item);
return result;
function _iterateSubItems(currentItem) {
currentItem.subMenu && currentItem.subMenu.forEach(function(subItem) {
subItem.stateRef && result.push(subItem.stateRef);
_iterateSubItems(subItem);
});
}
};
function defineMenuItemStates() {
return $state.get()
.filter(function(s) {
return s.sidebarMeta;
})
.map(function(s) {
var meta = s.sidebarMeta;
return {
name: s.name,
title: s.title,
level: (s.name.match(/\./g) || []).length,
order: meta.order,
icon: meta.icon,
stateRef: s.name,
};
})
.sort(function(a, b) {
return (a.level - b.level) * 100 + a.order - b.order;
});
}
function shouldMenuBeCollapsed() {
return window.innerWidth <= layoutSizes.resWidthCollapseSidebar;
}
function canSidebarBeHidden() {
return window.innerWidth <= layoutSizes.resWidthHideSidebar;
}
}
};
}
})();
This is the module I need to inject the service and call the method I need DashboardModule:
/**
* #author v.lugovsky
* created on 16.12.2015
*/
(function() {
'use strict';
angular.module('BlurAdmin.pages.dashboard', [])
.config(routeConfig);
/** #ngInject */
function routeConfig($stateProvider) {
// I need call service here
baSidebarService.getStorage()
$stateProvider
.state('dashboard', {
url: '/dashboard',
templateUrl: 'app/pages/dashboard/dashboard.html',
title: 'Dashboard',
resolve: {
user: function(baSidebarService){
console.log('DASHBOARD',baSidebarService.getStorage())
}
},
sidebarMeta: {
icon: 'ion-android-home',
order: 0,
},
});
}
})();
Is there any way to do this? I need to get this method so I can make a logic over that value
Inject the service:
/** #ngInject */
function routeConfig($stateProvider, baSidebarService) {
// I need call service here
baSidebarService.getStorage()
Related
I'm following John Papa's Angular1 styleguide and trying to implement a provider.
The provider as per the document looks like this,
angular
.module('blocks.router')
.provider('routerHelper', routerHelperProvider);
routerHelperProvider.$inject = ['$locationProvider', '$stateProvider', '$urlRouterProvider'];
/* #ngInject */
function routerHelperProvider($locationProvider, $stateProvider, $urlRouterProvider) {
/* jshint validthis:true */
this.$get = RouterHelper;
$locationProvider.html5Mode(true);
RouterHelper.$inject = ['$state'];
/* #ngInject */
function RouterHelper($state) {
var hasOtherwise = false;
var service = {
configureStates: configureStates,
getStates: getStates
};
return service;
///////////////
function configureStates(states, otherwisePath) {
states.forEach(function(state) {
$stateProvider.state(state.state, state.config);
});
if (otherwisePath && !hasOtherwise) {
hasOtherwise = true;
$urlRouterProvider.otherwise(otherwisePath);
}
}
function getStates() { return $state.get(); }
}
}
I've mocked up a very basic version here on plnkr
My provider:
angular
.module('plunker')
.provider('random', function() {
this.$get = helper;
this.getX = function() {
return 10;
}
function helper() {
var provider = {
getX: getX,
getY: getY
}
return provider;
function getX() {
return 10;
}
function getY() {
return 20;
}
}
});
So, getX works fine, but getY is undefined.
Fear, I'm missing something very trivial.
Shouldn't you be adding getY method too?
angular
.module('plunker')
.provider('random', function() {
this.$get = helper;
this.getX = function() {
return 10;
}
this.getY = function() {
return 10;
}
function helper() {
var provider = {
getX: getX,
getY: getY
}
return provider;
// not required after return statement, why you need them?
}
});
It looks like you have a getter for getX but not for getY:
this.getX = function() {
return 10;
}
this.getY = function() {
return 20;
}
Here is a forked version of your plunker:
http://plnkr.co/edit/lHBj6LiirisZ4Bb6DM9M?p=preview
I'm trying to setup a restful API interface via AngularJS with the following code:
'use strict';
(function(angular) {
function ApiAction($resource, ResourceParameters) {
return $resource(ResourceParameters.route,
{ },
{ api_index: {
method: ResourceParameters.method,
isArray: true
}
});
return $resource(ResourceParameters.route,
{ },
{ create: {
method: ResourceParameters.method,
isArray: true
}
}
);
}
function ResourceParameters($scope) {
var factory = {};
factory.method = '';
factory.route = '';
factory.SetMethod = function(method) {
factory.method = method;
}
factory.SetRoute = function(route) {
factory.route = route;
}
return factory;
}
function superheroCtr($scope, ApiAction, ResourceParameters) {
$scope.superheroSubmit = function() {
// ApiAction.create({}, { superhero_name: $scope.superheroName, age: $scope.superheroAge });
angular.forEach($scope.superheroes, function(hero) {
// ApiAction.create({}, { superhero_name: hero.superhero_name, age: hero.age });
});
};
var heroesResources = ResourceParameters($scope).SetRoute('/api/');
var heroes = ApiAction.api_index({}, heroesResources);
$scope.superheroes = [];
heroes.$promise.then(function(data) {
angular.forEach(data, function(item) {
$scope.superheroes.push(item);
});
}, function(data) {
//if error then...
});
$scope.appendSuperheroFields = function() {
var i = $scope.superheroes.length + 1;
$scope.superheroes.push({"id": i, age: "", superhero_name: "" })
}
}
var superheroApp = angular.module('superheroApp', ['ngResource']);
superheroApp.controller('superheroCtr', ['$scope', 'ApiAction', 'ResourceParameters', superheroCtr]);
superheroApp.factory('ResourceParameters', ['$scope', ResourceParameters]);
superheroApp.factory('ApiAction', ['$resource', ResourceParameters, ApiAction]);
})(angular);
Yet, when I run it I get the following error:
Error: [$injector:itkn] Incorrect injection token! Expected service name as string, got function ResourceParameters($scope)
Why is this?
Simply you can not inject $scope OR you can not have access to $scope
inside a factory
Your problem is at this line
superheroApp.factory('ResourceParameters', ['$scope', ResourceParameters]);
You need to replace that line with
superheroApp.factory('ResourceParameters', [ResourceParameters]);
Factory
function ResourceParameters() { //<--removed $scope from here
var factory = {};
factory.method = '';
factory.route = '';
factory.SetMethod = function(method) {
factory.method = method;
}
factory.SetRoute = function(route) {
factory.route = route;
}
return factory;
}
Update
Additionally you should correct the declaration of ApiAction where ResourceParameters should be placed inside ' single qoutes
superheroApp.factory('ApiAction', ['$resource', 'ResourceParameters', ApiAction]);
I am trying to create a tag layout filled with categories, but I am not getting my Authentication because I am trying to resolve that service in my Router.
this is my Router code
(function () {
'use strict';
angular
.module('learningApp')
.config(sslRouter);
// Minification safe dependency Injection
sslRouter.$inject = ['$stateProvider'];
function sslRouter ($stateProvider) {
// SSL Route Definition
$stateProvider.state('ssl', {
parent: 'policy',
url: '/ssl',
data: {
roles: ['USER']
},
views: {
'policyConfig': {
templateUrl: 'components/configuration/service/policy/ssl/ssl.tpl.html',
controller: 'SSL'
}
},
resolve: {
'sslServiceData': function(sslService) {
return sslService.promise;
}
}
});
}
}());
This is my Service
(function() {
'use strict';
angular
.module('learningApp')
.factory('sslService', sslResource);
sslResource.$inject = ['Principal', '$resource', 'BASE_URL', 'exDomainService'];
function sslResource (Principal, $resource, BASE_URL, exDomainService) {
debugger;
var res = $resource(BASE_URL + '/api/companies/' + Principal.company() + '/sconfig/ssl/sslConfiguration', {}, {
query: {
method: 'GET',
isArray: false
},
update: {
method: 'PUT'
}
});
var data = {};
var servicePromise = _initService();
servicePromise.$promise.then(function (d) {
data = d;
if (!data.excludedCategories) {
data.excludedCategories = [];
}
if (!data.excludedDomains) {
data.excludedDomains = [];
}
exDomainService.tableData = getExcludedDomains();
});
function _initService () {
return res.query();
}
return {
promise: servicePromise,
rest: res
}
}
}());
This is my controller
(function() {
'use strict';
angular
.module('learningApp')
.controller('SSL', SSLController);
SSLController.$inject = ['$scope', 'sslService', 'preDefinedCategoryService', '$timeout', 'exDialog', 'exDomainService'];
function SSLController ($scope, sslService, preDefinedCategoryService, $timeout, exDialog, exDomainService) {
var vm = $scope;
/**
* #desc Flags for different type checks
* Booleans and Categories
*/
vm.flags = {
// By default true
enableInspectSSLTraffic: sslService.getSSlInspectionFlag(),
allowUntrustedCertificates: sslService.getUntrustedCertificatesFlag(),
allowHostnameMismatch: sslService.getHostnameMismatchFlag(),
selectedCategory: undefined,
initializing: true
};
vm.excludedCategories = sslService.getExcludedCategories();
vm.predefinedCategories = preDefinedCategoryService.rest.query();
vm.predefinedCategories.$promise.then(function() {
vm.categories = _processedCategories(vm.predefinedCategories, vm.excludedCategories);
});
}
}());
So basically problem is, I am getting Principal.Identity as undefined, but if I remove resolution from Router, I got identity but then I lose my data coming from service. I want my service to be loaded completely before its Controller, and I want my principal service to be loaded before service.
for Reference, This is my Principal Class
'use strict';
angular.module('learningApp')
.service('Principal',['$q', 'Account', 'localStorageService', function Principal($q, Account, localStorageService) {
var _identity,
_authenticated = false;
return {
isIdentityResolved: function () {
return angular.isDefined(_identity);
},
isAuthenticated: function () {
return _authenticated;
},
isInRole: function (role) {
if (!_authenticated || !_identity || !_identity.roles) {
return false;
}
return _identity.roles.indexOf(role) !== -1;
},
isInAnyRole: function (roles) {
if (!_authenticated || !_identity.roles) {
return false;
}
for (var i = 0; i < roles.length; i++) {
if (this.isInRole(roles[i])) {
return true;
}
}
return false;
},
company: function () {
debugger;
if (_identity) return _identity.companyId;
},
authenticate: function (identity) {
_identity = identity;
_authenticated = identity !== null;
},
identity: function (force) {
var deferred = $q.defer();
if (force === true) {
_identity = undefined;
}
// check and see if we have retrieved the identity data from the server.
// if we have, reuse it by immediately resolving
if (angular.isDefined(_identity)) {
deferred.resolve(_identity);
return deferred.promise;
}
// rather than retrieving from server, use cookie or whatever method
var cookieFound = UTIL.cookie("token");
if (cookieFound) {
var response = JSON.parse(JSON.parse(cookieFound));
var expiredAt = new Date();
expiredAt.setSeconds(expiredAt.getSeconds() + response.expires_in);
response.expires_at = expiredAt.getTime();
localStorageService.set('token', response);
}
// retrieve the identity data from the server, update the identity object, and then resolve.
Account.get().$promise
.then(function (account) {
account.data.roles = ["ADMIN", 'USER'];
account.data.langKey = "en";
_identity = account.data;
_authenticated = true;
deferred.resolve(_identity);
})
.catch(function() {
_identity = null;
_authenticated = false;
deferred.resolve(_identity);
});
return deferred.promise;
}
};
}]);
I have the following code for a directive using a separated controller with the "controller as" syntax:
'use strict';
angular.module('directives.featuredTable', [])
.controller('FeaturedTableCtrl',
['$scope',
function ($scope){
var controller = this;
controller.activePage = 1;
controller.changePaginationCallback =
controller.changePaginationCallback || function(){};
controller.density = 10;
controller.itemsArray = controller.itemsArray || [];
controller.metadataArray = controller.metadataArray || [];
controller.numberOfItems = controller.numberOfItems || 0;
controller.numberOfPages = 1;
controller.options = controller.options || {
'pagination': false
};
controller.changePaginationDensity = function(){
controller.activePage = 1;
controller.numberOfPages =
computeNumberOfPages(controller.numberOfItems, controller.density);
controller.changePaginationCallback({
'page': controller.activePage,
'perPage': controller.density
});
};
controller.getProperty = function(object, propertyName) {
var parts = propertyName.split('.');
for (var i = 0 ; i < parts.length; i++){
object = object[parts[i]];
}
return object;
};
controller.setActivePage = function(newActivePage){
if(newActivePage !== controller.activePage &&
newActivePage >= 1 && newActivePage <= controller.numberOfPages){
controller.activePage = newActivePage;
controller.changePaginationCallback({
'page': controller.activePage,
'perPage': controller.density
});
}
};
initialize();
$scope.$watch(function () {
return controller.numberOfItems;
}, function () {
controller.numberOfPages =
computeNumberOfPages(controller.numberOfItems, controller.density);
});
function computeNumberOfPages(numberOfItems, density){
var ceilPage = Math.ceil(numberOfItems / density);
return ceilPage !== 0 ? ceilPage : 1;
}
function initialize(){
if(controller.options.pagination){
console.log('paginate');
controller.changePaginationCallback({
'page': controller.activePage,
'perPage': controller.density
});
}
}
}]
)
.directive('featuredTable', [function() {
return {
'restrict': 'E',
'scope': {
'metadataArray': '=',
'itemsArray': '=',
'options': '=',
'numberOfItems': '=',
'changePaginationCallback': '&'
},
'controller': 'FeaturedTableCtrl',
'bindToController': true,
'controllerAs': 'featuredTable',
'templateUrl': 'directives/featuredTable/featuredTable.tpl.html'
};
}]);
You can see at the beginning of the controller that I'm initializing its properties with the attributes passed by the directive or providing default values:
controller.activePage = 1;
controller.changePaginationCallback =
controller.changePaginationCallback || function(){};
controller.density = 10;
controller.itemsArray = controller.itemsArray || [];
controller.metadataArray = controller.metadataArray || [];
controller.numberOfItems = controller.numberOfItems || 0;
controller.numberOfPages = 1;
controller.options = controller.options || {
'pagination': false
};
At the end I'm executing the initialize(); function that will execute the callback according to the options:
function initialize(){
if(controller.options.pagination){
controller.changePaginationCallback({
'page': controller.activePage,
'perPage': controller.density
});
}
}
I'm now trying to unit test this controller (with karma and jasmine) and I need to "simulate" the parameters passed by the directive, I tried the following:
'use strict';
describe('Controller: featured table', function () {
beforeEach(module('directives.featuredTable'));
var scope;
var featuredTable;
var createCtrlFn;
beforeEach(inject(function ($controller, $rootScope) {
scope = $rootScope.$new();
createCtrlFn = function(){
featuredTable = $controller('FeaturedTableCtrl', {
'$scope': scope
});
scope.$digest();
};
}));
it('should initialize controller', function () {
createCtrlFn();
expect(featuredTable.activePage).toEqual(1);
expect(featuredTable.changePaginationCallback)
.toEqual(jasmine.any(Function));
expect(featuredTable.density).toEqual(10);
expect(featuredTable.itemsArray).toEqual([]);
expect(featuredTable.metadataArray).toEqual([]);
expect(featuredTable.numberOfPages).toEqual(1);
expect(featuredTable.numberOfItems).toEqual(0);
expect(featuredTable.options).toEqual({
'pagination': false
});
});
it('should initialize controller with pagination', function () {
scope.changePaginationCallback = function(){};
spyOn(scope, 'changePaginationCallback').and.callThrough();
scope.options = {
'pagination': true
};
createCtrlFn();
expect(featuredTable.activePage).toEqual(1);
expect(featuredTable.changePaginationCallback)
.toEqual(jasmine.any(Function));
expect(featuredTable.density).toEqual(10);
expect(featuredTable.itemsArray).toEqual([]);
expect(featuredTable.metadataArray).toEqual([]);
expect(featuredTable.numberOfPages).toEqual(1);
expect(featuredTable.numberOfItems).toEqual(0);
expect(featuredTable.options).toEqual({
'pagination': true
});
expect(featuredTable.changePaginationCallback).toHaveBeenCalledWith({
'page': 1,
'perPage': 10
});
});
});
And got the following error, meaning that scope is not well initialized:
Expected Object({ pagination: false }) to equal Object({ pagination: true })
at test/spec/app/rightPanel/readView/historyTab/historyTab.controller.spec.js:56
Simulating the bindings would be non-trivial - after all, it's hard to really know what compiling and linking a directive does with the data passed to it...unless you just do it yourself!
The angular.js documentation offers a guide on how to compile and link a directive for unit testing - https://docs.angularjs.org/guide/unit-testing#testing-directives. After doing that, you'd just need to get the controller from the resulting element(see the documentation for the controller() method here - https://docs.angularjs.org/api/ng/function/angular.element) and perform your tests. ControllerAs would be irrelevant here - you would be testing the controller directly, instead of manipulating the scope.
Here's an example module:
var app = angular.module('plunker', []);
app.controller('FooCtrl', function($scope) {
var ctrl = this;
ctrl.concatFoo = function () {
return ctrl.foo + ' world'
}
})
app.directive('foo', function () {
return {
scope: {
foo: '#'
},
controller: 'FooCtrl',
controllerAs: 'blah',
bindToController: true,
}
})
And test setup:
describe('Testing a Hello World controller', function() {
ctrl = null;
//you need to indicate your module in a test
beforeEach(module('plunker'));
beforeEach(inject(function($rootScope, $compile) {
var $scope = $rootScope.$new();
var template = '<div foo="hello"></div>'
var element = $compile(template)($scope)
ctrl = element.controller('foo')
}));
it('should produce hello world', function() {
expect(ctrl.concatFoo()).toEqual('hello world')
});
});
(Live demo: http://plnkr.co/edit/xoGv9q2vkmilHKAKCwFJ?p=preview)
I have a angular service like this:
videoApp.factory('cacheLoader', function ($http, $filter, $rootScope) {
this.self = this;
return {
load: function (url, allowCache) {
if(allowCache == false || localStorage.getItem(url) && (parseInt(localStorage.getItem(url + 'time')) + 20000) < (new Date().getTime()) || (!localStorage.getItem(url) )) {
$http.get(url).success(function (data) {
$rootScope.allData = data;
$rootScope.videos = data;
$rootScope.categories = $filter('categoryFilter')(data);
if(allowCache == true && parseInt(localStorage.getItem(url + 'time')) + 20000 < (new Date().getTime() )) {
localStorage.setItem(url, JSON.stringify(data));
localStorage.setItem(url + 'time', new Date().getTime());
}
});
} else {
$rootScope.allData = JSON.parse(localStorage.getItem(url));
$rootScope.videos = JSON.parse(localStorage.getItem(url));
$rootScope.categories = $filter('categoryFilter')(JSON.parse(localStorage.getItem(url)));
}
},
getFilteredResults: function (category, data, callback) {
callback = callback || $filter('articleFilter');
$rootScope.videos = callback(category, data);
return $rootScope.videos;
}
};
});
Now, what I want to use cacheLoader.getFilteredResults inside cacheLoader.load function. this is a window object, so I can't point on it, I tried to bind this to the whole module pattern, but it throw error.
Is there any way how to accomplish what I'm trying to?
You should be able to use this.getFilteredResults inside the load method:
videoApp.factory('cacheLoader', function ($http, $filter, $rootScope) {
this.self = this;
return {
load: function () {
this.getFilteredResults();
},
getFilteredResults: function () {
alert('hi');
}
};
});
And if you need to handle referring to this inside a callback, then do something like:
return {
load: function () {
var that = this;
doSomeThing().then(function(data) {
that.getFilteredResults();
});
},
getFilteredResults: function () {
alert('hi');
}
};