Angular - global variable initialise once - example user name in page header - javascript

In my angular application I store the logged in user name in a cookie.
And I want to display this user name in the web page header.
I can retrieve this in my controller as below and display in the page.
$rootScope.fullName = $cookies.get('user');
But instead of doing this in every controller, is it possible to do it in one place and always get this data ?
Update #1:
I don't use ui-view. I use angular route and a sample is as below.
Are there any simple approach please ?
var mdmApp = angular.module('mdmApp');
// Routes
mdmApp.config(function($routeProvider, $httpProvider, $locationProvider) {
$routeProvider
.when('/listScopeAndFrequency/:reportTypeId', {
templateUrl : '3_calendar/listScopeAndFrequencies.html',
controller : "listScopeAndFrequenciesController"
})
.when('/listTemplateFrequencyExceptions/:reportTypeId/:consolidationScopeCode/:frequencyCode', {
templateUrl : '3_calendar/listTemplateFrequencyExceptions.html',
controller : "listTemplateFrequencyExceptionsController"
})
.when('/viewSubmissionDates', {
templateUrl : '3_calendar/viewSubmissionDates.html',
controller : "viewSubmissionDatesController"
})
Update #2
I tried like below but could not get any dynamic values from REST API, I am only able to hard code the value. Not able to read from cookie, localStorage or var all are undefined or give errors.
mdmApp.factory('userService', function($http, $localStorage, $cookies) {
var fullName ;
// Gets user details
$http.get("/mdm/getUser")
.success(function(data, status, headers, config) {
console.log('userService > user : ' +data.fullName);
fullName = data.fullName;
$localStorage.user = data;
$cookies.put('user', data.fullName);
})
.error(function(data, status, headers, config, statusText) {
console.log("Error while retrieving user details.");
});
console.log('fullName : ' +fullName);
console.log('$cookies.get(user) : ' +$cookies.get('user'));
console.log('$localStorage.user : ' +$localStorage.user);
return { name: "hard code only works here"};
});
mdmApp.directive('userTitle', ['userService', function(user) {
console.log('userTitle > user : ' +user);
return {
template: user.name,
};
}]);

Why don't you use a service? Those are specially designed for this task:
Angular services are:
Lazily instantiated – Angular only instantiates a service when an application component depends on it.
Singletons – Each component dependent on a service gets a reference to the single instance generated by the service factory.
How I do it (not necessarily the best way):
setup an independent angular app (non-secured area) for user registration, on login store a token or some identifier in a cookie or the localStorage
when instantiating the full angular app (secured area), create a service which will get back the previous value and inject it whenever you need the user
My User service looks like:
angular.module('app.core')
.service('User', User);
function User($rootScope, localStorageService) {
let user = localStorageService.get('user');
let token = localStorageService.get('userToken');
if(!token) {
//this loads another angular app which has nothing to do with this secured area (on /login)
window.location = '/login'
return;
}
for(let i in user)
this[i] = user[i]
this.token = 'Bearer ' + token;
//note you could reference this to the rootScope so that every scope can have the User object, this can be considered as bad practice!
//$rootScope.user = this;
return this;
}

I would go with a service and a directory for this.
As soyuka explained. Services exists for the purpose of sharing data between parts of an application. I would then use a directive to handle the data in relation to the DOM, that is, to write to the header.
Service/factory:
app.factory('userService', function() {
// do what you have to get your user and save the data.
return { name: 'John'};
});
Directive. Notice the injection of the service. This could be done through a controller if you wish. It depends on whether you want your directives to have a dependency to the service.
app.directive('userTitle', ['userService', function(user) {
// do what you want with the data
return {
template: user.name,
};
}]);
Here the service just returns a dummy name and the directive outputs the name in the DOM.
Here is a running plunker of the setup.

Related

Dynamic partial arguments in AngularJS routing

I'm working with an angularjs site and have a background with working with routes in Rails and also Laravel in php. With routes in Laravel we could dynamically create a set of routes similar to:
foreach($cities as $city):
Route::get($city.'/hotels');
Route::get($city.'/{slug}');
endforeach;
Here we defined series of seperate routes in Laravel which technically do look the same except for the value of city and slug.
I'm finding angularJS a bit limited in defining routes in this case. Frankly am a bit lost here.
UPDATE
I've made some modifications here - basically I set up a service which retrieves assets from my database such as in this case a list of cities and categories. I'm trying to do this:
If {slug} is in the array of categories retrieved from my API, then use my ListController and list view but if its not then instead use my SingleVenueController and single view. Here's my code at the moment but its not working :(
appRouteProvider.when('/:city/:slug', {
templateUrl : function(sharedParams, $routeParams){
t = sharedParams.getCurrentPageType($routeParams);
if(t=='list'){
return '../../app/templates/list.html';
}
if(t=='single'){
return '../../app/templates/single.html';
}
},
controller : function(sharedParams, $routeParams){
t = sharedParams.getCurrentPageType($routeParams);
if(t=='list'){
return 'ListsController';
}
if(t=='single'){
return 'SingleController';
}
},
resolve:{
sharedParamsData:function(sharedParams){
return sharedParams.promise;
},
}
})
In the above sharedParams is a service and the getCurrentPageType just checks the url slug to decide what controller to send back - but its not really working at all :(
How about defining a single route with a paramater ?
In angularjs v1.x you can defined as many routes you want with as many params xor query
.config(function($routeProvider, $locationProvider) {
$routeProvider
.when('/city/:slug', {
templateUrl: 'book.html',
controller: 'BookController',
resolve: {
// you can also retrieve some data as a resolved promise inside your route for better performance.
}
})
ref: https://docs.angularjs.org/api/ngRoute/service/$route
appRouteProvider.when('/:city/:slug', {
templateUrl : 'dafault.html',
controller : 'DefaultController',
resolve:{
factory: function($routeParams, $http, $location, sharedParams){
var city = $routeParams.city;
var slug = $routeParams.slug;
var deferred = $q.defer();
sharedParams.getCurrentPageType($routeParams).then(function(t) {
if(t=='list'){
$location.path('/' + city + '/' + slug + '/list');
deferred.resolve();
}
else if(t=='single'){
$location.path('/' + city + '/' + slug + '/single');
deferred.resolve();
} else {
deferred.reject();
}
});
return deferred.promise;
},
}
});
appRouteProvider.when('/:city/:slug/list', {
templateUrl: '../../app/templates/list.html',
controller: 'ListsController',
});
appRouteProvider.when('/:city/:slug/single', {
templateUrl: '../../app/templates/single.html',
controller: 'SingleController',
});
You can do it with separate routes. The idea is when user hits the main route it resolves first with the data from the backend. If the condition is met, resolve function will redirect to specific route if not it wont pass
Services in Angular cannot be injected in the configuration phase since they become available only in the run phase of an Angular application.
There is however a trick to load $http service in the config phase which you can use to load your cities/categories and set up your routes. Meanwhile, since controllers aren't registered up until the run phase, you may use the $controllerProvider to register your controllers beforehand in the configuration phase:
app.config(function ($routeProvider, $controllerProvider) {
$controllerProvider.register('ListController', ListController);
$controllerProvider.register('SingleController', SingleController);
// wire the $http service
var initInjector = angular.injector(['ng']);
var $http = initInjector.get('$http');
...
});
You can now call your API to get the cities (or whatever else) and iterate while registering each route:
...
// fetch the cities from the server
$http.get('/cities')
.then(function (response) {
var cities = response.data;
for(var i = 0; i < cities.length; i++){
$routeProvider
// assuming each city object has a `name` property
.when('/' + cities[i]['name'] + '/:slug', {
templateUrl: getTemplate(cities[i]['name']),
controller: getController(cities[i]['name'])
})
}
});
...
Note that I'm using the getTemplate and the getController methods which return the templateUrl and the relevant controller name strings respectively using an ordinary switch expression. You can choose your own approach.
Plunkr Demo
Note:
While a function with the templateUrl route options property does work with setting up a custom template, but when you use a function alongside the controller property, Angular will consider it as the constructor for the controller. Therefore, returning the name of the controller in that function won't work.
As Ahmad has already pointed out in his answer, if you pass a function to controller it is considered as a constructor for the controller.
Also you can't get a service injected dynamically in config block of your app.
So what you can do is, move your sharedData service in separate app (in my code below I've used appShared as a separate app where this service is defined) and then access it using angular.injector. This way you don't have to define it as a parameter to templateUrl / controller functions.
Btw, you can't pass custom parameters to templateUrl function (ref: https://docs.angularjs.org/api/ngRoute/provider/$routeProvider)
If templateUrl is a function, it will be called with the following
parameters:
{Array.<Object>} - route parameters extracted from the current $location.path() by applying the current route
Now for the controller, use $controller to dynamically load either ListsController or SingleController based on your condition.
Once that is loaded, extend your current controller (defined by your controller function) using angular.extend so that it inherits all the properties and methods of the dynamically loaded controller.
Check the complete code here: http://plnkr.co/edit/ORB4iXwmxgGGJW6wQDy9
app.config(function ($routeProvider) {
var initInjector = angular.injector(['appShared']);
var sharedParams = initInjector.get('sharedParams');
$routeProvider
.when('/:city/:slug', {
templateUrl: function ($routeParams) {
console.log("template url - ");
console.log($routeParams);
var t = sharedParams.getCurrentPageType($routeParams);
console.log(t);
if (t == 'list') {
return 'list.html';
}
if (t == 'single') {
return 'single.html';
}
},
controller: function ($routeParams, $controller, $scope) {
//getController(cities[i]['name'])
console.log("controller - ");
console.log($routeParams);
var t = sharedParams.getCurrentPageType($routeParams);
console.log(t);
if (t == 'list') {
angular.extend(this, $controller('ListsController', { $scope: $scope }));
}
if (t == 'single') {
angular.extend(this, $controller('SingleController', { $scope: $scope }));
}
}
});
});

Why is AngularJS service re-initialized when route is re-loaded?

An AngularJS service is injected into two separate modules. This is causing the service to re-initialize separately when the second module calls it. I have used the FireFox debugger to confirm that the module is being re-initialized. How can I avoid this problem?
Here is the specific case:
An AngularJS app uses an authentication service in a module called auth to manage authentication concerns. The auth service is imported into a message module which manages access to the secure /message route and auth is also imported into a navigation module which manages both login/registration and also the visible contents of the navigation links in the user's browser. A user is able to successfully login using the GUI tools linked to the navigation module, and is then successfully redirected to the secure /message route as an authenticated user because the auth.authenticated1 and auth.authenticated2 properties are set to true just before the redirect to /message occurs.
However, the FireFox debugger confirms that the problem is that, when the user refreshes the browser to reload the /message url pattern, the auth module is re-initialized, setting the values of auth.authenticated1 and auth.authenticated2 back to false, and thus giving the user a message that they are not logged in, even though they were logged in a moment before using valid credentials provided by the user. What specific changes need to be made to the code below so that the user is NOT logged out on page re-load?
I want the AngularJS code to check the pre-existing value for auth.authenticated2 when the /message route is loaded or reloaded. If auth.authenticated2=false, then the user gets a message saying they are logged out. But if auth.authenticated2=true, I want the user to be able to see the secure content at the /message route. I DO NOT want auth.authenticated2 to be automatically re-set to false upon reloading the route, the way it is now.
Here is the code in message.html which contains the GUI elements for the /message route:
<div ng-show="authenticated2()">
<h1>Secure Content</h1>
<div>
<p>Secure content served up from the server using REST apis for authenticated users.</p>
</div>
</div>
<div ng-show="!authenticated2()">
<h1>You are not logged in.</h1>
</div>
Here is the code in message.js which is the controller for the message module that manages the /message route:
angular.module('message', ['auth']).controller('message', function($scope, $http, $sce, auth) {
$scope.authenticated2 = function() {
return auth.authenticated2;
}
//Other code calling REST apis from the server omitted here to stay on topic
});
Here is the code for the navigation module, which also injects the auth service:
angular.module('navigation', ['ngRoute', 'auth']).controller('navigation', function($scope, $route, auth, $http, $routeParams, $location) {
$scope.credentials = {};//from old navigation module
$scope.leadresult = "blank";
$scope.processStep = "start";
$scope.uname = "blank";
$scope.wleadid = "initial blank value";
$scope.existing = "blank";
$scope.tab = function(route) {
return $route.current && route === $route.current.controller;
};
$scope.authenticated1 = function() {
return auth.authenticated1;
}
$scope.authenticated2 = function() {
return auth.authenticated2;
}
$scope.login = function() {
auth.authenticate1($scope.credentials, function(authenticated1) {
//a bunch of stuff that does level 1 authentication, which is not relevant here
})
}
$scope.logout = auth.clear;
//some other methods to manage registration forms in a user registration process, which are omitted here because they are off-topic
$scope.pinForm = function(isValid) {//this method finishes authentication of user at login
if (isValid) {
$scope.resultmessage.webleadid = $scope.wleadid;
$scope.resultmessage.name = $scope.uname;
$scope.resultmessage.existing = $scope.existing;
var funcJSON = $scope.resultmessage;
auth.authenticate2(funcJSON, function(authenticated2) {
if (authenticated2) {
$location.path('/message');
$scope.$apply();//this line successfully re-directs user to `/message` route LOGGED IN with valid credentials
}
});
}
};
$scope.$on('$viewContentLoaded', function() {
//method that makes an unrelated call to a REST service for ANONYMOUS users
});
});
Here is the code for the auth service in auth.js:
angular.module('auth', []).factory( 'auth', function($rootScope, $http, $location) {
var auth = {
authenticated1 : false,
authenticated2 : false,
usrname : '',
loginPath : '/login',
logoutPath : '/logout',
homePath : '/message',
path : $location.path(),
authenticate1 : function(credentials, callback) {
var headers = credentials && credentials.username ? {
authorization : "Basic " + btoa(credentials.username + ":" + credentials.password)
} : {};
$http.get('user', {
headers : headers
}).success(function(data) {
if (data.name) { auth.authenticated1 = true; }
else { auth.authenticated1 = false; }
callback && callback(auth.authenticated1);
}).error(function() {
auth.authenticated1 = false;
callback && callback(false);
});
},
authenticate2 : function(funcJSON, callback) {
$http.post('/check-pin', funcJSON).then(function(response) {
if(response.data.content=='pinsuccess'){
auth.authenticated2=true;
callback && callback(auth.authenticated2);
}else {
auth.authenticated2=false;
auth.authenticated2 = false;
callback && callback(false);
}
});
},
clear : function() {
$location.path(auth.loginPath);
auth.authenticated1 = false;
auth.authenticated2 = false;
$http.post(auth.logoutPath, {}).success(function() { console.log("Logout succeeded");
}).error(function(data) { console.log("Logout failed"); });
},
init : function(homePath, loginPath, logoutPath) {
auth.homePath = homePath;
auth.loginPath = loginPath;
auth.logoutPath = logoutPath;
}
};
return auth;
});
The routeProvider is managed by the main js file for the app, which is hello.js and is as follows:
angular.module('hello', [ 'ngRoute', 'auth', 'home', 'message', 'public1', 'navigation' ])
.config(
function($routeProvider, $httpProvider, $locationProvider) {
$locationProvider.html5Mode(true);/* This line is one of 3 places where we set natural urls to remote the default # */
$routeProvider.when('/', {
templateUrl : 'js/home/home.html',
controller : 'home'
}).when('/message', {
templateUrl : 'js/message/message.html',
controller : 'message'
}).when('/public1', {
templateUrl : 'js/public1/public1.html',
controller : 'public1'
}).when('/register', {
templateUrl : 'js/navigation/register.html',
controller : 'navigation'
}).otherwise('/');
$httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
}).run(function(auth) {
// Initialize auth module with the home page and login/logout path
// respectively
auth.init('/checkpin', '/login', '/logout');
});
Not a complete answer as in, exact code to change here. But enough to get something built nicely.
You shouldn't explicitly use only boolean values inside your auth service. As far as I can see, you're not using any data that you're receiving from the server after a successful authentication. So whenever you refresh, everything is lost. You don't have anything in your code to recover from a refresh.
Ideally, you should have something like a token or a cookie. The cookie will be saved and can be recovered after a refresh, so while starting the auth service, you can check for the existence of that cookie.
You could also save a token that can be used to access an API inside an indexedDB or something like that. Just as I said before, during boot time of the auth service, you'll have to check for the existence of that token.
If you need more information, check for Oauth2. Even though Oauth2 isn't an Authentication API but an authorization API, the password grant type is something you could use. To get an idea how to build a solid system. The other grant types are mostly only focused on the authorization side of OAuth.
Because OP ask for code here it is:
when creating_service:
if exists(cookie) or exists(token) and valid(cookie) or valid(token):
mark_service_as_logged_in()
If pseudo code is better than words.

AngularJS: do not load view until first query return success (not 401 response)

In my app (generated with yeoman) i have such structure:
view:
index.html with ng-view for rendering controller view
controller:
every controller try to get some data with api urlm like:
$scope.getArticles = function() {
$http.get(settings.apiBaseUri + '/app/articles/', {
headers: {
'Content-Type': 'application/json',
'Pragma': 'no-cache',
'Cache-Control': 'no-cache',
'If-Modified-Since': ''
}
})
.success(function(response) {
/* do some magic here :) */
});
};
$scope.getArticles();
and so in every controller (+ many other queries)
and now i'm a little bit confused: how to do not render anything, until my first request get 200 but not 401?
here is my interceptor:
var deleteAuth = function(){
$location.path('/signin');
delete $localStorage.authStatus;
};
var responseError = function(rejection) {
...
if (rejection.status === 401 || rejection.status === 403) {
authStatus = $localStorage.authStatus;
if (authStatus && authStatus.isAuth && authStatus.authToken && !ipCookie('authToken') && !renewTokenAttempt) {
deleteAuth();
}
}
...
return $q.reject(rejection);
};
and all works almost 'as must be', but...
every time, i get my index.html loaded (with sidebar, logo etc) and only then i try to get (for example) my articles, and then, if i get 401 i'm redirected to auth-page and this is not so good: becouse i see small blink with all styles, and only then login page.
Is it real, to check first, if i get 200, and only then render page with sidebar etc, and if not: then auth-page.
How to do that?
i saw some solutions in web, but all they are huge, i need something really fast and simple)
Ok, I guess you try to build an SPA and you have routes in your app. You can use resolve before you render something. Take a look here http://www.johnpapa.net/route-resolve-and-controller-activate-in-angularjs/
You will have something like this:
'use strict';
var yeomanTestApp = angular.module('yeomanTestApp', [])
.config(['$routeProvider', function($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/main.html',
controller: 'MainCtrl',
resolve : {
// add your code here to see it the user has rights or not
}
})
.otherwise({
redirectTo: '/'
});
}]);
If any of these dependencies are promises, they will be resolved and converted to a value before the controller is instantiated and the $stateChangeSuccess event is fired.
The resolve property is a map object. The map object contains key/value pairs of:
key – {string}: a name of a dependency to be injected into the controller.
factory - {string|function}:
If string, then it is an alias for a service.
Otherwise if function, then it is injected and the return value is treated as the dependency. If the result is a promise, it is resolved before the controller is instantiated and its value is injected into the controller.

Dynamically Creating Routes in Angular JS

We're trying to make the switch to angular, but we have a pretty big issue with routing. Our current site has something like 10,000 unique routes -- ever page has a unique ".html" identifier. There's no particular convention that would allow us to assign the controller to them, so I created a lookup API endpoint.
Here's the workflow I'm trying to create:
Angular app loads. One "otherwise" route is set up.
When someone clicks a link, I don't know if the resource is a product or a category, so a query is made to the lookup endpoint with the unique ".html" identifier. The endpoint returns two things: the name of the resource, and an ID ("product" and "10" for example). So to be clear, they hit a page like, "http://www.example.com/some-identifier.html," I query the lookup API to find out what kind of resource this is, and get a result like, "product" and "10" -- now I know it's the product controller/template and I need the data from product id 10.
The app assigns the controller and template ("productController" and "product.html"), queries the correct endpoint for data ("/api/product/10"), and renders the template.
The problems I'm facing:
$http isn't available during config, so I can't hit the lookup table.
Adding routes after the config is sloppy at best -- I've done it successfully by assigning $routeProvider to a global variable and doing it after the fact, but man, it's ugly.
Loading all the routes seems impractical -- just the size of the file would be pretty heavy for a lot of connections/browsers.
We can't change the convention now. We have 4 years of SEO and a lot of organic traffic to abandon our URLs.
I feel like I might be thinking about this the wrong way and there's something missing. The lookup table is really the problem -- not knowing what kind of resource to load (product, category, etc). I read this article about loading routes dynamically, but again, he's not making an external query. For us, loading the controllers isn't the problem, it's resolving the routes and then assigning them c
How would you solve the problem?
Solution
Huge thanks to #user2943490 for pointing me in the right direction. Don't forget to upvote his answer! I made it a little more general so that I don't have to define the route types.
API Structure
This configuration requires at least two endpoints: /api/routes/lookup/:resource_to_lookup:/ and /api/some_resource_type/id/:some_resource_id:/. We query the lookup to find out what kind of resource it points to and what the ID of the resource is. This allows you to have nice clean urls, like, "http://www.example.com/thriller.html" (a single) and "http://www.example.com/michaeljackson.html" (a collection).
In my case, if I query something like, "awesome_sweatshirt.html" my lookup will return a JSON object with "{type: 'product', id: 10}". Then I query "/api/product/id/10" to get the data.
"Isn't that slow?" you ask. With varnish in front, all of this happens in way less than 1 second. We're seeing pageload times locally of less than 20ms. Across the wire from a slow dev server was closer to half a second.
app.js
var app = angular.module('myApp', [
'ngRoute'
])
.config(function($routeProvider, $locationProvider) {
$routeProvider
.otherwise({
controller: function($scope, $routeParams, $controller, lookupService) {
/* this creates a child controller which, if served as it is, should accomplish your goal behaving as the actual controller (params.dashboardName + "Controller") */
if ( typeof lookupService.controller == "undefined" )
return;
$controller(lookupService.controller, {$scope:$scope});
delete lookupService.controller;
//We have to delete it so that it doesn't try to load again before the next lookup is complete.
},
template: '<div ng-include="templateUrl"></div>'
});
$locationProvider.html5Mode(true);
})
.controller('appController', ['$scope', '$window', '$rootScope', 'lookupService', '$location', '$route', function($scope, $window, $rootScope, lookupService, $location, $route){
$rootScope.$on('$locationChangeStart', handleUniqueIdentifiers);
function handleUniqueIdentifiers (event, currentUrl, previousUrl) {
window.scrollTo(0,0)
// Only intercept those URLs which are "unique identifiers".
if (!isUniqueIdentifierUrl($location.path())) {
return;
}
// Show the page load spinner
$scope.isLoaded = false
lookupService.query($location.path())
.then(function (lookupDefinition) {
$route.reload();
})
.catch(function () {
// Handle the look up error.
});
}
function isUniqueIdentifierUrl (url) {
// Is this a unique identifier URL?
// Right now any url with a '.html' is considered one, substitute this
// with your actual business logic.
return url.indexOf('.html') > -1;
}
}]);
lookupService.js
myApp.factory('lookupService', ['$http', '$q', '$location', function lookupService($http, $q, $location) {
return {
id: null,
originalPath: '',
contoller: '',
templateUrl: '',
query: function (url) {
var deferred = $q.defer();
var self = this;
$http.get("/api/routes/lookup"+url)
.success(function(data, status, headers, config){
self.id = data.id;
self.originalPath = url;
self.controller = data.controller+'Controller';
self.templateUrl = '/js/angular/components/'+data.controller+'/'+data.controller+'.html';
//Our naming convention works as "components/product/product.html" for templates
deferred.resolve(data);
})
return deferred.promise;
}
}
}]);
productController.js
myApp.controller('productController', ['$scope', 'productService', 'cartService', '$location', 'lookupService', function ($scope, productService, cartService, $location, lookupService) {
$scope.cart = cartService
// ** This is important! ** //
$scope.templateUrl = lookupService.templateUrl
productService.getProduct(lookupService.id).then(function(data){
$scope.data = data
$scope.data.selectedItem = {}
$scope.$emit('viewLoaded')
});
$scope.addToCart = function(item) {
$scope.cart.addProduct(angular.copy(item))
$scope.$emit('toggleCart')
}
}]);
Try something like this.
In the route config you set up a definition for each resource type and their controllers, templates and a resolve:
$routeProvider.when('/products', {
controller: 'productController',
templateUrl: 'product.html',
resolve: {
product: function ($route, productService) {
var productId = $route.current.params.id;
// productService makes a request to //api/product/<productId>
return productService.getProduct(productId);
}
}
});
// $routeProvider.when(...
// add route definitions for your other resource types
Then you listen for $locationChangeStart. If the URL being navigated to is a "unique identifer", query the lookup. Depending on the resource type returned by the lookup, navigate to the correct route as defined above.
$rootScope.$on('$locationChangeStart', handleUniqueIdentifiers);
function handleUniqueIdentifiers (event, currentUrl, previousUrl) {
// Only intercept those URLs which are "unique identifiers".
if (!isUniqueIdentifierUrl(currentUrl)) {
return;
}
// Stop the default navigation.
// Now you are in control of where to navigate to.
event.preventDefault();
lookupService.query(currentUrl)
.then(function (lookupDefinition) {
switch (lookupDefinition.type) {
case 'product':
$location.url('/products');
break;
case 'category':
$location.url('/categories');
break;
// case ...
// add other resource types
}
$location.search({
// Set the resource's ID in the query string, so
// it can be retrieved by the route resolver.
id: lookupDefinition.id
});
})
.catch(function () {
// Handle the look up error.
});
}
function isUniqueIdentifierUrl (url) {
// Is this a unique identifier URL?
// Right now any url with a '.html' is considered one, substitute this
// with your actual business logic.
return url.indexOf('.html') > -1;
}
You can use $routeParams for this.
e.g.
route/:type/:id
so type and id can be totally dynamic, the different type handling will be up to the route's controller.
What if you have a json file with the information of the routes (and if there is not a security issue) and iterate over it to attach routes to the app?
e.g.
JSON:
routes: [
{
controller: "Controller1"
path: "/path1"
templateUrl: 'partials/home/home.html'
},
{
controller: "Controller1"
path: "/path1"
templateUrl: 'partials/home/home.html'
}
]
And then iterate over the contents of the JSON and attach them to $routeProvider.when ?
I am not sure if it is a good idea, depends how big would be the JSON file and if you dont want to expose all your routes to a possible attacker.
From the AngularJS documentation,
The $routeParams service allows you to retrieve the current set of
route parameters.
Dependencies: $route
Example look like
// Given:
// URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby
// Route: /Chapter/:chapterId/Section/:sectionId
// Then
$routeParams ==> {chapterId:'1', sectionId:'2', search:'moby'}
ngRouteModule.provider('$routeParams', $RouteParamsProvider);
function $RouteParamsProvider() {
this.$get = function() { return {}; };
}

AngularJS call common controller function from outside controller

My basic premise is I want to call back to the server to get the logged in user in case someone comes to the site and is still logged in. On the page I want to call this method. Since I am passing the user service to all my controllers I don't know which controller will be in use since I won't know what page they're landing on.
I have the following User Service
app.factory('userService', function ($window) {
var root = {};
root.get_current_user = function(http){
var config = {
params: {}
};
http.post("/api/user/show", null, config)
.success(function(data, status, headers, config) {
if(data.success == true) {
user = data.user;
show_authenticated();
}
});
};
return root;
});
Here is an empty controller I'm trying to inject the service into
app.controller('myResourcesController', function($scope, $http, userService) {
});
So on the top of my index file I want to have something along the lines of
controller.get_current_user();
This will be called from all the pages though so I'm not sure the syntax here. All examples I found related to calling a specific controller, and usually from within another controller. Perhaps this needs to go into my angularjs somewhere and not simply within a script tag on my index page.
You could run factory initialization in run method of your angular application.
https://docs.angularjs.org/guide/module#module-loading-dependencies
E.g.
app.run(['userService', function(userService) {
userService.get_current_user();
}]);
And userService factory should store authenticated user object internaly.
...
if (data.success == true) {
root.user = data.user;
}
...
Then you will be able to use your factory in any controller
app.controller('myController', ['userService', function(userService) {
//alert(userService.user);
}]);
You need to inject $http through the factory constructor function, for firsts
app.factory('userService', function ($window, $http) {
var root = {};
root.get_current_user = function(){
var config = {
params: {}
};
$http.post("/api/user/show", null, config)
.success(function(data, status, headers, config) {
if(data.success == true) {
user = data.user;
show_authenticated();
}
});
};
return root;
});
in your controller you can say
$scope.get_current_user = UserService.get_current_user();
ng attributes in your html if needed. besides this, i am not sure what you need.

Categories

Resources