Ways to use server date in an AngularJS application - javascript

Is there a "nice" way to set the server's date to an angularJS app?
Let's say that I have an API route to get this, sort of /api/date and I can call it with an angular service called dateService:
angular.module('myApp').service('DateService', ['$resource', function ($resource) {
return $resource('/api/date', { }, {
});
}]);
I need to get the date as soon as the app starts because my html file uses a function based on that date to display something. Otherwise I'll get an "undefined" error everytime I call it.
Thanks in advance

Have the back-end server return a timestamp in UTC.
Parse the timestamp value with var date = new Date(serverTimestampValue);
You now have a JS Date object that can be used with Angular Date filters.

If you are using ui-router for routing and if you need to learn the timestamp before showing anything to user you can use the resolve property of ui-router. Basically it resolves what you need before activating a state, and if you retrieve your server time in your parent state you can be sure that you will have the timestamp before anything starts in your application. Here is an example how to do it:
angular.module("yourApp").config(["$stateProvider", function ($stateProvider) {
$stateProvider
.state("topState", {
abstract: true,
template: "<ui-view></ui-view>",
controller: ["$rootScope", "serverTimestamp", function ($rootScope, serverTimestamp) {
$rootScope.serverTime = new Date(serverTimestamp);
//Do what you need to do with the server time, from now on you will have access to server time from each controller in your app.
}],
resolve: {
serverTimestamp: ["", function (bakkalBrandService) {
return yourService.retrieveTimestampFromServer();
}]
}
})
}])

You can do that in app.run
var app = angular.module('myApp',[]);
app.run(function ($rootScope, $state, DateService) {
// call date service and save it in $rootScope
});

