ui-router: open modal on $stateChangeEvent - angularjs

Some states in my app require further authentication. Eg a Edit state should prompt the user to enter a one time password.
I would be nice to solve this horizontally: attach metadata to the states requiring extra auth (ie state.requiresExtraAuth: true), and plug some modal logic on the state change events. NOTE: What I am after is that the state does indeed load and the user can see it, but a modal forbids the user from continuing without entering the password.
I tried adding a 'onEnter' property to the toState object param I get in the event listener function, but it didn't work :).
Is something like that possible/how would you go about designing this use case?

In run method of you application.
add.run(function($rootScope, $state, $modal){
$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams){
if(toState.name != 'app.home') {
$state.go('app.login');
// or open modal
$modal.open({...});
}
});
});
This event will help you to check what state user try to access and redirect to login form if needed.

Related

How to maintain the user session without data loss and without refreshing the page in Angular js

I have implemented an application:
Technology used: -- Angular, Mvc, WebApi.
How I have calculated the session in the application means?
When user login into the app, Set the logout time , Before 3 minutes set the warning message to the user.In the warning message, there is an proceed button and close button.
Proceed button is for continue the user to another 15 minutes. Cancel button is for cancel the warning message.
Implementation
Here how I capture the Session
Angular run block:
myApp.run(function ($rootScope, $cacheFactory, $interval, $timeout, $state) {
// here I have implemented the logic
// set the 15 minutes to the logout.
$rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams, $rootScope, $state, $location) {
lastDigestRun = Date.now();
if(current url != login url)
{
// show the warning message to the user ,in 2 minutes have remaining logout.
}
//In the warning message,there is an ok button for maintating the session to another 15 minutes,This is the code
$('#ok').click(function () {
location.reload();
})
});
}
What's the problem over here is:
Implementation technology.
I have using angular ui router navigation between the pages. if
any navigation between the pages, comes to run block and state
-router and reload the session for the user as active. working as expected.
Problem for mine.
if any of the nested views , there are 100 textboxes , user is enter
the data,after 13 minutes he gets the warning alert.
user not made any changes, gets the warning message.
At that time, notification comes and click the Proceed button ,the page gets refreshed , and
the data gets losses.
Need to Handle :
In the OK button,need to handle with out dataloss for the ui-router views. Here reload cause data loss in the textbox.
In that Scenario, need to handle the Data loss. How to handle that any one could suggest is it possible, is there any solution?

Angular - How to check if user is allowed before loading the view?

I have an angular app. I am using ui-router to manage my states. I need to call a common API to check whether the user is allowed to view the web page or not. Is there a common place where I can call the API and only then proceed to the state the user is requesting for?
With my current implementation, the view partially gets displayed and then the reroute happens.
//app.js
$rootScope.$on('$stateChangeSuccess', function(evt, toState) {
if(toState.name === 'login' || toState.name === 'payment')
return;
userService.isBillingCleared().then(function() {
//redirect to toState
}, function() {
$state.go('payment');
});
});
How do I prevent this?
Try the following
$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams, options) {
if(toState.name === 'login' || toState.name === 'payment')
return;
event.preventDefault(); // prevent the state change
userService.isBillingCleared().then(function() {
$state.go(toState.name); // do the state change manually if allowed
}, function() {
$state.go('payment');
});
}));
Try this link which talks about authentication for routes in ui-router. Seems like this would be a good solution for you.
I was facing similar issue while developing a system and the only difference was that I was using default router that is provided by angular but obviously that wont cause any problem.
Keep the data hidden before checking the authentication level of the user and make directive to centralize the whole process. Let's imagine it this way - the view in question that you'll be showing to the user is hidden unless the details are being checked and until then show them any preloader or something or whatever animation you want to.
This is what generally people do in case of a web app or any mobile application, clog the data unless you are sure that yes the data is ready to be viewed.
This practice can be useful even in use case where you are loading the data after the page is being loaded, for e.g. let's say I am showing transactions to user but the transactions are loaded only after the user calls for the view so a call to the backend is made when the view is called that means the user can see for a split second only - an empty table. So...just make use of a preloader and call for the function first and then let the user see the table.

AngularUI pattern to forward to url after login

