Ui-router: how to block url navigation but accept state navigation - angularjs

I try to do some functionality that accepts a state navigation (using $state.go(otherState)), refreshing the associated url in the adress/url bar but it blocks (or redirects to not allowed page) if user directly puts this url in the adress/url bar.
Could it be done by ui-router rules or something inside ui-router module?
I put the example code:
$stateProvider.state("main", {
url: "/index.html",
templateUrl: "main.html"
}).state("notAccessibleScreenByBar", {
url: "/private/example.html",
templateUrl: "example.html"
});
From main view (index.html), the next angular code will be executed:
$state.go("notAccessibleScreenByBar");
This action changes the view, loading example.html and refreshing the url bar to /private/example.html.
If user puts /private/example.html in the adress/url bar, ui-router must block this request (or redirect to not-allowed page).

What you are trying to do seems very similar to any web authentication standard, but, if you don't want that, you could use $locationChangeSuccess (docs here)
A basic example inspired by this sample:
$rootScope.$on('$locationChangeSuccess', function(e, newUrl, oldUrl) {
// Prevent $urlRouter's default handler from firing
e.preventDefault();
if(isThisTransitionValid(newUrl, oldUrl)) {
// Ok, let's go
$urlRouter.sync();
});
});

Related

Can we use event.preventDefault() in angularjs application?

In my ionic application while page load each time it's loading the login page then it's coming to profile page . I think if this works i can can use it in profile controller so that each time i can see the current page which is getting refresh.
Can any body help me out to achieve this?
Again when i am navigating to each page using $state.go("app.profile") or $state.go("app.signup"), Each time it's loading login.html as a splash screen & later it's navigating to the requested page.
My requirement is if I refresh the profile page or signup page it should only reload the current page not login page.
Any help would be appreciated.
You just need to pass the event to the function and handle it from there, it's the clean and best way doing it:
in your view(html):
<button (click)="clicked($event)">click</button>
and in your Javascript (Typescript):
clicked(e) {
e.preventDefault();
//then do whatever you should do here
}
Thanks All ,
My problem got solved . I made two changes in my app.
used preventDefault() as required .
And i changed the $stateProvider to $routeProvider.
inside app.config i put the below code
$routeProvider
.when("/app", {
// controller: AppCtrl,
templateUrl: 'templates/menu.html'
}) .when("/signup", {
// controller: SignupCtrl,
templateUrl: 'templates/signup.html'
}) .when("/profile", {
// controller: ProfileCtrl,
templateUrl: 'templates/profile.html'
});
You can just return false in the event handler
(click)="onClick($event);false"
or
(click)="onClick($event)"
onClick(event) {
event.preventDefault();
// or
return false;
}

Create a non abstract parent with a URL

I have a simple form that can be used to initiate a forecast request. I created this as a parent state requests (Initiate Forecast).
Desired behavior
When a request is submitted, that immediate request is shown in a child state (View Status) as most recent request. This View Status state will also hold a grid of all past requests, meaning I will be refreshing the grid with data every time this state is invoked.
Both parent and child states are navigable from a sidebar menu.
So, if a user clicks on parent (Initiate Forecast), he should be able to see only the form to submit a request. If a user directly clicks on the 'View Status'(child), then he should be able to see both the form and the grid of requests.
app.js
function statesCallback($stateProvider) {
$stateProvider
.state('requests', {
url: '',
views: {
'header': {
templateUrl: 'initiateforecasting.html',
controller: 'requestsInitiateController'
},
'content': {
template: '<div ui-view></div>'
}
},
params: {
fcId: null,
fcIndex: null
}
})
.state('requests.viewStatus', {
url: '/ViewStatus',
templateUrl: 'viewstatus.html',
controller: 'requestsStatusController'
});
}
var requestsApp = angular.module('requestsApp', ['ui.router']);
requestsApp.config(['$stateProvider', statesCallback]);
requestsApp.run(['$state', function($state) {
$state.go('requests');
}]);
Plunker of my attempts so far.
This is all working for me as shown in the plunker, but I am doing it by not setting a URL to the parent. Having no URL for the parent state is allowing me to see both states. If I add a URL to parent state, then clicking on View Status link is not going anywhere (it stays on Initiate).
How do I change this so that I can have a URL for parent state and still retain the behaviour I need from the parent and child states as described above?
Note: I am fine without the URL for parent state in standalone sample code, but when I integrate this piece with backend code, having no URL fragment on the parent state is making an unnecessary request to the server. This is visible when I navigate to the child state and then go to the parent state. It effectively gives the impression of reloading the page which I think is unnecessary and can be avoided if a URL can be set to the parent state.
You shall not directly write url when using ui.router, try like this:
<a ui-sref="requests.viewStatus">View Status</a>
You are writing state name in ui-sref directive and it automatically resolves url. It's very comfortable because you can change urls any time and it will not break navigation.

