Passing global data / user data to routeProvider in Angular + express application - angularjs

This might seem like a silly question.. but how do I pass req.user.username (for example) to all pages / globally after the user signs in with passport. This question can apply to any data I would like accessible for all pages...
On the server side, I have the below which sends allows routeProvider to handle all client side routing.
app.get('*',
function(req, res) {
res.sendfile('./public/index.html')
// load the single view file (angular will handle the page changes on the front-end)
})
I'm not sure if the solution is specific to passport... express or involves both...
The client side routing is handled by something like:
.config(function($routeProvider, $locationProvider) {
$routeProvider
.when('/', {templateUrl: 'views/home.html'})
.when('/login', {templateUrl: 'views/login.html'})
.when('/users', {templateUrl: 'views/users.html', controller: 'UserController'})
...

You have two options. Either
BAD - include that data in your compiled view that is initially served (./public/index.html) or
GOOD/COMMON - fetch the data you need inside something like an Angular controller which is in your view; eg.
$routeProvider.when('/', {
templateUrl: 'views/home.html',
controller: function($scope, myThings) {
//myThings is a service that async fetches some data from api
myThings().then(function(user) {
$scope.user = user;
});
}
});
This obviously means you are exposing data on an api endpoint, but how else would angular be fetching the bits it needs since you said this is a single-page app?

Services are the way to go if you want to share global data among controllers, directives and other services.
Depending upon the type of data, you can expose services that load data from the server or services which do not need to make remote call to load data (like some custom view settings).
For example if in case you want to get the current logged in user.
The first thing is to create a method on the server that return the current logged in user data in json format.
Then create something like a UserService or SessionService that call this server method to load currently loggedin user data.
Something like
angular.module('myApp').factory('SessionService',['$http',function($http) {
var service={};
service.getCurrentUser=function() {
return $http('/user');
};
return service;
}]);
Inject this service into your controllers to get the current user. You can optimize it to cache the user data.
If you want to use the data in routeProvider use the resolve property
.when('/users', {templateUrl: 'views/users.html', controller: 'UserController',
resolve: {
currentUser:function(SessionService) {
return SessionService.getCurrentUser();
}
}}})

Related

angular routeprovider with authentication on nodejs

I'm currently experimenting a little bit with my first app with angular-js, nodejs and a mongodb backend. This app and its URLs are managed by ngRoute from angular. In this app there is a little admin area with authentication (based on nodejs' client-sessions). This works so far pretty well. The authentication works with URLs managed by nodejs, but not with the ones of the routeProvider. The provider looks like this:
app.config(['$locationProvider', '$routeProvider', function ($locationProvider, $routeProvider) {
$routeProvider.when("/main",
{
templateUrl: "app/main/landing.html",
controller: "MainController"
})
.when("/admin",
{
templateUrl: "restricted",
controller: "AdminController"
})
.when("/login",
{
templateUrl: "app/login/login.html"
})
.otherwise({redirectTo: '/main'});
}]);
The restricted area (/admin) calls a get to the server and sends the view.html if user is valid:
app.get('/restricted', function (req, res) {
if (!req.session_state.user) {
res.res.status(302).redirect("/login");//Does not really work, should redirect to login, but endless loop
} else {
res.sendFile(path.resolve(__dirname + "/../views/restricted/admin.html"));
}
});
The server's restricted folder is outside of the static area provided by nodejs.
The problem is that deep links (when calling directly '#!/admin') there is an endless loop. How can this be solved? I already tried with differnt URLs and status codes. Is this a correct approach to realize authentication with routeProvider or is there a better one?

Secure admin panel with AngularJS