You can delay the execution of angular app by manually bootstrapping it rather than auto initialization.
https://docs.angularjs.org/guide/bootstrap
This way you can do the following to get the server settings and have it available when your angular app starts.
//Define a global settings object. add proper namespace as per your app.
var myServerConfig = (function () {
var settings = {
someDefaultSetting: 'someDefaultValue'
};
return {
init: function (serverSettings) {
settings = $.extend(settings, serverSettings);
},
settings: settings
}
})();
//Define angular wrapper for your global object
var serverSettings = angular.module('server.settings', []);
serverSettings.factory('serverSettings', ['$window', function () {
return $window.myServerConfig.settings;
}]);
//On dom ready get all settings and bootstrap angular app
$(document).ready(function () {
//You will have to show some animation if you are making ajax call
//also if errors out handle that scenario
//Other option is to render settings from server on page load as mentioned in other answers
$.ajax({
type: 'GET',
url: '/api/settings',
error: function (error) {
//show user message that app cannot be loaded
},
success: function (setting) {
myServerConfig.init(setting);
bootStrapMyApp();
}
});
function bootStrapMyApp() {
angular.element(document).ready(function () {
angular.bootstrap(document, ['myApp']);
});
}

angular.module('myApp').service('DateService', ['$resource', function ($resource) {
this.date = null;
var self = this;
$resource('/api/date', { }, {
}).then(function(data){
self.date = data.date;
});
}]);

You can assign it to a service variable after you get it.
angular.module('myApp').service('DateService', ['$resource', function ($resource) {
this.date = null;
var self = this;
$resource('/api/date', { }, {
}).then(function(data){
self.date = data.date;
});
}]);
Then call the service to get it when you need it.
$scope.date = function() {
return DateService.date;
}

Related

How can I execute a function in an angularJS controller from a backbone controller

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');
}

AngularJS module.controller vs $controllerProvider.register

I'm trying to add controller dynamically in my AngularJS application.
On sub-domain, I have anotherController.js file.
Here's anotherController.js content:
function anotherControllerWrapper() {
return ['$scope', '$state', function ($scope, $state) {
$scope.doWork = function () {
//...doing some work...
alert('work done');
};
$scope.doWork();
}];
};
Also I have wrote runtimeController provider to be able to use $controllerProvider in runtime:
app.provider('runtimeController', function () {
var controllerProvider = null;
this.setControllerProvider = function (cp) {
controllerProvider = cp;
};
this.$get = function () {
return {
registerController: function (controllerName, controllerConstructor) {
if (!controllerProvider.has(controllerName)) {
controllerProvider.register(controllerName, controllerConstructor);
}
}
};
};
});
Here's config section of application:
app.config(function($controllerProvider, runtimeControllerProvider) {
runtimeControllerProvider.setControllerProvider($controllerProvider);
});
I'm receiving controller's code over http (inside another controller), so it looks like this:
app.controller('testController', ['$scope', '$state', '$http', 'runtimeController',
function ($scope, $state, $http, runtimeController) {
$http.get('http://someUrl/anotherController.js')
.then(
function(sucess){
var evaluated = new Function('return ' + success.data)();
var ctrl = evaluated();
// routing to ui state with specified 'anotherController' works
// no 'anotherController' in app._invokeQueue
runtimeController.registerController('anotherController', ctrl);
// routing to ui state with specified 'anotherController' constanly fails
// 'anotherController' appears in app._invokeQueue
//app.controller('anotherController', ctrl);
//--registering new UI route with 'anotherController' as controller here
$state.go('anotherState');
},
function(error){ alert('something went wrong!'); },
);
}]);
Ui states are also added dymanically, after I'm adding controller.
Can someone explain me please, what's happening and what's difference between $controllerProvider.register and module.controller?
Module methods (controller, directive, etc) result in adding a config block (_configBlocks) that is executed on application initialization. Once the application has passed config phase, it won't execute newly added config blocks, so app.controller(...) has no effect during run phase.
As this example shows, runtimeController implementation can be simplified to
app.config(($provide, $controllerProvider) => {
$provide.value('$controllerProvider', $controllerProvider);
});
eval should be avoided for numerous reasons. Considering that the script is loaded from a domain that is allowed by CORS and doesn't require to be evaled, a suitable alternative is to load it as a script. This will require to patch AngularJS API to allow late component registrations, similarly to how ocLazyLoad does - or just use ocLazyLoad, because it already does that.

invoking an angular route from angular service to load a new view and controller

I'm trying to invoke a route through and angular service and since I am using $http.post I can't get the route to invoke. I may be going at this all wrong so I'm hoping someone can make a suggestion or point me in the right direction. Initally I have a page load with a controller which once the search command is called it passes a json object with the request to an angular service which then calls webAPI to pass the request onto my other business layers. Here is a logical diagram of the workflow. The response in blue is a new data object being returned to the UI with the users search results.
From my app I have the following routes setup
(function () {
app = angular.module('app', ['ui.bootstrap', 'ngRoute', 'ngAnimate']).value('ngToastr', toastr);
function router($routeProvider) {
$routeProvider.
when('/search/query', {
templateUrl: '../../AngularTemplates/searchResults.html',
controller: 'searchResultCtrl'
}).
otherwise({
templateUrl: '../../AngularTemplates/splashPage.html'
});
}
app.config(['$routeProvider', router]);
//added toaster as factory so it can be injected into any controller
angular.module('app').factory('ngNotifier', function (ngToastr) {
return {
notify: function (msg) {
ngToastr.success(msg);
},
notifyError: function (msg) {
ngToastr.error(msg);
},
notifyInfo: function (msg) {
ngToastr.info(msg);
}
}
});
})();
The initial page calls the controller which has a service dependency
app.controller('searchController', ['$scope', '$filter', 'searchService', 'ngNotifier', '$log', '$timeout', 'searchAttributes' , function ($scope, $filter, searchService, ngNotifier, $log, $timeout, searchAttributes) {
var vm = this;
vm.search = search;
vm.updateEntities = updateEntitySelection;
//bootstraped data from MVC
$scope.userOptions = searchAttributes.mvcData;
//scoped variables
$scope.searchTerm = null;
//ui container for search response
$scope.searchResponse;
$scope.entityList = [
'Search All ',
'Search in Departments ',
'Search in Automotive '
]
$scope.selectedEntity = 'Search All';
function buildSearchRequest() {
var searchResponse = {
searchTerm: $scope.searchTerm,
pageSize: 10,//this will be set by configuration from the UI
pagesReturned: 0,
entityFilter: $scope.selectedEntity
};
return searchResponse;
}
function onError(msg) {
$log.error('An error has occured: ' + msg.data);
}
function updateEntitySelection(entityName) {
$scope.selectedEntity = entityName;
}
function search() {
var request = buildSearchRequest();
searchService.search(request);
}
}]);
and the search service
app.factory('searchService', ['$http', function($http) {
var myEsResults;
function getSearchResults(searchRequest) {
return $http.post('search/query', searchRequest, {}).then(function (response) {
myEsResults = response.data});
}
var getResults = function () {
return myEsResults;
};
return{
search: getSearchResults,
getResults: getResults
};
}]);
What I am trying to accomplish is when the document loads a splash screen is displayed (which works). when the search is executed the request is passed to webapi and then the response is returned as an objectback to the view and a new controller so it can render the search results. I have passed data back and forth between controllers in the past however where I am stuck is using an angular service to call route in webapi. Making this call does not update the page URL and therefore the route is not invoked nor is the second controller loaded to display the results. In the past I have invoked angular routes using a url http://#/route however in this instance I am using an input button with ng-click. I would appreciate any suggestions as to how on the return of data get the 'result view' and controller to load. Is routing the correct approach or is there another way to load the view and controller when using an angular service?
Thanks in advance
<button type="button" class="btn btn-primary btn-lg" ng-click="vm.search()"><span class="glyphicon glyphicon-search"></span></button>
Should be able to do it using $location.path('/search/query')
function getSearchResults(searchRequest) {
return $http.post('search/query', searchRequest, {}).then(function (response) {
myEsResults = response.data;
$location.path('/search/query');
});
}
however workflow seems like it would make more sense to add either routeParams to the url or a search query param and pass url encoded query term to url and make request based on that. Then the request would be made by the searchResultCtrl controller or a resolve in the router config.
Something like:
$routeProvider.
when('/search/query/:queryterm', {
templateUrl: '../../AngularTemplates/searchResults.html',
controller: 'searchResultCtrl'
}).
And path would be generated by:
$location.path('/search/query/' + encodeURIComponent($scope.searchTerm) );

use angular factory to hold a value for rest of application to access to minimize server calls

I have the following factory:
angularModule
.factory('ArticleCategoryService', function ($http, $q) {
// Service logic
// ...
var categories = [];
var _getCategories = $http.get('/api/articles/category').success(function (_categories) {
categories = _categories;
});
// .error( function (data, status, headers, config) {
// });
// Public API here
return {
getCategories: function () {
var deferred = $q.defer();
deferred.resolve(_getCategories);
return deferred.promise;
}
};
});
and this is the section that calls this service in the controller:
// Calls the getCategories function from the ArticleCategory Service,
// Will return a promise
ArticleCategoryService.getCategories()
.then(function (categoriesResult) {
$scope.categories = categoriesResult.data;
}, function (err) {
console.log(err);
});
This works but there will be a GET call to the server every time user comes back to this view/state and the categories object that belongs to the factory is never used.
I'm trying to make it so that it will return the categories variable in the factory singleton, and have it initialize on site load (or from first GET call).
But if I just return categories when user calls getCategories, it will return nothing since we need time for the $http call.
Check if categories is defined, and resolve the promise with the variable rather than the GET request if it is:
return {
getCategories: function () {
var deferred = $q.defer();
if (categories.length > 0) {
deferred.resolve(categories);
} else {
deferred.resolve(_getCategories);
}
return deferred.promise;
}
};
I'm doing exactly same thing on my app. I have a main module and a main controller within it that wraps any other controllers, so its scope is persistent over views.
In this main controller you could assign the factory's getCategory() data to a scope variable and then you could use that in your entire app, because the scope will be inherited to child scopes.
angularMainModule
.factory('ArticleCategoryService', function ($http) {
var ArticleCategoryServiceMethods = {};
//fn: getCategories
ArticleCategoryServiceMethods.getCategories = function(fromWhere){
return $http.get(fromWhere)
.then(function(res){
return res.data;
}, function(err){
return err.status;
});
}
return ArticleCategoryServiceMethods;
}
angularMainModule
.controller('MAINCTRL', function($scope, ArticleCategoryService) {
//get all categories
$scope.categories = ArticleCategoryService.getCategories('/api/articles/category');
//... the rest of the main ctrl code ... //
}
... when you define the main module, make sure you inject the rest of your modules in it
var angularMainModule = angular.module('angularMainModule', [
'ngRoute',
'ngTouch',
'ngAnimate',
//< ng - etc >,
'Module1',
'Module2',
//< module - n >
]);
...and the markup (i'm bootstrapping my angular app manually, but you could add the ng-app="angularMainModule" attribute on the html tag if you're doing it that way):
<html ng-controller="MAINCTRL">
<!-- head data -->
<body>
<div id="page" ng-view></div>
If you want to make sure data is loaded before your app opens the main page, you could add that service call in the routeProvider block of your app (on the default route), so when the MAINCTRL will be loaded the data will be already there, ready to be assigned.
angularModule
.factory('ArticleCategoryService', function ($http) {
// Service logic
// ...
var categories = [];
$http.get('/api/articles/category').success(function (_categories) {
categories = _categories;
});
// Public API here
return {
categories: categories
};
});
angularModule
.controller('ControllerMain', function($scope, ArticleCategoryService) {
$scope.categories = ArticleCategoryService.categories;
});

How load $http service app on startup in Ionic framework?

How load $http service app on startup in Ionic framework?
// This is a service
appModule.factory('NewGames', function($http, $ionicLoading) {
// JSON Array
var newGames = [];
var request = {'searchString' : 'name_fr'};
$http.get('http://example.com/rest/data.php', { cache: true}, request).success(function(response) {
newGames = response;
});
return {
all: function() {
//$ionicLoading.hide();
return newGames;
}
}
});
// This is a controller
myApp.controller('MainCtrl', function($scope, NewGames, $ionicSlideBoxDelegate, $stateParams, $ionicPlatform) {
$scope.nextSlide = function() {
$ionicSlideBoxDelegate.next();
}
$scope.newGames = NewGames.all();
});
If you press the button, the data is loaded. How to make so that the data was loaded at startup?
Use the ionic.Platform.ready() method, documented here.
You can run this method from anywhere in your app, may it be factory or controller.

Categories

Resources