angular ui router back button issue - javascript

'use strict';
angular.module('cbApp')
.config(function ($stateProvider) {
$stateProvider
.state('search', {
url: '/college/search',
templateUrl: 'app/collegesearch/views/collegesearch.html',
controller: 'collegeSearchCtrl'
})
.state('searchCollegeFilter', {
url: '/college/search/:streamId?cities&courses&branches&ordering',
templateUrl: 'app/collegesearch/views/collegesearch.html',
controller: 'collegeSearchCtrl'
});
});
Here my application calls the 1st state i.e 'search' with a url /college/search. Inside the controller I transition to another state searchCollegeFilter.
What I wanna do is navigate the user back to the back they came from when they click the browser back button. Say they came from '/' I want them to go back to home page. But in browser back history there are 2 entries for college/search. I want this to happen only for the 1st time.

For this northing is do with angularjs, the thing is you need to watch browser back event before navigating "window.onhashchange". By observing that you can make you check and can redirect default page

What I am doing in a different application would serve the purpose:
Basically: Specify parent states!
-> Then the logic is becoming easy.
You don't have to be specific about history or back button or anything like that.
Basically:
-> Check in
$rootScope.$on("$onstateChange", ...
-> If
the fromState.parent equals toState.parent
then $window.history.replaceState({}, "My Detail State Page", $state.url(toState)

Related

AngularJs : Multiple clicks on same button is not working

I have created a web page in Angular Js. Whenever I navigate to some other page from Home page and click on Home button from that page it navigates back to Home page which is expected and working fine. But when again I click on Home button then I'm expecting a page refresh (because currently it is Home page) but it is not happening.
Angular does not reload the view when the $location of the route you are trying to go to is the same as the current route you are on.
You can use the reload() method to achieve this.
See https://docs.angularjs.org/api/ngRoute/service/$route
(Make sure you switch the api version to match the Angular version you are using)
For a cleaner solution (imho) than using search parameters see the following code (I am using controllerAs syntax):
angular.module('app').controller('MainController', [
'$location',
'$route',
function ($location, $route) {
var main = this;
main.goToHome = function () {
if ($location.path() === '/') {
$route.reload();
}
};
}
]);
in combination with calling that function on your home button link on click:
Home
This function checks the location when you click your button, if the location is / then it reloads the view.
Of course you can replace the route url with whatever url you use for your home route.
The following is an untested, alternative solution I came up with.
In your routes configuration object for the home page (which I am assuming has a url of /home), specify the following parameters:
{
// ...
reloadOnSearch: true,
redirectTo: function (routeParams, path, search) {
if (search.redirected) {
// don't redirect, so return same path + search
return '/home?redirected=true';
} else {
return '/home';
},
// ...
}
The thinking is that when you link to /home, the redirectTo() function will fire and redirect you to /home?redirected=true. That will be a change to the search parameter, so the route should reload correctly due to specifying reloadOnSearch: true. Since the home page links will always be pointing to /home, the page should always reload.
It's a bit ugly, and will likely cause the home page controller to run twice when going from some other page back to the home page, but if you can't get any other way to work, this one might be worth a try.

Can't navigate to ui-router state with URL

I'm working on a simple angular application using ui-router. I have a couple states for selecting and then editing information about a publisher. The relevant config:
.state('select-publisher', {
url: '/select-publisher',
templateUrl: '/Content/superadmin/src/templates/publishers/publishers.html'
})
.state('publisher', {
abstract: true,
url: 'publisher/{id:int}',
templateUrl: '/Content/superadmin/src/templates/publishers/publisher.html'
})
.state('publisher.details', {
url: '/details',
templateUrl: '/Content/superadmin/src/templates/publishers/details.html'
})
.state('publisher.ad-tags', {
url: '/ad-tags',
templateUrl: '/Content/superadmin/src/templates/publishers/ad-tags.html'
})
.state('publisher.native-ads', {
url: '/native-ads',
templateUrl: '/Content/superadmin/src/templates/publishers/native-ads.html'
})
Inside the select-publisher state I have a big list of available publishers. Each one of them is bound to an ng-click event that triggers the following function in my controller:
$scope.selectPublisher = function(publisher) {
publisherService.setSelectedPublisher(publisher);
$state.go('publisher.details', {id: publisher.Id});
};
This works just fine and takes me to the publisher.details state and renders the proper view. At this point the URL in my browser points to localhost:1337/superadmin#/publisher/39/details where 39 is the ID of the publisher that I selected.
The problem is, if I refresh this page or attempt to navigate directly to it by pasting the URL into the browser from another area of the application, I am ALWAYS taken back to the select-publisher state. I would like to be able to configure my states such that I am able to navigate to the details state (or any other state) based on URL.
Worth noting is that I do have a catch all route defined after all of my states:
$urlRouterProvider.otherwise('/select-publisher');
I'm assuming that for some reason this is being triggered but I can't reason as to why navigation works in my app using either $state.go as I have indicated in my controller as well as using ui-sref directive in my HTML templates but not through navigating directly to the URL.
Maybe it's because of missing slash url: /publisher/{id:int}

"Re" resolve resources with angular ui router without reload

I am using the following code to resolve a resource when the main state is loaded. Is it possible to re - resolve the resource without reloading the page? I am avoiding reload so that the user experience is not affected.
$stateProvider
.state('main', {
url: '/',
templateUrl: 'publicApp/main.html',
controller: 'MainCtrl as mainCtrl',
resolve: {
userData: ["UserApi", function (UserApi) {
return UserApi.getUserData().$promise;
}]
}
})
.controller('MainCtrl', function (userData) {
console.log(userData.something);
})
Since is a public site, the user can visit any page without logging in but when the user logs in the page must customize based on the user data.
EDIT
I am using a modal for login so the state doesn't reload after logging in, I was thinking of throwing an event on $rootScope and then add listeners in controllers to load them again. But this doesn't look good, so I am looking for a better way
I currently have two options:
Reload page - will effect the user experience, so its the last option
Throw an event for the login modal and catch it in other controllers
Any better ideas?
Try using state reload once promise is resolved
$state.reload();

Angular JS ui-router how to redirect to a child state from a parent?

When using onEnter to redirect to a state, if the new state is a child of the current state, an infinite loop occurs.
Example:
$stateProvider
.state 'inventory',
url: '/inventory'
templateUrl: 'views/inventory.html'
controller: 'InventoryCtrl'
onEnter: () ->
$state.go 'inventory.low'
.state 'inventory.low',
url: '/low'
templateUrl: 'views/inventory-table.html'
controller: 'LowInventoryCtrl'
When:
$state.go 'inventory.low'
Is called, the state inventory is re-initialized, causing it to be called again = infinite loop.
However, if the redirect state is:
$state.go 'otherStateThatIsNotAChild'
This issue does not occur. I assume that the parent state is being re-initialized, but why?
Why is the parent state being reinitialized when .go is called on a child state?
How then, would you handle redirecting to a child state?
1) Why is the parent state being reinitialized when .go is called on a
child state?
While a transition is in process, any $state.go/transitionTo will cause the currently in process transition to be Superseded. An in-process transition that is superseded is cancelled immediately. Since your original transition to inventory is not completed by the time all the states' onEnters are called, the original transition is cancelled and a new transition to inventory.low is started from the previously active state.
See ui-router src https://github.com/angular-ui/ui-router/blob/master/src/state.js#L897 ...
2) How then, would you handle redirecting to a child state?
You could...
Wrap $state.go in a $timeout() to allow the original transition to complete before redirecting.
Call $state.go from your controller instead. UI-router invokes the controller from the ui-view directive AFTER the transition is completed.
In any case, be very sure you want your app to redirect like this. If a user navigated directly to any other child state of inventory (such as inventory.high), the redirect will still occur, forcing them to inventory.low which would not be what they intended.
I had the same problem. The simple solution I found is to listen state changes and redirect to your child state from there.
It's kind of hack that makes routes redirecting to child states by default. It's not needed to do url: '' or $state.go(), they don't work correctly.
So, in config file:
$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState) {
if (toState.redirectTo) {
event.preventDefault();
$state.go(toState.redirectTo, toParams);
}
});
In state file:
.state('home', {
url: '',
redirectTo: 'home.list'
});
I've made an example on gist: https://gist.github.com/nikoloza/4ab3a94a3c6511bb1dac
You need to step back and think about what you're trying to achieve. What's the point of having a state when all it's doing is redirecting to a child state?
Regarding your first question, parent states are always activated when you arrive at a child state, this behaviour is extremely useful in sharing data among states, and without it nested routing would be impossible (or rather wouldn't make sense).
As for the 2nd question, I've worked on a few big angular apps and so far I haven't found myself needing to do that.
OK, believe it or not, as much I'd hate to say it, right now I came across a scenario where I needed to do this. I have a profile/userName route (technically this should be profile/userName/details) and a profile/userName/products route, I wanted to have a master view for both states but at the same time I wanted the profile/userName route have a clean url, like: profile/mike62, NOT profile/mike62/details. So I ended up doing this:
.state('publicProfile', { url: '/profile/{username}'})//this is the base/shell state
.state('publicProfile.details',{url:null})//I want this to be my de-facto state, with a clean URL, hence the null url
.state('publicProfile.products', {url:'/products/{exceptProductId:/?.*}'})
Ended up achieving it like this, there are many ways though:
in my publicProfile state controller (this is the base state):
$scope.state = $state;
$scope.$watch('state.current', function(v) {
if(v.name=='publicProfile') {//want to navigate to the 'preferred' state if not going to publicProfile.products
$state.go('publicProfile.details');
};
}, true);
Yes, it does feel hacky but now I know that there are some edge cases where we want to do this, althopugh we could rethink our state design altogether. Another, dirtier way would be to check the current state in a $timeout with a small delay inside the base state, if we are not on the publicProfile.products state, we navigate to our preferred/de-facto state of publicProfile.details.
It looks like you're trying to set a default child state.
That's a commonly asked question about ui-router: How to: Set up a default/index child state
The tl;dr is to use abstract states by settings abstract: true in the parent state. If you add multiple child states, it'll default to first child state.
$stateProvider
.state('inventory', {
abstract: true,
url: '/inventory',
templateUrl: 'views/inventory.html',
controller: 'InventoryCtrl'
}).
.state('inventory.low', {
url: '/low',
templateUrl: 'views/inventory-table.html',
controller: 'LowInventoryCtrl'
});
Per pdvorchik's answer on the related GitHub thread, there is a simple fix for this which worked perfectly for me, consisting of wrapping the $state.go call in a .finally() call to make sure the first transition completes before a new one is started.
onEnter: ['$state', function($state){
if ($state.transition) {
$state.transition.finally(function() {
$state.go('home.my.other.state', {})
});
}
}]
After #Chris T's explanation, it seems that the cleanest solution is to listen for the $stateChangeSuccess event:
redirects =
inventory: 'inventory.low'
$rootScope.$on '$stateChangeSuccess', (e, toState) ->
redirect = redirects[toState.name]
$state.go redirect if redirect
Where redirects will contain any route redirects that may occur. Thanks all!
Don't make the low inventory state a child state.
$stateProvider
.state 'inventory',
url: '/inventory'
templateUrl: 'views/inventory.html'
controller: 'InventoryCtrl'
onEnter: () ->
$state.go 'lowinventory'
.state 'lowinventory',
url: '/inventory/low'
templateUrl: 'views/inventory-table.html'
controller: 'LowInventoryCtrl'

Angular UI-router default state on same URL

This is going to be difficult to explain but I'll try.
I am using UI-router in an angular app and would like t use the following URLS:
/contacts
/contacts/{id}
When you visit the /contacts page it will get a list of contacts from the server and display them. When you go to /contacts/1 it will get the contact 1 record from the server and show it.
My code currently looks like this:
.state('contacts', {
url: "/contacts",
templateUrl: "templates/contacts.tpl.html",
controller: "ContactsCtrl"
})
.state('contacts.contact', {
url: "/{contactID}",
templateUrl: "templates/contact.tpl.html",
controller: "ContactCtrl"
})
So far so good. but when you go to the second URL the parent is also activated so it's going to the server to get the list of contacts, even though they're not displayed, which is a waste.
I could set /contacts to "abstract:true" and use /contacts/list as the first URL, but that's not the URL I want to use and I do need to set a controller on the parent because I do have some logic I want to put in the parent (creating the navigation for that section).
Ideally, when the user hits /contacts I'd like the parent state to activate (to create the navigation) and run a default child state to list the contacts without redirecting to another URL. If the user goes to /contacts/8 then It would still activate the parent state but not the default state so it never goes to the server to get the contacts.
I hope that makes sense. I've not been able to create a plunkr, but the Angular UI guys kindly created one which shows the imperfect solution above.
http://plnkr.co/edit/gmtcE2?p=preview
I could set /contacts to "abstract:true"
That would be one part of the correct approach. A parent state should not load data that doesn't apply to a child, but your state tree doesn't have to reflect your URL structure exactly. For example:
.state('contacts', {
abstract: true,
url: "/contacts",
/* Various other settings common to both child states */
})
.state('contacts.list', {
url: "", // Note the empty URL
templateUrl: "templates/contacts.tpl.html",
controller: "ContactsCtrl"
})
.state('contacts.item', {
url: "/{id}",
templateUrl: "templates/contact.tpl.html",
controller: "ContactCtrl"
})

Categories

Resources