ui-router Inherited Resolved Dependencies - angularjs

I was trying to implement a secure area with an abstract state that is then implemented by concrete states.
According to this blog post, I thought i could have the abstract state resolve the authentication and have the concrete states rely on it, because as stated in ui-router docs
Child states will inherit resolved dependencies from parent state(s)
But later on, the same ui-router doc mentions that
The resolve keys MUST be injected into the child states if you want to wait for the promises to be resolved before instantiating the children.
and indeed, if i comment out the resolveline on the concrete state, it doesnt work anymore.
So i am really missing something. If ChildState inherit resolved dependencies, why should they be re-injected?
my code below :
router.config(function($stateProvider, $urlRouterProvider) {
var authentication = ["Auth", function(Auth) {
return Auth.$requireAuth();
}];
.state('secure', {
abstract: true,
template: '<navbar></navbar><ui-view/>',
resolve: authentication
})
.state('secure.concrete', {
url: '/concrete',
templateUrl: 'views/concrete.html',
controller: 'concreteCtrl',
resolve: authentication // <--- if i comment this out, it wont work
})
UPDATE :
After hours of tears, it looks like i'm going the wrong path. The problem doesnt come from inherited resolved dependencies, but from the fact that once the promisse is resolved, it's not "resetted".
Is there anyway to force the promise to be re-setted / re-evaluated on each state change ?

The problems may result from incorrect resolve syntax, it should be an object:
resolve: {
authentication: authentication
}
'MUST be injected into the child states' means that authentication should be explicitly stated as child state controller dependency. Duplicate resolve on child state isn't an injection; it will just make authentication called twice.

this did the trick for me but i'm not exactly sure why :
instead of calling state.go() or ui-sref normally, i call them with state.go(state, {reload:true}) or
that way the resolve seems to be re-evaluated.

Related

Dependency injection into onEnter callbacks for state changes with ui-router

When using ui-router in Angular JS, what dependencies can be injected into onEnter callbacks? Specifically, I want to use the $http service and a constant defined in the parent module. The code I am working with defines ui-router states in module config; I know I cannot use services in config code, but maybe it's ok to use them in callbacks defined by config code? At any rate, the following code seems to work; I am just worried it may not be reliable. I guess
the real question is, when are dependencies injected? Does this happen at the time that a function is defined, or at the time that a function is called?
angular.module('sim', ['ui-router']).
constant('ENV', {
BASE_URL: '/simulation/secure'
}).
config(config);
function config($stateProvider) {
$stateProvider
.state('root.training', {
url: '/training',
controller: 'trainingCtrl',
onEnter: function($http,ENV)
{
$http.get(ENV.BASE_URL + '/setIsRunning');
}
});
};
The dependencies are injected when the function is called. Looking at the source code of ui-router, the onEnter-callback is called during a state transition, when the transition is considering entering. The call to the onEnter-callback is done using Angulars $injector.invoke() function, which resolves any dependencies in the $injector and invokes the method, as per the angular documentation. So pretty much any dependency should be valid to use in onEnter.

Angular-ui-router child state with multiple parent states

Using Angular-ui-router, is there a possibility to define a child-state that has multiple parent states:
$stateProvider
.state("parent1", {
url: '/',
templateUrl: 'parent1.html'
})
.state('parent2', {
url: '/parent2',
templateUrl: 'parent2.html'
})
//Make accessible from every state parent-state
.state("child", {
url: 'child',
//TODO parents: ['parent1', 'parent2']
onEnter: function() {
//do something;
}
})
Example:
Your Angular app has a directive that is used multiple times in different states in your app. The directive itself includes a link which redirects to a different state. Depending on where the directive is used, it should append its child-state to the current active parent-state.
It just doesn't seem right to define states for each permutation like state1.child, state2.child etc.. There needs to be a better way.
This kind of hierarchy would go against the DOM tree structure, which by definition doesn't allow multiple parents of same element.
Moreover, it is error (and headache) prone and could easily result in the multiple inheritance diamond problem, as child state do inherit from parent state in some cases.
It sounds like a directive, and not a state, would be the better solution for what you're looking for.
EDIT:
Just saw that there's a closed issue on this, which is closed because he reached the same conclusion (that a directive is the better way)

Where is the first place I can call a service in angular ui-router?

I have an Angular app and I am using ui-router.
I need to call my service at the beginning of my App before anything else runs.
Is there a way I can call a service inside of my app.js file without using app.run? Or is there a better solution other than in my app.js file?
Take a look at the resolve functionality of ui-router https://github.com/angular-ui/ui-router/wiki/Quick-Reference. You just add another field to your state called 'resolve' and then can inject the service there. By using a resolve, this route will not execute until this resolve is completed.
I have something similar in an application where I created an abstract state for others to inherit and therefore, none of the states are registered until that initial resolve function is completed.
Ex.
$stateProvider.state('someState', {
template: 'someTemplate.html',
controller: 'someController',
resolve: {
authenticate: function(AuthSrv){
//Do stuff with AuthSrv in here
}
}
})

update $stateParams in ui-router after $location.search() is called

I have an angular application which is using the ionic-framework, which uses ui-router on the back end.
In one of my controllers I call:
$location.search('filter','sometext');
I have reloadOnSearch disabled in my routing configuration. I noticed that updating the location does not update the $stateParams. Is there a way to force the $stateParams to update as the location is updated? I looked through the ui-router documentation, but didn't see anything about this scenario, but perhaps I missed it.
I had a similar situation. You cannot track route updates if you had disabled reloadOnSearch, but I found a solution for this case.
Watch $stateParams:
$scope.$watchCollection('$stateParams', function (newParams) {
var search = $location.search();
if (angular.isObject(newParams)) {
angular.extend(search, newParams);
}
$location.search(search).replace();
});
Change $stateParams, not route:
$scope.$stateParams.offset += $scope.$stateParams.limit;
Completely working example.

UI-Router's resolve functions are only called once

I was going to use ui-routers resolve feature to inject some readiliy resolved promises into my controllers.
I used the example plnkr to make an example.
Consider these nested states: route1 and route1.list.
I have a resolve function called abc defined on route1. Now when I navigate to route1 for the first time, abc is called and will be resolved. Now when I navigate to route1.list and back to route1, abc is not called again.
http://plnkr.co/edit/mi5H5HKVAO3J6PCfKSW3?p=preview
I guess this is intentional, but consider this use-case:
the resolve function retrieves some data via http and should be refreshed/invalidated at some point, maybe on every state change. Is there some way to do this when using nested states?
If the resolve function was called on every inject I could implement it any way I want: Return the old, resolved promise or create a new one.
I have only briefly tested this, but if the states were not nested things would work as I expected. Giving up on nested states because of this stinks though. And having the resolve dependencies available in nested states is actually nice.
Supplying the option reload:true to go() / transtionTo() / ui-sref does the trick :-)
Thanks to Designing the Code for pointing me in this direction. The solution is a bit different though, so I write up this answer.
Reload is documented as follows:
reload (v0.2.5) - {boolean=false}, If true will force transition even if the state or params have not changed, aka a reload of the same state. It differs from reloadOnSearch because you'd use this when you want to force a reload when everything is the same, including search params.
The direct way is to change every ui-sref link into something like this: <a ui-sref="route1" ui-sref-opts="{reload: true}">.
To avoid supplying the option at every link I wrote a decorator around $state (see also http://plnkr.co/edit/mi5H5HKVAO3J6PCfKSW3?p=preview):
myapp.config(function($provide) {
$provide.decorator('$state', function($delegate) {
var originalTransitionTo = $delegate.transitionTo;
$delegate.transitionTo = function(to, toParams, options) {
return originalTransitionTo(to, toParams, angular.extend({
reload: true
}, options));
};
return $delegate;
});
});
Try $state.reload();
It will force resolve the resolves again. Though think there is some issue related to this where controllers aren't reloaded. (ui.router n00b here as well)

Resources