So I have a scenario where a user can provide a url like:
http://localhost:8080/#/component?id=1234
The first thing AngularUI does is check if the user is authorized.
If it's not authorized it's forwarded to the login page using
$state.go("login");
My intention is to record the url in
$rootScope.$on("$stateChangeStart"....
Save it in the rootscope and then forward to that url once the login is done.
However, I can't seem to find a way to maintain the url with the query parameter component?id=1234. should I use $location or $state. I can get it to work with the state name only (using $state) but not with parameters.
I don't know if your solution is the best one there. But if you want to do that, you can simply write a function like this:
$rootScope.$on("$stateChangeStart", function(event, toState, toParams, fromState, fromParams){
/* your code here */
})
and you can navigate to any state with any parameters by doing something like:
$state.go("state_name",{/*parameters as an object e.g. id:1234*/});
You can also read the wiki on angular ui-router's page.
Hope I could answer your question.
Actually turns out I can just use the $location object.
storing $location.url() in the rootScope. and then navigating using $location.url(savedUrl) to redirect it back.

Prevent a stateChange with angular ui router without using $rootScope

My user can leave a state but before I want to show a modal dialog "Do you want to save?"
ONLY if the user data is dirty that means changed.
What I do NOT want is to stick a isDirty property in my EditController to the $rootScope go to the stateChangeStart event and check there for isDirty and then show/not the save dialog.
Prevent global variables says every javascript beginner book...
1.) What is then the pro way to prevent a state change without hacking the $rootscope?.
2.) Are there any helper libraries for ui-router which enhance the ui-router offering function hooks inside the controller to encapsulate the ui logic?
(1) According to the docs under State Change Events
$rootScope.$on('$stateChangeStart',
function(event, toState, toParams, fromState, fromParams){
event.preventDefault();
// transitionTo() promise will be rejected with
// a 'transition prevented' error
})
You could change $rootScope to $scope wherever appropriate and works.
Under Attach Custom Data to State Objects, you can pass on custom data.
(2) I'm not sure what you're asking but factories/services/providers would really help.
Using $transitions.onStart (angular-ui-router 1.0.0-rc) you can return a boolean. If false the transition will be cancelled.
$transitions.onStart({}, function (trans) {
var answer = confirm("Want to leave this page?")
if (!answer) {
return false;
}
});
Here is the documentation: https://ui-router.github.io/ng1/docs/latest/modules/transition.html#hookresult
Though at the time of writing it is not a part of the stable release, the 1.0 release of the UI-router will use the return value of onEnter/onExit to prevent navigation.
See GitHub issue 2219

With angularjs and ui-router, how to disable the navigator history for one view change

I want the current state change not to be saved in navigation history. Then when user clicks 'back', the view will be skipped and go back to previous page.
Can I temporally disable the state change to be saved in navigator history?
I believe this where ui-router's option parameter with value { location:'replace'} is intended for.
The documentation explains:
location Boolean or "replace" (default true), If true will update the
url in the location bar, if false will not. If string "replace", will
update url and also replace last history record.
It seems that there's an issue (see it here on GitHub) to be fixed though.
In the spirit of stackoverflow I will try to answer the question and not mention that what you are asking for is a truly horrible hack ;-)
I think you may need something like this. (the code is coffeescript)
angular
.module 'app', ['ui.router']
.run ($rootScope, $state) ->
$rootScope.$on '$locationChangeStart', (event) =>
if $state.is 'bad-state'
event.preventDefault()
$state.go 'good-state'
When the location changes (you could also use $stateChangeStart) you check the current state and if it is the state you don't want then you go to the state you do want. the event.preventDefault() effectively halts the processing of the originally requested state.
If you need an even dirtier version then you could check the location urls instead of the states and use $location.url to perform the transfer, like this:
angular
.module 'app', ['ui.router']
.run ($rootScope, $location) ->
$rootScope.$on '$locationChangeStart', (event, nextLocation) =>
if nextLocation is '/bad/location'
event.preventDefault()
$location.path '/good/location'
This worked for me.
$rootScope.$on('$stateChangeStart',
function(event, toState, toParams, fromState, fromParams){
event.preventDefault();
window.history.forward();
});

Resources