AngularJs UI-Router - Page Refresh $state.current is now empty

I have an Angular application using ui-router and I am having issues whenever I refresh the page. I am using nested views, named views to build the application. Whenever I refresh the page, ui-router doesn't reload the current state and just leaves the page blank.
On page load $state.current is equal to
Object {name: "", url: "^", views: null, abstract: true}
I am reading my navigation from a .json file via $http and looping through the states. So this is what I can show:
stateProvider.state(navElement.stateName, {
url: navElement.regexUrl ? navElement.regexUrl : url,
searchPage: navElement.searchPage, //something custom i added
parent: navElement.parent ? navElement.parent : "",
redirectTo: navElement.redirectTo,
views: {
'subNav#index': {
templateUrl: defaults.secondaryNavigation,
controller: 'secondaryNavigationController as ctrl' //static
},
'pageContent#index': {
template: navElement.templateUrl == null
? '<div class="emptyContent"></div>'
: undefined,
templateUrl: navElement.templateUrl == null
? undefined
: navElement.templateUrl,
controller: navElement.controller == null
? undefined
: navElement.controller + ' as ctrl'
}
}
});
This code gets executed for each item in the nested json object. If there is anything else that would be helpful, let me know.
There is a question: AngularJS - UI-router - How to configure dynamic views with one answer, which shows how to do that.
What is happening? On refresh, the url is evaluated sooner, then states are registered. We have to postpone that. And solution is driven by UI-Router native feature deferIntercept(defer)
$urlRouterProvider.deferIntercept(defer)
As stated in the doc:
Disables (or enables) deferring location change interception.
If you wish to customize the behavior of syncing the URL (for example, if you wish to defer a transition but maintain the current URL), call this method at configuration time. Then, at run time, call $urlRouter.listen() after you have configured your own $locationChangeSuccess event handler.
In a nutshell, we will stop URL handling in config phase:
app.config(function ($urlRouterProvider) {
// Prevent $urlRouter from automatically intercepting URL changes;
// this allows you to configure custom behavior in between
// location changes and route synchronization:
$urlRouterProvider.deferIntercept();
})
And we will re-enable that in .run() phase, once we configured all dynamic states from JSON:
.run(function ($rootScope, $urlRouter, UserService) {
...
// Once the user has logged in, sync the current URL
// to the router:
$urlRouter.sync();
// Configures $urlRouter's listener *after* your custom listener
$urlRouter.listen();
});
There is a plunker from the linked Q & A
I don't know how are all your routes.. but if you refresh a page of a child state, you need to pass all parameters of the parents states to be resolved correctly.

Angular - UI Router - state reentrance

