Angular ui-router bound state to URL - angularjs

I'm struggling with this counfiguration of $stateProvider and $urlRouterProvider for days now.
My problem is, in fact, that after I use $state.go(), state changes normally, but url in browser stays the same (#/).
Additionaly, when I set the $urlRouterProvider.otherwise('home'); option, after every $state.go() router redirects to /home, no matter what state was picked.
My configuration:
StateConfiguration.$inject = ['$stateProvider', '$urlRouterProvider'];
function StateConfiguration($stateProvider, $urlRouterProvider){
$urlRouterProvider.otherwise('home');
addState("state1", "/state1", "<example-page></example-page>");
addState("state2", "/state2", "<example-page2></example-page2>");
addState("register", "/register", "<register-page></register-page>");
addState("error", "/error", "<error-page></error-page>", {
error: null
});
addState("myLibrary", "/my-library/{groupId}{groupName}", "<my-library></my-library>", {
groupId: null,
groupName: null
});
addState("home", "/home", "<main-page></main-page>");
///////////////////////////
function addState(stateName, url, template, params){
$stateProvider.state(
stateName,
{
url: url,
template: template,
params: params || {}
}
);
}
}
Can somebody point me, where I have error? Every state just uses another directive.
My goal is that when application changes state, the url in browser changes as well. Additionally, when user enter url like app.com/state-url, application redirects to state with url state-url. How should I do it?
UPDATE
As requested, I'm adding $state.go() calls, but they're not very "big".
service.home = function(params){
$state.go("home", params);
};
service.register = function(params){
$state.go("register", params);
};
Also, after looking closer, after going to register state, url changes for 0.5 s to register and then, goes back to home.

Your use of this is wrong:
$urlRouterProvider.otherwise('home');
The clue is in the name (urlRouterProvider) - it deals with url, not state, so you should do it like this:
$urlRouterProvider.otherwise('/home');
I can't comment on your $state.go calls, because you haven't provided examples of those.

Related

My state url is matched even if parameter has no value, how do I transition to default state?

This is my UI-Router state:
$stateProvider.state("project",{
url:'/project/:projectid/:typeid/:directoryid',
templateUrl: '/app/project/view.html',
controller: 'projectViewController',
params:{
typeid:{squash:true,value:null},
directoryid:{squash:true,value:null},
}
});
These urls are working fine:
/project/5
/project/5/1
/project/5/1/2
However, this url "/project" is working fine (or being matched), but it should not be.
:projectid should be required. If no value is passed, the url should not match and "otherwise" should switch to default state.
How do I make it so that projectid is required?
The "typeid" and "directoryid" parameters should be optional.
check condition through resolve like as
$stateProvider.state("project",{
url:'/project/:projectid/:typeid/:directoryid',
templateUrl: '/app/project/view.html',
controller: 'projectViewController',
params:{
typeid:{squash:true,value:null},
directoryid:{squash:true,value:null},
},
resolve: function($stateParams, $location){
//Check if url parameter is missing.
if ($stateParams.projectid === undefined) {
//Do something such as navigating to a different page.
$location.path('/'); // put any location where you want to redirect
}
}
});

How to catch the invalid URL?

I am building a simple HTML site using Angular JS ui-router.
I have built a custom 404 page ('www.mysite/#/error/404'), to which the user is redirected if the user types a url like 'www.mysite/#/invalidUrl'. This functionality was achieved by the following code snippet:
$urlRouterProvider
.when('', '/')
.when('/home', '/')
.otherwise('/error/404');
And the following state snippet:
$stateProvider.state('404', {
parent: 'app',
url: '^/error/404',
views: {
'errorContent#main': {
controller: 'error404Controller',
templateUrl: 'js/general/templates/error404.html'
}
}
});
I am trying to capture the invalid URL requested by the user to be displayed to the user like below.
The URL you requested cannot be found
Request: www.mysite.com/#/invalidUrl
I have tried listening to '$stateNotFound' event, which only seems to be firing when a specific invalid state is requested. I've also tried to listening to events '$stateChangeStart', '$locationChangeStart', and '$locationChangeSuccess' but couldn't find a way to achieve this functionality.
Is this possible? Please, share your thoughts - thank you
Awesome! Thank you Radim Köhler. I don't know how I didn't land on that thread when I searched but that helped me get the answer.
I changed the answer in that thread to fit my scenario which I am going to leave below for reference to any other users.
$stateProvider.state('404', {
parent: 'app',
url: '^/error/404',
views: {
'errorContent#main': {
controller: 'error404Controller',
templateUrl: 'js/general/templates/error404.html'
}
}
});
$urlRouterProvider
.when('', '/')
.when('/home', '/')
.otherwise(function ($injector, $location) {
var state = $injector.get('$state');
state.go('404', { url: $location.path() }, { location: true });
});
So the magic change was the function defined for 'otherwise' option. I didn't change the URL of the 'error' state. Instead, I used the 'state.go' to activate state '404' and passed the location path as the state parameter.
Setting location=true will take you to the URL of the state, and location=false will not. Whichever way you prefer the invalid URL can be accessed easily now.

AngularJS. Retain state after page reload

I am using UI Router with html5Mode enabled, states are loaded from JSON.
Expected behavior after F5 or when pasting URL is, respectively, having current state reloaded or navigating to the said state, instead the initial application state is loaded.
For e.g. root/parent/child gets redirected to root/.
By the way, navigating with ui-sref works fine.
So, how can the state be retained after page reload?
In order to retain the state of page after reload app, a url represent the state should be gave. when you include ui-route module, url will be parsed and sent to corresponding state. You don't need to parse the url handly in most cases, ui-route born to do this.
Please can you post your code here? Specifically the $stateProvider.
This is an example of a correct $stateProvider and it works fine:
$stateProvider.state('main.admin', {
url: '/admin',
resolve: {},
views: {
'main-content#main': {
controller: 'AdminController as admin',
templateUrl: 'main/admin/admin.tpl.html'
}
}
});
Seems a bit hacky, but works for now.
app.run(['$location', '$state', function ($location, $state) {
function stateFromUrl () {
var path = $location.path(),
hash = $location.hash();
// do JSON states map parsing and find a corresponding to the URL state
return state;
}
if (stateFromUrl) {
$state.go(stateFromUrl);
} else {
$state.go('home'); // initial state
}
}]);

Angular UI Router - Default Parameter

Is there anyway to specify a default parameter for every route using the Angular UI Router?
My app is entered through the context of another application by selecting a user and then navigating to my application. The URL in my application will always have the user ID in the URL so that people can bookmark the URL, email it, etc. So, as you navigate around, the URL always follows a scheme of:
#/{userId}/view/...
#/{userId}/edit/...
etc.
This userId will always be the same for a user inside the app for any route they go to. If they happen to log out, go back to the main app, select a new user and come back to my app, this userId will change, but will be the same value for every route.
Is there anyway to read this value from say a service/factory and then plug it into every route?
EDIT:
I should mention I want to avoid having to explicitly set this parameter on every route when I navigate to a state. For example, I don't want to have to do ui-sref="new-state({userId : blah})" every time I navigate to a new state. That userId will never change in the context of my application.
EDIT AGAIN:
I actually went about this a different way concerning the requirement to not have to send 'userId' to every route manually. Instead of using a directive, I used a $provide.decorator to add this functionality to the 'go' method. I've added an answer below to what I did.
You can declare an abstract parent state from which child states inherit:
$stateProvider
.state('user', {
url: '/:userid',
abstract: true,
resolve:
// assuming some kind of User resource factory
currentUser: function($stateParams, User) {
return User.get($stateParams.userid);
}
}
})
.state('user.view', {
url: '/view', // actual url /:userid/view
controller: function($scope, currentUser) {
// currentUser resource available
}
});
.state('user.edit', {
url: '/edit', // actual url /:userid/edit
controller: function($scope, currentUser) {
// currentUser resource available
}
});
In terms of navigating to a state, you need to pass in the desired user:
$state.go('user.view', {userid: 'myuserid'});
As a consequence it might make sense to create some kind of .go() wrapper method on your currentUser service, so that you needn't specify the user id each time.
UPDATE:
To counter the problem posted in your edit, you could introduce a directive like this:
angular.module('app')
.directive('userSref', function($state) {
return function(scope, elem, attrs) {
var state = 'user.' + attrs.userSref;
elem.bind('click', function() {
$state.go(state, {userid: $state.params.userid});
});
scope.$on('$destroy', function() {
elem.unbind('click');
});
};
});
Then, any future links to user-based states can be done so with:
<a user-sref="view">View User</a>
Instead of writing a directive that handled the auto-sending of userID, I used $provide.decorator as follows:
app.config(['$provide',
function($provide) {
$provide.decorator('$state', function($delegate, UserService) {
// Save off delegate to use 'state' locally
var state = $delegate;
// Save off reference to original state.go
state.baseGo = state.go;
// Decorate the original 'go' to always plug in the userID
var go = function(to, params, options) {
params.userID = UserService.userID;
// Invoke the original go
this.baseGo(to, params, options);
};
// assign new 'go', decorating the old 'go'
state.go = go;
return $delegate;
});
}
]);
I got the idea from this post:
Changing the default behavior of $state.go() in ui.router to reload by default
You can use the "nested states" and "resolves" features of UI-Router to create a hierarchy of states in your app. You'll define a top level state that resolves the userId. Then define any number of child states, and they will automatically inherit the "resolved" userId.
Check out this page of the documentation, in particular the section titled "Important $stateParams Gotcha". I will paste the two code snippets from that page here.
Incorrect method:
$stateProvider.state('contacts.detail', {
url: '/contacts/:contactId',
controller: function($stateParams){
$stateParams.contactId //*** Exists! ***//
}
}).state('contacts.detail.subitem', {
url: '/item/:itemId',
controller: function($stateParams){
$stateParams.contactId //*** Watch Out! DOESN'T EXIST!! ***//
$stateParams.itemId //*** Exists! ***//
}
})
Correct method using "resolves":
$stateProvider.state('contacts.detail', {
url: '/contacts/:contactId',
controller: function($stateParams){
$stateParams.contactId //*** Exists! ***//
},
resolve:{
contactId: ['$stateParams', function($stateParams){
return $stateParams.contactId;
}]
}
}).state('contacts.detail.subitem', {
url: '/item/:itemId',
controller: function($stateParams, contactId){
contactId //*** Exists! ***//
$stateParams.itemId //*** Exists! ***//
}
})
Since the "contactId" parameter is resolved by the parent state, the child state will inherit that.

