I am stuck with this strange behaviour. None of the Google hits seem to return any mention of a similar case so I am assuming that I am doing something seriously wrong.
My interceptor does react on the 401 status and redirects to the appropriate route but at the same time it renders the previous route too. So I have a very ugly flash of one template then the other. I am simply trying to test my authentication: if authenticated then render the table, otherwise render the login form. It does redirect to the login form but still flashes the table template for a fraction of a second.
angular.module('Basal', ['ngRoute', 'Basal.users'])
.config(function($routeProvider, $locationProvider, $httpProvider) {
$locationProvider.html5Mode(true);
$httpProvider.interceptors.push('authInterceptor');
$routeProvider.
when('/', {
templateUrl: '/tpl/table.html',
controller: 'MainCtrl'
}).
when('/login', {
templateUrl: '/tpl/login-form.html',
controller: 'MainCtrl'
}).
otherwise({
redirectTo: '/'
});
});
angular.module('Basal.users', [])
.controller('MainCtrl', function($scope, getJson) {
getJson.fetch(function (d){
$scope.computers = d;
});
})
.factory('getJson', function($http, $location) {
return {
fetch: function (c) {
$http.get("/json")
.success(function(data) {
console.log("getJson: Success!");
c(data);
})
.error(function() {
console.log("getJson: Failure!");
});
}
}
})
.factory('authInterceptor', function($q, $location) {
return {
'responseError': function(response) {
if (response.status === 401) {
$location.path('/login');
}
return $q.reject(response);
}
}
});
Now, when I hit '/' on the browser Angular does two requests on the background: one is to fetch the table template and insert it in to the view and the other is to get the JSON data.
On the server side I put session restriction only on the data request! The template is just a static file. To put restriction on the template I need to drag it through the server routing and this is not, I believe, how Angular does things. But if I do create server side route for the template and put session restriction on it then double rendering disappears.
I am confused about how this works. Fetching the template and fetching the data is done asynchronously in parallel. So while JSON request triggers 401 and redirects to the login template, the original table template is still going through and being rendered empty. Hence I get all the ugly double rendering. This is a race of some kind. How do I stop it? Maybe my interceptor is wrong? Isn't the Angular interceptor supposed to stop any further processing?
As a related issue, the otherwise function on the $routeProvider does not work either. If I put a non-existent URL, I get 404 from the server but Angular routing does not catch it. Is this how it is supposed to be? If a URL change happens in Angular and then I hit reload on the browser, I get an ugly 404 instead of a nice redirect. Am I supposed to handle 404 in an interceptor too? Then what is the point of otherwise?
Thanks.
Related
I have the following routing config
myApp.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/', {
templateUrl: 'partials/list.html',
controller: 'contactListCtrl'
}).
when('/new', {
templateUrl: 'app/partials/form.html',
controller: 'contactAddCtrl'
}).
otherwise({
redirectTo: '/'
});
}]);
my home page('/') simply loads a number of contacts and its contactListCtrl is like
myApp.controller('contactListCtrl', function($scope, $http) {
$http.get("list")
.then(function(response) {
$scope.contact_list = response.data;
});
});
So when I load the home page on browser, it loads the template and then send another request ('/list') to server to get the contacts (json). so the browser sends 2 requests to the server.
Is this normal in AngularJS development to send 2 requests simultaneously to get template along with data? Is there any work around to send only 1 request to get both template and data ?
If I need send 2 requests in this case and suppose I have to load 5 templates in home page then I have to send another 5 requests to bind data (from database) to each template ?
You can use resolve in the route, that ill load the data and than load your template. You can check here for solution http://odetocode.com/blogs/scott/archive/2014/05/20/using-resolve-in-angularjs-routes.aspx
I have been driving myself crazy. Hoping one of you can help me out here...
Let me give you some background:
I have an ASP.NET web app that uses AngularJS.
Here is what my Controller looks like:
public ActionResult Index()
{
return View();
}
Ultimately this loads my HomePage and everything comes up great. However when I click a link for example:
<a ui-sref="core.applications">My Apps</a>
It prompts me to enter in a Username and Password. Even though this should be set for anonymous access.
Now if I open a new tab and go directly to that page for example: http://localhost/#/core/applications this loads just fine.
Looking at my routes:
app.config(function ($stateProvider, $urlRouterProvider, $httpProvider) {
$urlRouterProvider.otherwise('/core/dashboard');
$stateProvider
.state('core', {
url: '/core',
views: {
'': { templateUrl: 'app/core/views/core.html' },
'sidebar#core': {
templateUrl: 'app/core/views/sidebar.html'
},
'content#core': {
templateUrl: ''
}
}
})
.state('core.dashboard', {
url: '/dashboard',
views: {
'content#core': {
templateUrl: 'app/core/views/dashboard.html'
}
}
})
.state('core.applications', {
url: '/applications',
views: {
'content#core': {
templateUrl: 'app/core/views/applications.html'
}
}
})
});
I do not see what could be blocking it and prompting it to load a Username and Password. Is it something to do with ASP.NET Routing? Please help!
I actually figured out the issue.
The issue was related to the Web API call that I do that I pass security credentials and if the credentials end up being repeated too quickly my system blocks them... (dumb enterprise security guys)
Long story short - check if you are calling a 3rd party service this could prevent you from accessing those pages.
I am using AngularJS in a nodewebkit application.
I have three views:
Home.html
Conversation.html
Login.html
On login, I am calling
$state.go('home.main');
which calls
$stateProvider.state('home.main', {
url: '/home',
views: {
"mainContent": {
templateUrl: 'views/home.html',
controller: 'loginController'
}
}
}
In main html, I am making a server call (through socket.io) to get all conversations.
Since data is huge, it takes some time to load it and in this time gap i.e. before user gets response from server, If user clicks on Logout, It takes user back to login page.
$scope.logout = function(){
//Logout Logic
$state.go('login');
}
i.e. it calls
$stateProvider.state('login', {
url: '/login',
templateUrl: 'views/login.html',
controller: "loginController"
});
Now When a user is trying to login, server responds to call made for getting conversations allowing user to take him to /home/conv.html.
I don't want to disable Logout button while data is being sent from server to client.
Is there any way on routing from /home to /login, we can cancel all server calls?
I copied the code from https://github.com/angular-ui/ui-router/wiki:
$stateProvider.state("contacts", {
template: '<h1>{{title}}</h1>',
resolve: { title: 'My Contacts' },
controller: function($scope, title){
$scope.title = 'My Contacts';
},
onEnter: function(title){
if(title){ ... do something ... }
},
onExit: function(title){
if(title){ ... do something ... }
}
})
The onExit event will be triggered when you move from home.main to another route. So you should register to handle the event at where you declare the home.main route. In this event, you can cancel all server calls. As you are using socket.io, I suggest you use the function socket.removeAllListeners();
I have an <ng-view></ng-view> that is filled with a partial html file when a button is clicked i.e Sign In
myApp.js
var myApp = angular.module("myApp", ['ngRoute']);
//Define Routing for app
myApp.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/signin', {
templateUrl: 'signin.ejs'
}).
when('/signup', {
templateUrl: 'signup.ejs'
}).
when('/myAccount', {
templateUrl: 'myAccount.ejs'
}).
otherwise({
//home page
});
}]);
What I want to know is, how can fill this ng-view based on data that is sent by the server. For example, if the server renders the index.html with data {page: '/signin'} how can I let Angular know that I want to to populate the ng-view with 'signin.ejs'??
Any help is appreciated. Thank you!!
EDIT:
I have someone logged in to the site and on a page I provide a "switch account" button. So what I want to do is, when that is clicked, post to the server /logout route so the session can be cleared, and then change the page to the signin page (partial html file) and populate the username field with the account that they are switching to
Your ejs needs to be rendered by nodejs so you need to apply your the routing there.
You need to come up with a way to identify the route path like '/partials/:filename'
app.route('/partials/:filename').get(funcs.partials);
Then you handle the request and render your ejs or plain you just send(html) and you use the path module to locate and manipulate the url string.
exports.partials = function(req, res, next, filename) {
res.render(filename, vars);
};
Then your angular is like, though signin is probably not the best example.
$routeProvider.
when('/signin', {
templateUrl: '/partials/signin'
})
I'm working on a project which has several views, which some of the views may not be accessible to a given user. What I want to do is that, if a user navigates to a url that is restricted (due to his permissions), lets say '/project/manhatten/security' I want to display a view (an html partial) which simply says 'Access Denied'.
But I want to display the view without changing the url. I want the url to stay '/project/manhatten/security', so the user can copy the url and give it to someone with enough permission, and it would work fine.
What is the best way to achieve this ? Can I still use ng-view or a combination of ng-view and ng-include ?
Thanks in Advance.
I don't know of a way on how to restrict access to a specific view in angular. I think you shouldn't restrict views. What you should do is restrict access to your api. So if a user doesn't have the privilege to delete a project. Simply send a 401 from the server when he calls the api. On the client side handle this 401 in angular with an $http interceptor.
I would do the following:
In your index.html create an element/directive to display the error message
index.html
<div ng-controller=ErrorMessageCtrl ng-show=error.message>
{{error.message}}
<ng-view></ng-view>
The ErrorMessageCtrl will get notified when an access denied error occured:
.controller('ErrorMessageCtrl', function ($scope) {
$scope.error = {}
$scope.$on('error:accessDenied', function(event, message) {
$scope.error.message = message
})
})
Create an interceptor service to handle http 401 auth error:
.factory('authErrorInterceptor', function ($q, $rootScope) {
return {
response: function (response) {
return response
},
responseError: function(rejection) {
if (rejection.status === 401) {
$rootScope.$broadcast('error:accessDenied', 'Access Denied')
}
return $q.reject(rejection)
}
}
})
add the interceptor service to $httpProvider
.config(function ($httpProvider, $routeProvider) {
$httpProvider.interceptors.push('authErrorInterceptor')
$routeProvider.when('/project/manhatten/security', {
template: '<div><h1>Secure Page</h1>secure data from server: {{data}}</div>',
controller: 'SecureDataCtrl'
})
})
$http.get('/api/some/secure/data') returns a 401 http status code
.controller('SecureDataCtrl', function ($scope, $http) {
$scope.data = 'none'
$http.get('/project/manhatten/security')
.success(function (data) {
$scope.data = 'secure data'
})
})
Please keep in mind that this was hacked in 10 minutes. You need to do some refactoring. Like creating an errorMessage directive which gets the error notification service injected and not broadcasting the error message to the scope.