I'd like to build a web application with AngularJS (client) and a RESTful API (server) using PHP + MySQL, just for studying purpose. The application must have admin panel, protected by login.
I'm using ui-router to prevent unauthorized users to access the panel, but as far as I know, every code on client side is not safe.
What if a malicious user modify the code to grant access to the panel without login? I know that the server data and code are protected, but not the HTML partials (layout is exposed), different from a common PHP application, where the views are "protected" in the server side. Should I be worried about it?
I would use $httpProvider to set up at least a basic token based login with a token/user check. You could manage the headders with an Auth service and methods like login(), logout, isLogedIn() to handle and save states to $cookies for example. This way, a malicious user could hack and gain access to the html templates, but with no database data... Minnifying your code helps avoid this risk as well.
angular.module('myApp', [])
.run(['Auth', '$location', '$rootScope', function (Auth, $location, $rootScope) {
$rootScope.$watch(function () {
if (!Auth.isLogedIn())
$location.path("/login");
return $location.path();
});
}])
.config(['$routeProvider', '$httpProvider',
function ($routeProvider, $httpProvider) {
$routeProvider
.when('/home', {templateUrl: 'partials/home.html'})
.when('/login', {templateUrl: 'partials/login.html', controller: 'LoginCtrl'})
.when('/logout', {templateUrl: 'partials/login.html', controller: 'LogoutCtrl'})
.otherwise({redirectTo: '/home'});
$httpProvider.defaults.headers.common["Authorization"] = "";
$httpProvider.defaults.headers.common["X-User"] = "";
}
]);
From code snippet:
$httpProvider.defaults.headers.common will set a headder on each request.
$httpProvider.defaults.headers will set headder only for next request.
On run the $watch set on $rootScope will be triggered on each change to scope isLogedIn() should check the headder token with the entry in the database.
You are right about "every code on client side is not safe."
Your sider side code need to check every request for access privilege. This can be done by session, web token or even http basic authentication. It is very Not secure by restriction from your javascript (ui-router, onStateChange ...)

Close socket.io connection from client on Url change