How not to change url when show 404 error page with ui-router

I want to show 404 error page, but also I want to save wrong url in location.
If I'll do something like that:
$urlRouterProvider.otherwise('404');
$stateProvider
.state('404', {
url: '/404',
template: error404Template
});
url will change to /404. How I can show error message on wrong urls without changing actual url?
There is solution for this scenario. We'd use 1) one ui-router native feature and 2) one configuration setting. A working example could be observed here.
1) A native feature of ui-router state definitions is:
The url is not needed. State could be defined without its representation in location.
2) A configuration setting is:
otherwise() for invalid routes, which parameter does not have to be a string with default url, but could be a function as well (cite):
path String | Function The url path you want to redirect to or a function rule that returns the url path. The function version is passed two params: $injector and $location
Solution: combination of these two. We would have state without url and custom otherwise:
$stateProvider
.state('404', {
// no url defined
template: '<div>error</div>',
})
$urlRouterProvider.otherwise(function($injector, $location){
var state = $injector.get('$state');
state.go('404');
return $location.path();
});
Check that all in this working example
As of ui-router#0.2.15 you can use $state.go() and send option not to update the location:
$urlRouterProvider.otherwise(function($injector) {
var $state = $injector.get('$state');
$state.go('404', null, {
location: false
});
});

Resources