This is my main app (app.js)
(function(ng, module) {
module.config(['$stateProvider', '$urlRouterProvider', function( $stateProvider, $urlRouterProvider){
$urlRouterProvider.otherwise("app");
$stateProvider.state('login', {
url: 'login',
templateUrl: '/assets/templates/pages/login.html'
}).state('root', {
url: '',
templateUrl: '/assets/templates/pages/index.html'
});
}]);
}) (angular, angular.module('myapp', ['ui.router', 'myapp.submodule']));
This is the submodule (submodule.js)
(function(ng, module) {
module.config(['$stateProvider', function($stateProvider){
$stateProvider.state('root.substate', {
url: 'todo/{type}',
templateUrl: '/assets/app/todo/todo.html',
controller: function($stateParams, $scope) {
// Do stuff.
}
});
}]);
}) (angular, angular.module('myapp.submodule', ['ui.router']));
The expected behaviour would be
redirect to "app" url when no matching route is found
activate the "root" state on root url
activate the "root.substate" state on /todo url
This is working fine.
However, if i do refresh the page, the state is not activated and i'm sent back to "app". Why?
We have two root states (no parent) states. These should be either having no url, or it should start with some unique sign - the best choice would always with (URI spec) be a /:
// domain/app/login - easy to find
.state('login', {
url: 'login',
...
})
// no def === domain/app
.state('root', {
url: '',
...
});
Now, let's use some url even for our 'root' state :
// domain/app/
.state('root', {
url: '/',
...
});
That mans, that our child 'root.substate' will also contain the parent url part. So if we would use this
// this is a child and its url will be in fact: domain/app//todo/sometype
.state('root.substate', {
url: '/todo/{type}',
...
});
See, that this way, our url will now for child contain // (double slash)
To avoid that, we can use UI-Router feature '^'
// this stat will not use the parent part
// this will work domain/app/todo/sometype
.state('root.substate', {
url: '^/todo/{type}',
...
});
Check the doc:
Absolute Routes (^)
If you want to have absolute url matching, then you need to prefix your url string with a special symbol '^'.
$stateProvider
.state('contacts', {
url: '/contacts',
...
})
.state('contacts.list', {
url: '^/list',
...
});
I know a lot of time left. But about issue with refreshing. If you want to stay on the same url address as before refreshing you should add next:
angular.module('app').run(['$state', '$stateParams', function($state, $stateParams) {
//this solves page refresh and getting back to state
}]);
like mentioned here in pre-last answer
Related
I have a route definition as follows:
$stateProvider
.state('vehicles', {
url: '/vehicles',
templateUrl: 'foo/bar1.html'
}).state('vehicles.id', {
url: '/{id}',
templateUrl: 'foo/bar3.html'
}).state('vehicles.create', {
url: '/create',
templateUrl: 'foo/bar2.html',
controller: 'VehicleCreateController'
});
I have a button that does
$state.go("vehicles.create");
The problem is, that while the URL changes correctly, the page remains the same. Only after the second click, the correct template appears.
After a hint from my colleague I realized, that it was the state definitions that caused the problem. Reordering the states from "more specific" (URL-wise - i.e. /create) to less specific (/{id}) did the trick. So the thing that was wrong was having the more generic URL /vehicles/{id} before the very similar, but less generic /vehicles/create.
So here's the improved version:
$stateProvider
.state('vehicles', {
url: '/vehicles',
templateUrl: 'foo/bar1.html'
}).state('vehicles.create', {
url: '/create',
templateUrl: 'foo/bar2.html',
controller: 'VehicleCreateController'
}).state('vehicles.id', {
url: '/{id}',
templateUrl: 'foo/bar3.html'
});
use : for your params and ? to make those params optional if you need.
check the below code snippet, for routing with params.
$stateProvider
.state('contacts.detail', {
url: "/contacts/:contactId",
templateUrl: 'contacts.detail.html',
controller: function ($stateParams) {
// If we got here from a url of /contacts/42
expect($stateParams).toBe({contactId: "42"});
}
})
check this for more clear view on routing.
I am trying to use ui-router on my project.
Core module:
var core = angular.module('muhamo.core', ['angular-loading-bar', 'anguFixedHeaderTable', 'ui.router']);
Tracking module:
var app = angular.module(TRACKING_MODULE_NAME, ['muhamo.core']);
app.config(Configure);
Configure.$inject = ['$stateProvider', '$urlRouterProvider'];
function Configure($stateProvider, $urlRouterProvider) {
$stateProvider.state('contacts', {
templateUrl: '/static/partials/employee/employee-edit',
controller: function () {
this.title = 'My Contacts';
},
controllerAs: 'contact'
});
$urlRouterProvider.otherwise("/contacts");
console.log($stateProvider);
}
and the html definition :
<div ui-view></div>
It works fine if i click to a ui-sref link. But on page load it does not load the default view "/contacts". Am I missing something here?
UPDATE
It works after adding missing "url" property. But now I've another problem, if I extend my implementation like that :
function Configure($stateProvider, $urlRouterProvider) {
$stateProvider.state('employees', {
abstract: true,
url: "/employees"
/* Various other settings common to both child states */
}).state('employees.list', {
url: "", // Note the empty URL
templateUrl: '/static/partials/employee/employee-list'
});
$urlRouterProvider.otherwise("/employees");
console.log($stateProvider);
}
also with more states, ui-view is not rendering.
There are two fishy things in your implementation. You out an empty url and your default route is abstract. Try my changes below.
function Configure($stateProvider, $urlRouterProvider) {
$stateProvider.state('employees', {
abstract: true,
url: "/employees"
/* Various other settings common to both child states */
}).state('employees.list', {
url: "/list", // Note the empty URL
templateUrl: '/static/partials/employee/employee-list'
});
$urlRouterProvider.otherwise("/employees/list");
console.log($stateProvider);
Cheers
Yes. You need to set the state.url to '/contacts'
$stateProvider.state('contacts', {
url: '/contacts',
templateUrl: '/static/partials/employee/employee-edit',
controller: function () {
this.title = 'My Contacts';
},
controllerAs: 'contact'
});
It seems you forgot to set the url parameter, e.g.:
$stateProvider.state('contacts', {
url: "/contacts",
...
}
I'm trying to use $urlRouterProvider to redirect me from a parent state to a child one, though the child one is a URL that accepts a parameter. (child url: /:page?pageId).
I am simply hoping to enter the application by entering from www.example.com/home, where the $urlRouterProvider should then take over the routing.
The code looks something like this:
$urlRouterProvider.when('/home', '/home/?pageId=1');
$stateProvider.state('home', {
url: '/home',
abstract: true,
templateUrl: 'home.tpl.html'
}).state('home.page', {
url: '/:page?pageId',
templateUrl: '...'
});
As you can see, I was hoping that by using the $urlRouterProvider I would be able to direct the location from the /home (parent state) to the parameterized child state /:page?pageId by forcing the url /?pageId=1. Instead, it just appends a trailing slash to /home/.
Here is a very dysfunctional plnkr of this madness: http://plnkr.co/edit/XZ4jkhqzQmykIgx0CvH2?p=preview
Thanks!
There is a working plunker
I am not sure why the above notation is not working (if intended to skip it or not), but with this adjustment it will work:
$stateProvider.state('home', {
url: '/home',
abstract: true,
templateUrl: 'home.html',
controller: 'controller',
}).state('home.child', {
url: '/:page?pageId',
templateUrl: 'childtemplate.html',
params : { // HERE we do define the defaults
pageId: {value: 1}, // these would serve as a starting value
}
});
So, what we are effectively doing here, is to declaring the default value out of url, but in the params: {} setting:
...
params : {
pageId: {value: 1},
}
Check it here
Context
Users can register with a unique URL slug that identifies their page, e.g. 'http://example.com/slug'.
Current State
In my Express.js file, I successfully check my database to see if the slug exists on a user, then redirect the user from 'http://example.com/slug' to 'http://example.com/#!/slug' to take advantage of Angular's routing.
With Angular, however, I can't use $http or $location services in my router file (since it's taking place inside module.config...see this Stack Overflow explanation for more details).
Desire
Basically what I want to do is route the user to a 'default' view when a valid slug is found, or home if it's not. Any suggestions would be much appreciated.
For reference, my module.config code can be found here (note that the 'default' state I want to use is 'search'):
core.client.routes.js
'use strict';
// Setting up route
angular.module('core').config(['$stateProvider', '$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {
// Redirect to home when route not found.
$urlRouterProvider.otherwise('/');
// Home state routing
$stateProvider.
state('home', {
url: '/',
templateUrl: 'modules/core/views/home.client.view.html'
}).
state('search', {
url: '/search',
templateUrl: 'modules/core/views/search.client.view.html'
});
}
]);
What I would like to do, is something like this...
'use strict';
// Setting up route
angular.module('core').config(['$stateProvider', '$urlRouterProvider', '$http', '$location',
function($stateProvider, $urlRouterProvider, $http, $location) {
// Get current slug, assign to json.
var slug = $location.path();
var data = {
link: slug
};
// Check db for slug
$http.post('/my/post/route', data).success( function(response) {
// Found slug in db
}).error( function(response) {
// Route to home
$location.path('/');
});
// Home state routing
$stateProvider.
state('home', {
url: '/',
templateUrl: 'modules/core/views/home.client.view.html'
}).
state('search', {
// Set URL to slug
url: '/' + slug,
templateUrl: 'modules/core/views/search.client.view.html'
});
}
]);
To directly answer your question, what you want to do is use the routes "resolve" to check for the dependency and redirect to the appropriate view:
angular.module('app', ['ui.router','ngMockE2E'])
.run(function ($httpBackend) {
$httpBackend.whenGET(/api\/slugs\/.*/).respond(function (method, url) {
return url.match(/good$/) ? [200,{name: 'john doe'}] : [404,''];
});
})
.config(function ($stateProvider) {
$stateProvider
.state(
'search',
{
url: '/search?terms=:slug',
template: '<h1>Search: {{vm.terms}}</h1>',
controllerAs: 'vm',
controller: function ($stateParams) {
this.terms = $stateParams.slug;
}
}
)
.state(
'slug',
{
url: '/:slug',
template: '<h1>Slug: {{vm.user.name}}</h1>',
controllerAs: 'vm',
controller: function (user) {
this.user = user
},
resolve: {
user: function ($q, $http, $stateParams, $state) {
var defer = $q.defer();
$http.get('http://somewhere.com/api/slugs/' + $stateParams.slug)
.success(function (user) {
defer.resolve(user);
})
.error(function () {
defer.reject();
$state.go('search', {slug: $stateParams.slug});
});
return defer.promise;
}
}
}
);
});
<div ng-app="app">
<script data-require="angular.js#*" data-semver="1.3.6" src="https://code.angularjs.org/1.3.6/angular.js"></script>
<script data-require="ui-router#*" data-semver="0.2.13" src="//rawgit.com/angular-ui/ui-router/0.2.13/release/angular-ui-router.js"></script>
<script data-require="angular-mocks#*" data-semver="1.3.5" src="https://code.angularjs.org/1.3.5/angular-mocks.js"></script>
<a ui-sref="slug({slug: 'good'})">Matched Route</a>
<a ui-sref="slug({slug: 'bad'})">Redirect Route</a>
<div ui-view></div>
</div>
But, there are a few things you may want to revisit in your example:
Is there a need to perform this check client side if you are already validating and redirecting server side via express?
You seem to be overloading the / route a bit, if home fails, it redirects to itself
You are grabbing slug from $location on app init, not when the view is routed to which could be post init, you need to grab it when ever you are routing to the view
You may want to consider using a GET request to fetch/read data for this request rather than using a POST which is intended generally for write operations (but thats a different story)
I just started learning angularjs and I am using angular-ui-router. I am trying to send data from one state to another using $state.go but I have no success. Here is what I have so far:
I have not included the html intentionally because I assumed it was not needed if it is needed please tell me and I will add it.
I have configured my states as below:
$stateProvider
.state('public', {
abstract: true,
templateUrl: 'App/scripts/main/views/PublicContentParent.html'
})
.state('public.login', {
url: '/login',
templateUrl: 'App/scripts/login/views/login.html',
controller: 'loginCtrl'
})
$stateProvider
.state('private', {
abstract: true,
templateUrl: 'App/scripts/main/views/PrivateContentParent.html'
})
.state('private.visits', {
url: '/visits',
views: {
'main': {
controller: 'visitsListCtrl',
templateUrl: 'App/scripts/visits/views/VisitsList.html'
}
}
});
When my LoginController is invoked it will execute the below code:
loginModule.controller('loginCtrl', ['$state', function ($scope, $state) {
$state.go('private.visits', { name : "Object"});
}]);
When the private.visits page is active, I am trying to print the $stateParams:
visitsModule.controller('visitsListCtrl', ['$stateParams',
function ($stateParams) {
console.log($stateParams);
}]);
As things state $stateParams is an empty object. I expected it to to contain the object I passed in loginCtrl.
EDIT
It seems that if private.visits url has this url format '/visits/:name' and I also add the property params: ["name"] I get access to the object I send from the public.login state.
The side effect is that the parameters are added to the url which is logical.
I tried doing the same thing with a child state with no url, and in this case it seems that I do not have access to the params I passed from public.login.
How do I send data in child states?
What you have to do is to define the name param in the private.visits state like:
$stateProvider
.state('public', {
abstract: true,
templateUrl: 'App/scripts/main/views/PublicContentParent.html'
})
.state('public.login', {
url: '/login',
templateUrl: 'App/scripts/login/views/login.html',
controller: 'loginCtrl'
})
$stateProvider
.state('private', {
abstract: true,
templateUrl: 'App/scripts/main/views/PrivateContentParent.html'
})
.state('private.visits', {
// NOTE!
// Previously, we were only passing params as an array
// now we are sending it as an object. If you
// send this as an array it will throw an error
// stating id.match is not a function, so we updated
// this code. For further explanation please visit this
// url http://stackoverflow.com/a/26204095/1132354
params: {'name': null},
url: '/visits',
views: {
'main': {
controller: 'visitsListCtrl',
templateUrl: 'App/scripts/visits/views/VisitsList.html',
resolve: {
name: ['$stateParams', function($stateParams) {
return $stateParams.name;
}]
}
}
}
});
And then in the controller access to the name:
visitsModule.controller('visitsListCtrl', ['name',
function (name) {
console.log(name);
}]);
Hope it help you!
When you say:
$state.go('private.visits', { name : "Object"});
You're not passing data to the private.visits state, but rather you're setting a parameter to the private.visits state, which doesn't even support parameters as you have not defined parameters for it in the state config. If you want to share data between states use a service, or if your states have a parent-child relationship then the child state will have access to the parent states data. Seeing as how you don't want the data to sow up in your URLs, I would use a service (getters/setters) to achieve this.