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
Related
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()
I'm trying to figure out how to mock an angular provider for a unit test. In the following snippet I have a 'translate' provider that is used to determine which language will be displayed in a view by default. I'd like to inject a different version of this provider into my tests to ensure my app displays the right thing based on a provider's settings. What I'm doing right now clearly doesn't seem to be working. Thanks in advance for your help.
By the way, if you're wondering why a provider was used instead of something else like a service or a simple value, this is a contrived example that distills a problem I'm having in a larger application. I need to inject something into an application's config method, which means I need to mock a provider.
var app = angular.module('app', []);
app.config(function($provide) {
$provide.provider('translate', function() {
return {
$get: function() {
return {
language: 'en'
};
}
};
});
});
app.controller('ctl', function($scope, translate) {
if (translate.language === 'en') {
$scope.greeting = "Welcome to the application.";
} else {
$scope.greeting = "Velkommen til appen.";
}
});
// ---SPECS-------------------------
describe('App', function() {
beforeEach(angular.mock.module('app'));
describe('by default', function() {
beforeEach(angular.mock.inject(
function(_$compile_, _$rootScope_) {
const viewHtml = $('#view');
$compile = _$compile_;
$rootScope = _$rootScope_;
$rootScope.isOn = false;
elm = $(viewHtml);
$compile(elm)($rootScope);
$rootScope.$digest();
}));
it('shows English', function() {
expect(elm.text()).toMatch(/Welcome/);
});
});
describe('without English specified', function() {
beforeEach(angular.mock.module('app', function ($provide) {
$provide.provider('translate', function () {
return {
$get: function () {
return { preferredLanguage: 'no' };
}
};
});
}));
beforeEach(angular.mock.inject(
function(_$compile_, _$rootScope_) {
const viewHtml = $('#view');
$compile = _$compile_;
$rootScope = _$rootScope_;
$rootScope.isOn = false;
elm = $(viewHtml);
$compile(elm)($rootScope);
$rootScope.$digest();
}));
it('shows Norwegian', function() {
expect(elm.text()).toMatch(/Velkommen/);
});
});
});
// --- Runner -------------------------
(function() {
var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 1000;
var htmlReporter = new jasmine.HtmlReporter();
jasmineEnv.addReporter(htmlReporter);
jasmineEnv.specFilter = function(spec) {
return htmlReporter.specFilter(spec);
};
var currentWindowOnload = window.onload;
window.onload = function() {
if (currentWindowOnload) {
currentWindowOnload();
}
execJasmine();
};
function execJasmine() {
jasmineEnv.execute();
}
})();
<link href="http://jasmine.github.io/1.3/lib/jasmine.css" rel="stylesheet"/>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="http://code.angularjs.org/1.4.9/angular.js"></script>
<script src="http://code.angularjs.org/1.4.9/angular-mocks.js"></script>
<script src="http://jasmine.github.io/1.3/lib/jasmine.js"></script>
<script src="http://jasmine.github.io/1.3/lib/jasmine-html.js"></script>
<div ng-app="app">
<div id="view" ng-controller="ctl">{{greeting}}</div>
</div>
you can do it like this: -
beforeEach(module('app', function ($provide) {
$provide.provider('translate', function() {
return {
$get: function() {
return {
language: 'fr'
};
}
};
});
}));
you can also put the above code in a util method, that will take the language code as a parameter, so you don't spread above code everywhere.
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...
var app = angular.module('plunker', []);
TestController = function ($scope, $interval) {
$scope.test = this;
$scope.name = 'World';
this.init();
$interval(function () {
//Added this because we shouldn't update until we either get the user data or that request fails
$scope.test.init();
}, 500);
};
TestController.prototype.init = function () {
console.log("Test this thing");
};
app.controller('MainCtrl', TestController);
This works great but now I need to include the init function in another controller so I want both to inherit from a common prototype. However, when I try this plunker it doesn't seem to work.
What is the proper way to handle this sort of thing in JS?
It is TypeScript example http://plnkr.co/edit/HOeJ7nkkp2qRl53zEvVj?p=preview
TypeScript:
class Controller {
static $inject = ['$scope']; // deps
constructor($scope) {
$scope.vm = this;
}
value(): string {
return '123';
}
}
class NewController extends Controller {
constructor($scope) {
super($scope);
}
value(): string {
return super.value() + '456';
}
}
declare var angular: any;
angular.module('my', []).controller('controller', NewController);
In JavaScript it looks like:
//Compiled TypeScript
var __extends = this.__extends || function (d, b) {
function __() { this.constructor = d; }
__.prototype = b.prototype;
d.prototype = new __();
}
var Controller = (function () {
function Controller($scope) {
$scope.vm = this;
}
Controller.$inject = [
'$scope'
];
Controller.prototype.value = function () {
return '123';
};
return Controller;
})();
var NewController = (function (_super) {
__extends(NewController, _super);
function NewController($scope) {
_super.call(this, $scope);
}
NewController.prototype.value = function () {
return _super.prototype.value.call(this) + '456';
};
return NewController;
})(Controller);
angular.module('my', []).controller('controller', NewController);
If you need OOP in JavaScript use smth like http://prototypejs.org/