Configure IdleProvider and KeepaliveProvider from properties file - angularjs

I"m very new to Angular and I'm having problems configuring IdleProvider and KeepaliveProviders. Please understand the question: I've already configured those two providers correctly and my idle timeout is working. What I'm looking for is how to provide values to those providers by reading such values from a properties file. I've been able to read values from a properties file but I'm unable to pass them to the providers in my .config() method.
I'm using angular 1.4.3 (pls don't ask me to upgrade - I just joined a project where they are using this.
Here's my config method in route.js
define(['app'], function(app) {
'use strict';
return app
.config(function($stateProvider, $urlRouterProvider, $httpProvider, KeepaliveProvider, IdleProvider) {
console.log("Idle timer is here")
IdleProvider.idle(5); //Rather than hardcoding, I want to pass from a property file
IdleProvider.timeout(5); //Rather than hardcoding, I want to pass from a property file.
KeepaliveProvider.interval(10); //Same here - pass from a property file
I have a Service class where I read my property file and set the result in $sessionStorage but I'm not able to inject the sessionStograge into the .config(..) method above because you can only inject constants and providers into .config(..). Any help would be appreciated!
auth.getRedirectUrls2 = function() {
console.log("getRedirectUrls2 has been caled!!");
var promise = $http.get("resources/sessiontimeout.properties")
.then(function(response) {
console.log("In getRedirectUrl2 response is: ", response.data);
console.log("Is the damn data in the session or not: ", $sessionStorage.redirectprops);
$sessionStorage.redirectprops = response.data;
return response.data;
})
.catch(function(response) {
console.error('Gists error', response.status, response.data);
})
.finally(function() {
console.log("In getRedirectUrls2 - Finally, all is done!!");
});
$sessionStorage.redirectpromise = promise;
console.log("The promise is: ", promise);
console.log("The promise from session is: ", $sessionStorage.redirectpromise);
return promise;
};
=======Edited with follow-up questions ====
I have the following project structure but don't know where to create the provider.
webapp/static/app/js
controllers (all controllers here)
directivers (all directives here)
services (all services here)
Also, can I create tryConstant.layouts.idleTime inside the provider constructor like this?
layout.idleTime=xyz
layout.intervalTime=abc
Where do I inject $sessionStorage or the service in the provider?

You should create a provider and use the provider in the config, you can use the service and sessionStograge in the provider and you can use provider in the config.
Here's a example of provider:
(function() {
'use strict';
angular
.module('app')
.run(layoutRunner)
.provider('tryConstant', constantProvider);
function constantProvider() {
var layout = {};
}
function constanntRunner() {
// check for $stateChangeStart and update the layouts if we have data.layout set
// if nothing set reset to defaults for every state
var destroyOn = $rootScope.$on('$stateChangeStart');
$rootScope.$on('$destroy', removeWatch);
function removeWatch() {
destroyOn();
}
}
})();
And you can use sessionstorage and everything here, and update values in layout object. And use it in config as following
.config(function($stateProvider, $urlRouterProvider, $httpProvider, KeepaliveProvider, IdleProvider,tryConstant ) {
console.log("Idle timer is here")
IdleProvider.idle(tryConstant.layouts.idleTime);
}

Since this change, you can use setIdle and setTimeout functions on Idle service to change these values after service has been configured.

I had the same scenario and solved it using factory providers, as below:
angular.module()
.factory("myFun", ['$http',
function($http){
return {
getAppIdleDuration: function() {
return $http.get("/api/property/getAppIdleDuration")
.then(function(response) {
return response.data;
});
},
getAppIdleTimeout: function() {
return $http.get("/api/property/getAppIdleTimeout")
.then(function(response) {
return response.data;
});
}
};
}
]).config(function(IdleProvider, KeepaliveProvider,myFunProvider) {
myFunProvider.$get().getAppIdleDuration().then(function(data){
console.log(" ++++++ getAppIdleDuration "+data);
IdleProvider.idle(data); // in seconds
});
myFunProvider.$get().getAppIdleTimeout().then(function(data){
console.log(" ++++++ getAppIdleTimeout "+parseInt(data));
IdleProvider.timeout(parseInt(data)); // in seconds
});
})
Configs in application.properties:
app.ui.idle.duration=5
app.ui.idle.timeout=10
Here /api/property/getAppIdleDuration and /api/property/getAppIdleTimeout http calls return the above properties respectively.

Related

AngularJS set constant according to the result of an http request

I want to set module.constant according to a tag which is the result of an HTTP request.
Below is the demo code in my project;
code in app.js
(function(){
var myApp = angular.module('myApp',['ui.router','ui.bootstrap']);
myApp.contant("API_URL","https://www.helloworld.com/API");
myApp.run(function($rootScope.$http){
some code;
});
})();
I want to config a special Id according to the result of an HTTP request like this;
$http.get(API_URL + "/configTag")
.then(function success(response){
var tag = response.data.tag;
if(tag === 0) {
myApp.constant("specialID","111111111");
} else if (tag === 1) {
myApp.constant("specialID","222222222");
} else {
myApp.constant("specialID","333333333");
}
},function error(response){
});
But I am new to front end and AngularJS. I don't know how to realize this?
Short answer: The $http service can't be used to create Angular constants.
The AngularJS framework operates in two phases: the "config" phase and the "run" phase. Once the "run" phase starts, providers can no longer be configured and constants can no longer be added. $http service operations can only be done in the "run" phase.
However a service provider can provide a promise of a constant:
app.factory("specialIDpromise", function(API_URL, $http) {
var promise = $http.get(API_URL + "/configTag")
.then(function success(response){
var tag = response.data.tag;
if(tag === 0) {
//return to chain data
return "111111111";
} else if (tag === 1) {
return "222222222";
} else {
return "333333333";
};
}).catch(function error(response){
//return converts rejection to success
return "something";
});
//return promise to factory
return promise;
});
To use in a controller:
app.controller("myCtrl", function(specialIDpromise) {
specialIDpromise.then(function(specialID) {
console.log(specialID);
//Do other things that depend on specialID
});
});
All operations in the config phase need to be synchronous (including adding constants.) Asynchronous operations such as XHRs by the $http service happen in the run phase of an AngularJS app.
It's possible to do want you want using server side data. You have to inject what modules you need, and run your function to get the data in a document.ready function. Here is how I am doing it, but the only issue I'm having is if the server is unreachable; then the app doesn't get bootstrapped. I placed this code in my app.js file outside of the angular.module code, as it will not work there since the app hasn't been bootstrapped yet.
angular.element(document).ready(function () {
getConstants();
});
//Get constants from server and manually bootstraps application to the DOM
function getConstants() {
var initInjector = angular.injector(["ng"]);
var $http = initInjector.get("$http");
$http.get('urlThatRetrievesConstants').then(function (result) {
/*Below I'm setting the constants on the app module and giving them a
name, which will be used to reference in whatever modules you will use it
in */
angular.module('appModuleName').constant('constantsName', result.data);
/*Here I'm bootstrapping the app*/
angular.bootstrap(document, ["appModuleName"]);
}, function (error) {
/*Handle server errors from request*/
});
}

Can not figure out how to store $rootScope in angular.bootstrap

I'm trying to call a web service in AngularJS bootstrap method such that when my controller is finally executed, it has the necessary information to bring up the correct page. The problem with the code below is that of course $rootScope is not defined in my $http.post(..).then(...
My response is coming back with the data I want and the MultiHome Controller would work if $rootScope were set at the point. How can I access $rootScope in my angular document ready method or is there a better way to do this?
angular.module('baseApp')
.controller('MultihomeController', MultihomeController);
function MultihomeController($state, $rootScope) {
if ($rootScope.codeCampType === 'svcc') {
$state.transitionTo('svcc.home');
} else if ($rootScope.codeCampType === 'conf') {
$state.transitionTo('conf.home');
} else if ($rootScope.codeCampType === 'angu') {
$state.transitionTo('angu.home');
}
}
MultihomeController.$inject = ['$state', '$rootScope'];
angular.element(document).ready(function () {
var initInjector = angular.injector(["ng"]);
var $http = initInjector.get("$http");
$http.post('/rpc/Account/IsLoggedIn').then(function (response) {
$rootScope.codeCampType = response.data
angular.bootstrap(document, ['baseApp']);
}, function (errorResponse) {
// Handle error case
});
});
$scope (and $rootScope for that matter) is suppose to act as the glue between your controllers and views. I wouldn't use it to store application type information such as user, identity or security. For that I'd use the constant method or a factory (if you need to encapsulate more logic).
Example using constant:
var app = angular.module('myApp',[]);
app.controller('MainCtrl', ['$scope','user',
function ($scope, user) {
$scope.user = user;
}]);
angular.element(document).ready(function () {
var user = {};
user.codeCampType = "svcc";
app.constant('user', user);
angular.bootstrap(document, ['myApp']);
});
Note Because we're bootstrapping the app, you'll need to get rid of the ng-app directive on your view.
Here's a working fiddle
You could set it in a run() block that will get executed during bootstrapping:
baseApp.run(function($rootScope) {
$rootScope.codeCampType = response.data;
});
angular.bootstrap(document, ['baseApp']);
I don't think you can use the injector because the scope isn't created before bootstrapping. A config() block might work as well that would let you inject the data where you needed it.

$rootScope in angular document ready

I have this piece of code:
.service('webSocket', function ($rootScope, socketFactory, CONFIG, $timeout) {
angular.element(document).ready(function () {
$rootScope.log('Waiting for connection...',CONSOLE_INFO);
});
And I am getting this error:
TypeError: $rootScope.log is not a function
This service is injected into this controller:
.controller('mainCtrl', function mainCtrl($scope, $rootScope, webSocket, myConsole ...
In which I have:
$rootScope.log = function (msg, type) { myConsole.log(msg,type); ... };
Can you tell me where is the problem? Or at least point me in the right direction? The reason I am using document ready function is because apart from logging messages to browser console (console.log) I use notifications for user (pNotify library) which needs to be called after DOM is loaded.
Sharing something between services using $rootScope should be considered generally as anti-pattern. If you don't have some different implementation of console for different controllers, you can do it Angular-way and perform all configurations in config block. Subscribing to document ready event in the service is also not a good idea (I would prefer to do it in run block), since in angular service is instantiated once it is first time required by any other service or controller or whatever. In order to have configurable service that may have different console implementation I would implement it using provider as follows:
angular.module('app',[]).
constant('console', console).
constant('PNotify', PNotify).
provider('myConsole', function() {
var log = angular.noop;
function MyConsoleFactory() {
return {
log: log,
debug: log
}
}
this.setLog = function(logImplementation) {
log = logImplementation
}
this.$get = [MyConsoleFactory];
}).
config(['myConsoleProvider', 'console', 'PNotify', function(myConsoleProvider, console, PNotify) {
myConsoleProvider.setLog(function(msg) {
console.log('[LOG] '+ Date.now() + ':\t' + msg);
new PNotify({
title: 'Title',
text: msg
});
});
}]).
run(['myConsole', '$document', function(myConsole, $document) {
$document.ready(function () {
myConsole.log('Waiting for connection...');
});
}]);
In this case you don't need any controller at all.
Plunker: http://plnkr.co/edit/aV9TIO07pnDs26xDBPtf?p=preview
That happens because service code runs before service was added to controller(where $rootScope.log method is defined). You can move $rootScope.log = function (msg, type) { myConsole.log(msg,type); ... }; into app.run(...) method and it will work.

angular controller needing refresh to pickup new data in a factory promise

I have a pretty standard app which will display news items from a remote JSON feed. So basically I have decided to poll the remote server and store the JSON in localStorage (to enable offline usage). For the moment, I have a manual page/view I must click on to update the localStorage , this works fine.
The problem is that after I use my temporary manual update page, I then go to the news page/view and it is not updated. To view the current JSON contents I must hit refresh (while still developing in the browser.)
I'm totally new to Angular and have tried to find solutions to this myself - $watch or reload: true seem to be suggested as fixes, but I cannot get them to work in my case.
Route
.state('tab.news', {
url: '/news',
reload: true,
views: {
'news-tab': {
templateUrl: 'templates/news_home.html',
controller: 'newsCtrl'
}
}
})
factory
angular.module('schoolApp.services', [])
.factory('newsService', function($q) {
var newsHeadlines =localStorage.getItem('newsHeadlines') || '{"status":"READFAIL"}'; // get news as a JSON string. if newsHeadlines not found return a JSON string with fail status
var newsHeadlinesObj = JSON.parse(newsHeadlines);// convert to an object
console.log("factory newsService ran");
return {
findAll: function() {
var deferred = $q.defer();
deferred.resolve(newsHeadlinesObj);
return deferred.promise; // or reject(reason) to throw an error in the controller https://docs.angularjs.org/api/ng/service/$q
},
findById: function(newsId) {
var deferred = $q.defer();
var newsItem = newsHeadlinesObj[newsId];
deferred.resolve(newsItem);
return deferred.promise;
}
}
});
Controller
schoolApp.controller('newsCtrl', function($scope, newsService) {
console.log ( 'newsCtrl ran' );
newsService.findAll().then(function (newsHeadlinesObj) {
$scope.newsHeadlinesObj = newsHeadlinesObj;
}, function(error){
console.log(error)
});
})
Looking at my console, the first time I read the news, the factory then controller run, but if I go to pull more data down, then go hack to news, only the controller runs, unless I refresh, then both run again.
I do not need the news view to update 'live' while still on it (but if that can be easilly done all the better) - just to pick up new data when you go back to news after being elsewhere in the app.
Thank you.
Factories return singletons and only run once. The object newsService is cached by angular. The var declarations for newsHeadlines and newsHeadlinesObj will only ever run once; meaning your promise returning methods will always resolve the promise with the same data that was retrieved when your factory was first instantiated. You should put them in a function and call it from your find methods on the singleton object.
.factory('newsService', function($q) {
function getHeadlines() {
var newsHeadlines = localStorage.getItem('newsHeadlines') || '{"status":"READFAIL"}'; // get news as a JSON string. if newsHeadlines not found return a JSON string with fail
return JSON.parse(newsHeadlines);// convert to an object
}
return {
findAll: function() {
var headlines = getHeadlines();
var deferred = $q.defer();
deferred.resolve(headlines);
return deferred.promise; // or reject(reason) to throw an error in the controller https://docs.angularjs.org/api/ng/service/$q
},
findById: function(newsId) {
var headlines = getHeadlines();
var deferred = $q.defer();
var newsItem = headlines[newsId];
deferred.resolve(newsItem);
return deferred.promise;
}
}
});
PS - I'm sure you know and are planning to do things differently later or something, but just in case you don't: Using promises here is pointless and you have no need for $q here. You could simply return the data instead of returning the promises.
I solved this withouut promises, I just used $rootScope in the factory and $scope.$on in the controller; when I change the factory, i use $rootScope.$broadcast to tell the controller that I change it.
.factory('dataFactory', ['$http', '$rootScope', function ($http, $rootScope) {
var dataFactory = {
stock: null,
getStock: getStock
}
function getStock() {
$http.get("/api/itemfarmacia/").then(function success(res) {
dataFactory.stock = res.data;
$rootScope.$broadcast('changingStock'); //Ones who listen this will see it
}, function error(err) {
console.log("Bad request");
})
}
return dataFactory;
}])
and in the controller
.controller('atencion', ["$scope", "$state", "dataFactory", function ($scope, $state, dataFactory) {
$scope.stock = dataFactory.stock; //At first is null
dataFactory.getStock(); //wherever you execute this, $scope.stock will change
$scope.$on('changingStock', function () {//Listening
$scope.stock = dataFactory.stock; //Updating $scope
})
}])

How can I directly inject $http into a provider?

All I need to do is to download a json file and assign it to OCategories in PCategory provider after I set the path. However I get an error that $http doesnt exist. How can I inject it into my provider and download inside of the setPath function?
var app = angular.module('NSApp',
[
'ui.bootstrap',
'MDItem',
'MDUser',
'MDNotification',
'MDUpload'
]
);
app.config(function(PCategoriesProvider)
{
PCategoriesProvider.setPath('data/csv/categories.json');
});
MDItem/provider/category.js
angular.module('MDItem').provider('PCategories',function(){
var OCategories;
var OPath;
return{
setPath: function(d){
OPath = d;
console.log('Path is set. Trying to download categories.');
OCategories = $http.get(oPath);
},
$get : function() {
return {
categories : OCategories
}
}
}
});
You can never inject service instances into config functions or providers, since they aren't configured yet. Providers exist to configure specific services before they get injected. Which means, there's always a corresponding provider to a certain service. Just to clarify, here's a little example configuring $location service using $locationProvider:
angular.module('myModule').config(function ($locationProvider) {
$locationProvider.html5Mode(true);
});
So what happens here, is that we configure $location service to use its html5mode. We do that by using the interfaces provided by $locationProvider. At the time when config() is executed, there isn't any service instance available yet, but you have a chance to configure any service before they get instantiated.
Later at runtime (the earliest moment ist the run() function) you can inject a service. What you get when injecting a service is what its providers $get() method returns. Which also means, each provider has to have a $get() function otherwise $injector would throw an error.
But what happens, when creating custom services without building a provider? So something like:
angular.module('myModule').factory('myService', function () {
...
});
You just don't have to care about, because angular does it for you. Everytime you register any kind of service (unless it is not a provider), angular will set up a provider with a $get() method for you, so $injector is able to instantiate later.
So how to solve your problem. How to make asynchronous calls using $http service when actually being in configuration phrase? The answer: you can't.
What you can do, is run the $http call as soon as your service gets instantiated. Because at the time when your service get instantiated, you're able to inject other services (like you always do). So you actually would do something like this:
angular.module('myModule').provider('custom', function (otherProvider, otherProvider2) {
// some configuration stuff and interfaces for the outside world
return {
$get: function ($http, injectable2, injectable3) {
$http.get(/*...*/);
}
};
});
Now your custom provider returns a service instance that has $http as dependency. Once your service gets injected, all its dependencies get injected too, which means within $get you have access to $http service. Next you just make the call you need.
To make your this call is getting invoked as soon as possible, you have to inject your custom service at run() phrase, which looks like this:
angular.module('myModule').run(function (custom, injectable2) {
/* custom gets instantiated, so its $http call gets invoked */
});
Hope this makes things clear.
Since all services are singletons in angular you could simply store a variable in a factory with the $http promise. And then when the factory is called at startup it will download the json.
You can then also expose a method on the factory that refreshes the data.
I know this is not the exact answer to your question, but I thought I'd share how I would do it.
angular.module('MDItem').factory('PCategories', function ($http, PCategoriesPath) {
var service = {
categories: [],
get: function () {
if (angular.isUndefined(PCategoriesPath)) {
throw new Error('PCategoriesPath must be set to get items');
}
$http.get(PCategoriesPath).then(function (response) {
service.categories = response.data;
});
}
};
// Get the categories at startup / or if you like do it later.
service.get();
return service;
});
// Then make sure that PCategoriesPath is created at startup by using const
angular.module('MDItem').const('PCategoriesPath', 'data/csv/categories.json');
angular.module('app').controller('myCtrl', function ($scope, PCategories) {
$scope.categories = PCategories.categories;
// And optionally, use a watch if you would like to do something if the categories are updated via PCategories.get()
$scope.$watch('categories', function (newCategories) {
console.log('Look maa, new categories');
}, true); // Notice the true, which makes angular work when watching an array
})
You have to inject $http in the function $get, because that's the function called by the injector.
However, to download the categories you would be better off using promises:
angular.module('MDItem').provider('PCategories',function(){
var OCategories;
var OPath;
return{
setPath: function(d){
OPath = d;
console.log('Path is set');
},
$get : function($http) {
return {
fetch: function () {
var deferred = $q.defer();
$http.get(oPath).then(function (value) {
deferred.resolve(value);
}
return deferred.promise;
}
}
}
}
});
I implemented what I wanted with a diffrent approach which is quite simple and effective. Just add a dummy controller in the main index.html(NOT PARTIAL). Data is now shared between all my modules and controllers and everything is downloaded once. :) Oh I love AJ.
...
<div ng-controller="initController" hidden></div>
...
initController:
angular.module('NSApp',[]).controller("initController",function($scope, $http, FCategory, FLocation){
$http.get('data/json/categories.json').then(function (response) {
FCategory.categories = response.data;
});
$http.get('data/json/cities.json').then(function (response) {
FLocation.cities = response.data;
});
$http.get('data/json/regions.json').then(function (response) {
FLocation.regions = response.data;
});
});
And now you can access it:
angular.module('MDTest', []).controller("test",function($scope, FCategory, FLocation){
$scope.categories = FCategory.categories;
FCategory factory
angular.module('MDItem').factory('FCategory', function ($http) {
var service = {
categories: [],
....
};
return service;
});

Resources