How to config UI Router to reenter or reload the state by default?
E.g. user wants to refresh page, so he clicks the link that follows to that page. But currently the link isn't clickable, as it goes to the same page and the state doesn't change. Refreshing with browser button does work, so it reloads entire SPA again - isn't desirable.
This question - Reloading current state - refresh data - describes similar effect. Is it possible to change this solution - https://stackoverflow.com/a/23198743/404099 - to define the default behavior?
By the way, probably my approach is incorrect and I'm missing some basic idea in UI Router. Please point out if this is the case.
I second what #Sabacc said, but if you still instead, here you go:
angular.module('app')
.config(function($provide) {
$provide.decorator('$state', function($delegate)
{
$delegate.go = function(to, params, options)
{
return $delegate.transitionTo(to, params, angular.extend(
{
reload: true,
inherit: true,
relative: $delegate.$current
}, options));
};
return $delegate;
});
});

Redirect from Angular JS not working for URL with hash '#'

Background of how I am initializing my AngularJS app is here :
SCRIPT5022: 10 $digest() iterations reached. Aborting! and redirecting to index.html
Summery Problem
When I do a window.location = $scope.myReturnUrl and if the return URL contains a "#", instead of reloading the page, AngularJS captures the url change and it goes to .otherwise part of rout provider setting, and Angular App is loaded again. But, I am expecting it to redirect to specified URL.
Brief of how I am doing this:
I am loading my AngularJS app inside a Bootstrap Modal, this is how I initialize
var markup = '<div id="ng-app" ng-app="myapp" xmlns:ng="http://angularjs.org"><div ng-controller="MyAppAppCtrl"><div ng-view></div></div></div>';
jQuery('#works-modal').html(markup);
angular.bootstrap(jQuery('#ng-app'), ['myapp']);
jQuery('#works-modal').modal('show');
in my app.js, html5mode is set to false for all modules :
$locationProvider.html5Mode(false)
So, when my app is initialized, the url look something like :
http://my-site.uk/user/dashboard.html#/mywork/find/id/9780273651
Now, there is a cancel button on Modal dialog, click on which I am trapping through ng-click, and when clicked, it simply do a redirection something like
window.location = $rootScope.returnToUrl;
now, in my case, return url contains id of the link that was clicked, so that browser can scroll to particular section, the return url is :
http://my-site.uk/user/dashboard.html#my-work1
my rout provider config
$routeProvider.
when('/mywork/find/:id', {
templateUrl: '/mywork/partials/find/find.html',
controller: WorkCtrl
}).
when('/mywork/find/review/submit/:id', {
templateUrl: '/mywork/partials/review.html',
controller: ReviewCtrl
}).
otherwise({
redirectTo: '/mywork/find/id/'+id
});
Problem
Now, when I am trying to redirect to : http://my-site.uk/user/dashboard.html#my-work1
instead of reloading page and scrolling to designated #ID, AngularJS is capturing the URL change and reloading the app instead. this happens because, AngularJS captures the url change and it goes to .otherwise part of rout provider setting, and Angular App is loaded again. But, I am expecting it to redirect to specified URL.
I am use html5mode(true) due to historic reason (mentioned in this thread : SCRIPT5022: 10 $digest() iterations reached. Aborting! and redirecting to index.html)
I don't want to remove the # from my return url because it will break the workflow. Any suggestion how to overcome this?
Thanks,
Ravish
So it sounds like you want http://my-site.uk/user/dashboard.html#my-work1 to redirect you to dashboard.html and scroll to the element with id="my-work1".
You might want to take a look at the $anchorScroll(http://docs.angularjs.org/api/ng.$anchorScroll) service. I'm not sure how it works exactly as I've never needed to use it, but I know it solves problems such as yours.
in your URL (http://my-site.uk/user/dashboard.html#my-work1) try pass 'my-work1'as a parameter in routeProvider like http://my-site.uk/user/dashboard.html/my-work1.
example:
when('/my/route/:param', {
templateUrl: '/mywork/partials/param.html',
controller: WorkCtrl
})
in your controller, get this parameter with $routeParams.
var myParam = $routeParams.param
$location.hash(myParam)//set anchor
$anchorScroll()//do scroll
I hope this solve your problem

Resources