I want to load data and params for each state once when the app loads from my service settings (this service returns also promise since data are loaded asynchronously).
Something like this:
.state('tutorials', {
url: '/tutorials',
templateUrl: 'partials/tutorials.html',
controller: 'TutorialsCtrl',
data: {details: false}, // instead of false I want: settings.get('tutorials_data_details')
params: {popular: '512'} // instead of '512' I want: settings.get('tutorials_params_popular')
})
Those params must be loaded for all states before entering any state since they are used to build links with ui-sref directive that can be placed in any html pointing to any state (and those default params will be used).
Problem is that I cannot inject service into the config function. And using resolve will not handle all states at once.
I think $stateProvider.decorator could be used but I don't know how.
I would have commented, but not enough reputation for that.
If it's possible, one option is to create a parent abstract state such as app, so your state tree would become app.tutorials and then you can use resolve on the app state, allowing all sub-states to use the data/params.
something like this:
.state('app', {
url: '',
abstract: true,
templateUrl: 'partials/tutorials.html',
controller: 'AppCtrl',
resolve: { <resolve your dependencies here> }
})
.state('app.tutorials', {
url: '/tutorials',
templateUrl: 'partials/tutorials.html',
controller: 'TutorialsCtrl'
})
Note the Tutorials state wont load until the resolve is completed.
After some research and thinking I came up with these 3 possible solutions:
1) By using decorator we make all states resolving settings service.
.decorator('data', function(state, parent) {
state.resolve.SettingsLoaded = ['settings', function(settings) { return settings.$promise; }];
return parent(state);
})
Then in settings service use load success callback to setup states. You can use $state.get() to get all states (or with string parameter to get specific state by name) and modify the params object.
NOTE that you cannot use short version like state.params.popular = '512' but the full one: state.params.popular.value = '512'.
2) Those params could be actually functions that will be injected and invoked, and the result value will be used.
So the new params definition in the state will be:
params: {popular: ['settings', function(settings) { return settings.get('tutorials_params_popular') || '512'; }]}
Also I used again the same decorator to make sure settings will be resolved before any state is loaded.
I like this approach the most since all parameter values stays in settings service and the change is being dynamically reflected.
3) Since I'm using RequireJS, I could load settings using that and then inject it to the router file. This would however require more changes to the exising code in my settings service...
Related
In my application there are a lot of possible query parameters, and currently, I have to define them all:
.state('app.home', {
url: '/home/?date&from&to&website&settings&search..',
controller: function($transition$) {
console.log($transition$.params());
}
}
But this has two downsides. I have to define every possible param, and when it's not set, it's added to $transition$.params() as myParam: undefined.
Is there a more convenient way to set query parameters with UI Router 1.x (so without $stateParams or $location)?
Something like:
`url: '/home/?myParams`
I am working on porting a project over to Ui-router from ng-route. Previously my routes looked like this
{
customData: {
data: []
},
url: '/',
templateUrl: '/',
resolve: {
cmsData: {
cmsContent: cmsContent; // a function defined elswhere that reaches out to a cms system
}
},
}
This allowed me to access the customData attribute on $route.current.$$route.customData, however in ui-router the behavior is not the same, as is expected. I am looking for a way to pass that custom data, unique to that route, to its resolve function.
Any ideas would be really great. I have looked at solutions like $stateParams and $state.data, but since the routes are not resolved, I don't have the current state loading (at least how I understand it)
Can you somehow receive $stateParams without having a <ui-view>-tag in your html?
Basically, I want this code to work:
.config([
'$locationProvider',
'$stateProvider',
function($locationProvider, $stateProvider) {
$locationProvider.html5Mode(true);
$stateProvider
.state('schedules_show', {
name: 'edit_schedule',
url: '/schedules/:id/edit'
});
}])
So I can fetch the :id from any other controller that is being called via $stateParams.
Some more clarification: I don't want to use $stateParams to generate links or to move around my application, cause my app is an hybrid of RoR and Angular.js. I change views in server side with common links. I just want to use angular-ui-router to get some values from the URL to use in the Angular.js part of my app (in this case the :id). So because I don't want to navigate via Angular and don't want to use it's state dependent controllers or views, which again is the reason why I don't want to have <ui-view>-tags in my HTML.
Problem solved: I think my approach via angular-ui-router was wrong. I have a solution now, where I just pass the param from the HTML via ng-init to the controller, but it doesn't answer my question, so I think this should be closed.
If I understood you rigth you need state params. And the qnswer is: yes you can. First way is to use url params: /url/suburl/:param1/:param2/:paramN. Second way (if you do not want to see your params in url) use params option in your state. Then just call your state with this params inside. Example:
.state('schedules_show', {
name: 'state1',
url: '/state',
params: {
param1: null,
param2: null
}
});
Here, in state configuration null is for not to assign initial value; and call this state with
ui-sref="state1({ param1: 'test', param2: 10 })"
Then in injected $stateParams object you can get these params' values
Another possible solution is to use resolve in your state to provide specific params to your controller assigned with this state
More info about resolve
Does it makes sense?
I'm still working on an angular app, using the great ui-router. I'd like to use some dynamic nested states (I talked about it here), so I know it's possible.
Here is my code, with a specific state and its dynamic children states :
.state('home', {
url: '/home',
controller: 'RouteCtrl'
})
.state('home.state', {
url: '/home/:state',
controller: 'RouteCtrl'
})
$urlRouterProvider.otherwise('/home')
I have 3 buttons (more specifically, I use <a> to make it) to access 3 differents states : 'home', 'contact' and 'about'. 'contact' and 'about' are 'home' nested states and every state has a specific text to display when activated.
Unfortunatly, it appears that both of the children states aren't resolved from 'home' state.
Here is a plunker of the problem which match with my problem. Any idea ?
This will give you a working call to the new states:
$scope.redirect = function(state) {
console.log('redirect to state : ' + state);
if (state != 'home') {
$state.go('home.state', {
'state': state
});
} else {
$state.go('home');
}
}
However, it still won't change the text on the page, because the controller only sets it once when initially loaded.
Technically home, contact and about are not 3 states. What you appear to be doing is altering the content based of the parameters of the state. This could be achieved using one state and forcing ui-router to reload the state when you use $state.go
I have modified your plunkr here http://plnkr.co/edit/XXaltjG17FwY15tSbKaD?p=preview
Your state definition could look something like this,
.state('home', {
url: '/home?state',
controller: 'RouteCtrl'
})
The question mark makes the state parameter optional and also a query string.
The redirection could look something like this. You need to reload as you are going to the same route
$state.go('home', {state: state}, {reload: true});
Redirecting to the home page could look something like this. You need to disable inheritance for this redirect as you don't want to keep the query strings.
$state.go('home',{}, {reload: true, inherit: false});
The main problem here is that you want to have a variable in the state. You can't go to state home.about since it's not a given .state.
You should look at stateParams, or you can specify the URL where you want to go to the URL with Angular's $location.
Note: I think the url for a child state like home.state does not need the /home URL since it's in the father's state.
Given the following code:
$routeProvider.when('/movies/:type', {
title: 'Movies',
templateUrl: 'pages/movies/movies.html',
controller: 'MoviesCtrl'
});
How can I access the :type param from inside the when function? I want to do something like so:
$routeProvider.when('/movies/:type', {
title: 'Movies' + ' - ' + :type,
templateUrl: 'pages/movies/movies.html',
controller: 'MoviesCtrl'
});
That value in title must be dinamically generated.
Thanks in adv.
I'm not sure why you are extending the route (config) object, but you are able to access routeParams from within your controller. That is also the recommended way.
The $routeParams service allows you to retrieve the current set of route parameters.
angular.module('MyModule').controller('MoviesCtrl',function($scope, $routeParams) {
$scope.currentMovieType = 'Filmes-' + $routeParams.type;
});
Let's say your route is something like that /movies/scifi. In this case $scope.currentMovieType becomes scifi and you can use {{currentMovieType}} in your view to populate this value. You can find detailed informations in the documentation.
Note that the $routeParams are only updated after a route change completes successfully. This means that you cannot rely on $routeParams being correct in route resolve functions. Instead you can use $route.current.params to access the new route's parameters.
It is not really possible, because the route config object is not as dynamic as you think. Whatever you put in the route configuration object, it cannot depend on the value that the route param is going to take in the future. Think of how this code gets executed : the configuration object will be evaluated only once, when the route is configured.
On the other hand, if you want to change the page's title when going through this route, you can do it using the $routeParamsservice to access the param value, and the $document service to change the page's title, either in a controller or in a resolveclause.
An example with the latter option:
$routeProvider.when('/movies/:type', angular.extend({
templateUrl: 'pages/movies/movies.html',
controller: 'MoviesCtrl',
resolve: {
title: ['$routeParams','$document', function ($routeParams, $document) {
var title = 'Filmes-' + $routeParams.type;
$document.title = title;
return title;
}]
}
}, routeParams));
That works also in a controller of course.
Some notes on your code :
I'm not even sure that there is a point setting a title property in a route config object, I don't see it in the documentation at least.
That second argument routeParams in that angular.extend call - the name is confusing, one could mistake it for the $routeParams service. I think you should call it routeDefaults or something like that instead.
Give a try to $location.absUrl(); requires some calculation too .
https://docs.angularjs.org/api/ng/service/$location