I am using Socket.IO to pull data for plotting graphs, on
var socket = io.connect();
my Node js server's io.on('connection', function(socket){}) starts a function, on interval of every 1 sec it emits the data to the clients connected.
I want the socket to close on change of Url (away from app) from client and server should stop emitting data.
As we discussed in the comments to the question, you're looking to disconnect from the socket when your Angular ngRoute controller partial route changes away from a given state; this is different as a partial routing single-page application doesn't trigger a fully new page, but rather loads up partial content (defined in partial/template files or script definitions). This is key to why Furkan Başaran's answer won't work for you out of the box for changes between Angular routed states within the same SPA.
I've prepared a plunker that is a fairly basic AngularJS app; it has three html partials that it shows at three corresponding routes using ngRouter and invokes a function to <do-something> when the route change occurs. In this case, it's throwing an alert function, whereas in your case you may which to check for whether or not the route is to anything not the route you want to provide the socket functionality on.
Note: there is a change on every time the Angular app registers a route changes, so the initial establishment will register with a blank value for the / state, then every time it changes from the load, including the otherwise.redirectTo('/destination1').
Plunker (embedded) link:
http://embed.plnkr.co/ayjgYCsox7RGl5OjyGsV/
Quick break down:
I start by defining my handling function to be triggered on ngRouter changes. The passed value is the registered route (after the / or /index.html), such as /potatoes as is a case in my example. This is where you should perform your socket.disconnect();.
function changedMyPartialRoute(val){
alert('url has changed to: ' + val);
}
After I defined my app (angular.module('appName', [...), I define the config for my ngRouter setup.
.config(['$routeProvider', '$locationProvider',
function ($routeProvider, $locationProvider) {
$routeProvider
.when('/meat', {
templateUrl: 'meat.html',
controller: 'MeatCtrl',
controllerAs: 'meat'
})
.when('/potatoes', {
templateUrl: 'potatoes.html',
controller: 'PotatoCtrl',
controllerAs: 'potato'
})
.when('/bacon', {
templateUrl: 'bacon.html',
controller: 'BaconCtrl',
controllerAs: 'bacon'
})
.otherwise({
redirectTo: '/meat'
});
}])
Finally, I invoke a run block on the app (module) to provide that hook into detecting the change. I'm passing in $rootScope and $location and it performs a $watch on the $rootScope to detect a change in the $location.path(). That whole run block:
.run( function($rootScope, $location) {
$rootScope.$watch(function() {
return $location.path();
},
function(val){
// the handling function from step 1
changedMyPartialRoute(val);
});
})
If you need to do something with $rootScope, as I suspect you might, you'll need to hand off the $rootScope handle through the changedMyPartialRoute function or just define your handling inline in the function callback in the .run block. This HTML5rocks.com tutorial shows their configuration in their Angular app by hooking into $rootScope, as opposed to $scope as you mentioned in the above comments.
I hope that helps!
When client closed connection close automatically, if you want see this, write this code to your node server and look to console when closed your client tab.
io.on('disconnect', function() {
console.log("Connection disconnected");
})

Common practice for authentification in AngularJS + Sails SPA

I make a single page application with Sails and AngularJS. And now i'm trying to make an authentication.
I have this angular code on client side:
app.config(['$routeProvider', function($routeProvider) {
$routeProvider.
when('/', {
templateUrl:'pages/postList.html',
controller:'PostListController'
}).
when('/createPost', {
templateUrl:'pages/postCreation.html',
controller:'PostCreationController'
})
}])
Here all .html files are stored at assets/ folder, which is public. But i don't want to have a public access to postCreation.html, i want to permit access to this file for some users using policies.
I think i can put all .html files in views folder, create a controller methods for each file and than use policies. But i'm not sure this is a good solution.
So, how to use policies in Sails + Angular SPA?
You cannot use sails policies for this. Instead, you can use the resolve parameter in your $routeProvider.
From $routeProvider docs:
An optional map of dependencies which should be injected into the controller. If any of these dependencies are promises, the router will wait for them all to be resolved or one to be rejected before the controller is instantiated. If all the promises are resolved successfully, the values of the resolved promises are injected and $routeChangeSuccess event is fired. If any of the promises are rejected the $routeChangeError event is fired.
I have something along the likes of following configured in an app of mine, which is working great (using $stateProvider, but the concept should work just as well for $routeProvider):
.state('stateOnlyCertainUsersCanSee', {
url: "/stateOnlyCertainUsersCanSee",
templateUrl: "myTemplate.html",
controller: 'myController',
resolve: {
validate: function($q, $sails, $state) {
var defer = $q.defer();
$sails.get("/me") // gets user info
.then(
function(response) {
if (response.user.canAccessThisPage) { // Condition on which to pass or fail an user
defer.resolve();
}
else {
defer.reject("No access to page");
$state.go("home"); // Redirect wherever you want
}
}
);
return defer.promise;
}
}
})

AngularJS application architecture

I am relatively new to Angular but I am quite an experienced developer. So far I have made quite some progress in building my application to work with a CMS. I am a bit lost however on what the 'correct' approach would be to handle data in my model.
This is best described with an example:
Because I am hooking up my angular frontend with a CMS, the routing (pages) exist only in the CMS context. This means that the routing should be dynamic as well. I have managed to get the dynamic routes thing to work, but when I try to do things the right way (actually getting data from a server) I run into some issues...
app.config(function($provide, $routeProvider) {
$provide.factory("$routeProvider", function() {
return $routeProvider;
});
});
// Load the dynamic routes from the API...
app.run(function($routeProvider, $http, $scope, logger, siteRoutes) {
$routeProvider.when('/', { templateUrl: '__views/', controller: 'ContentPageController' });
$routeProvider.otherwise({redirectTo: '/'});
});
In other words, I inject a service into my app.run method (siteRoutes) and this one should connect to the API.
So my siteRoutes is a service:
cmsModule.service('siteRoutes', function siteRouteFactory(apiConnection, logger)
// SNIP
And in this service I inject my generic apiConnection service:
cmsModule.factory('apiConnection', ['$q', '$http', '$timeout', 'logger', function apiConnectionService($q, $http, $timeout, logger)
What I want is this:
I would like the siteRoutes service to load the data once and not execute the connection every time. I did this in the following way:
bla.service('example', function() {
var service = {
get: function(apiStuff) { // DO API CONNECT WITH .THEN HERE },
data: {}
}
service.get();
return service;
}
I would like one entry point towards the Api that handles all the $q stuff (my factory) I assumed I need to handle all the .then() stuff in my siteRoutes object, which is what I did.
Now, what happens in my app.run method is that I don't get the siteRoutes object with any data. So I recon I need to do a .then there as well?
But that made me question the entire design of putting all logic in a separate factory for the connection, because I basically like my app to just use the data and have my library deal with the async stuff (if you get what I am saying)...
Hope this is clear.
TL;DR -> How to make your services / factories handle async stuff without making your 'app' deal with it?
The templateUrl property can also be a function that takes the url parametes as input.
In the example below all routes will load a template with same name.
Eg. domain.com/#/blabla.html will load the view blabla.html from the server.
myApp.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/:templateName',
{
templateUrl: function (params) {
return params.templateName + ".html";
}
}
)
.otherwise({ redirectTo: '/main' });
}]);

Resources