Skip to content

Parent resolve values are not overridden by children #868

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
snoe opened this issue Feb 7, 2014 · 31 comments
Closed

Parent resolve values are not overridden by children #868

snoe opened this issue Feb 7, 2014 · 31 comments

Comments

@snoe
Copy link

snoe commented Feb 7, 2014

Child states can override a parent's data values, but they cannot override a parent's resolve values. In the code below, when refreshing on "/" parentData is 'parent' and selected is null. When refreshing on "/a" parentData becomes 'child' but selected remains null. Obviously the parent controller needs to manage it's internal state and scope during state transitions but on initial load it feels as though child resolved values should take precedence as data does.

Is this a bug or by design?

It would be nice if resolves behaved in the same way as data. Being able to inject resolved values instead of querying $state.params and resolving within the controller has a number of benefits.

  1. The controllers are instantiated when the data is ready, if a parent controller needs to make a slow query based on being in a substate the interface might not be usable or may be out of sync until it completes.
  2. The resolution can happen once at a high level, and it can be injected wherever needed.
  3. It is very nice to be able to instantiate a controller so that hitting reload on the browser can put the controller back in the same state before the refresh. It seems this should be the job of a state machine.
    angular.module('app', ['ui.router'])
      .config ($stateProvider, $urlRouterProvider) ->
        $urlRouterProvider.otherwise("/")

        $stateProvider
          .state 'main',
            url: '/'
            resolve:
              selected: -> null
            data:
              myData: 'parent'
            controller: ($state, $scope, $stateParams, selected) ->
              $scope.parentData = $state.$current.data.myData

              $scope.selected = selected
              $scope.options = ['a', 'b', 'c']

              $scope.showDetails = ->
                $state.go('main.child', {selected: $scope.selected})

            template: """
              data: {{parentData}}<br>
              <select
                ng-change="showDetails()"
                ng-model="selected"
                ng-options="o for o in options" >
              </select>
              <div ui-view></div>
              """

          .state 'main.child',
            url: ':selected'
            data:
              myData: "child"
            resolve:
              selected: ($stateParams) ->
                $stateParams.selected
              details: ->
                "it has details"
            controller: ($state, $scope, details, selected) ->
              $scope.details = details
              $scope.childData = $state.$current.data.myData

            template:
                "<div>info about {{selected}}: {{details}} and {{childData}}</div>"
@rockrep
Copy link

rockrep commented Feb 11, 2014

+1

@timkindberg
Copy link
Contributor

I'm not opposed to this and I do think that it waits for all the state heirarchy resolves to be ready before instantiating any controllers. So refreshing on '/a' I could see setting $scope.selected to the child value, but what if you are in '/' and then navigate to '/a'? Then the parent controller was already instantiated on the first page and would not be re-instantiated on the second. Is this acceptable behavior?

@snoe
Copy link
Author

snoe commented Feb 14, 2014

Absolutely.

In this case, it's the parent controller navigating from '/' to '/a' and passing in the selected param so the parent and child state would still be in sync.

In the case where the connection is less direct, I believe an instantiated parent controller would be able to listen for a state change event and access the resolved child values through that mechanism. If I really wanted the parent to be re-instantiated I'd expect to have to pass 'reload' to the transition.

@timkindberg
Copy link
Contributor

Would you want to do a pull request? :)

@cqr
Copy link

cqr commented Feb 27, 2015

@timkindberg I will start researching this to add it if it's still something you'd accept a PR for, but if it's no longer of interest I will look for another way to do what I am trying to do.

Are you still interested in having someone try to solve this problem?

@goliney
Copy link

goliney commented Mar 27, 2015

The documentation clearly says:

Child states will inherit resolved dependencies from parent state(s), which they can overwrite

So I assumed that child state can overwrite/neglect parent`s resolver. But it is not.
+1 to problem

@JustMaier
Copy link

+1

@sdoxsee
Copy link

sdoxsee commented Apr 9, 2015

I'm with @goliney in my assumptions. Had hoped i could override the parent's resolver in the child so that the parent's is ignored.

@letmein
Copy link

letmein commented May 8, 2015

+1

@daric81
Copy link

daric81 commented May 11, 2015

+1
I've just hit this problem and its a right pain.... I think its reasonable to be able to override a parents resolved field.

@ghost
Copy link

ghost commented May 28, 2015

+1 to this problem

@wahidnory
Copy link

Is this something being worked on? Because I've hit this problem aswell. Are there any alternatives that disable the parents resolve in the childs?

@christopherthielen
Copy link
Contributor

Closing because this is working (since 0.2.12) as the docs state. If there's a problem, please provide a plunk that demonstrates it and we can reopen.

http://plnkr.co/edit/RwKOPwBBQMkStdD1xqMX?p=preview

See: #1485, #1402, #1353, #1317

#1360

@TomerAvni
Copy link

The example pluker is not so useful in most cases . A common usable case when using child state(s), will be using the same controller instance as well as the same root template (while using some new view(s) - hence the views option.).
Here is your (@christopherthielen) plunker showing how it doesn't work:
http://plnkr.co/edit/OGDuP6ljWW3Qchv757AW?p=preview
Pay attention to the fact the parent's resolve is being visited ONCE and fixing the resolved value (although later on, before even being presented, the child's resolve is visited) .

Here is a hacky solution to the problem:
http://plnkr.co/edit/j1wCThlv1uczlX7d5cdg?p=preview

@wembernard
Copy link

I'm not sure if it's related or not but here is a plunk inspired by @TomerAvni hacky solution where I need to have different controllers according to $state params while keeping the parent/child relation for my breadcrumb & active tab.

Here is a plunk: http://plnkr.co/edit/jieVD94XLzufBw8JKUHW?p=preview

@dhyegocalota
Copy link

+1 when using the same parent controller

@webattitude
Copy link

@TomerAvni, not working with promises...

@trainerbill
Copy link

Just ran into this issue today. Any updates on if it will be fixed or any workarounds?

@christopherthielen
Copy link
Contributor

Just ran into this issue today. Any updates on if it will be fixed or any workarounds?

This is not broken; it works as designed.

Resolves are hierarchical. You can override parent resolves in a child, but the override is only applicable to the child (and its children). It does not retroactively modify resolves in the parent state/views.

@jtschoonhoven
Copy link

@christopherthielen Can you elaborate? It seems to me that the plunk by @TomerAvni demonstrates the opposite behavior.

@jtschoonhoven
Copy link

Actually, ksperling's comment in #78 helped me.

Ah, just noticing that your controller is defined on the parent state. A controller is only ever going to see 'resolve' values form it's own state and their ancestors. The controller isn't re-created when a child state is entered, so it does not make sense for it to see 'resolve' values of children.

@sam-soltech
Copy link

This is still an issue, its not updating the resolve values using promises and one parent controller

@christopherthielen
Copy link
Contributor

This is not broken; it works as designed.

Resolves are hierarchical. You can override parent resolves in a child, but the override is only applicable to the child (and its children). It does not retroactively modify resolves in the parent state/views.

@sam-soltech
Copy link

@christopherthielen I am not talking about the resolve value changing the parent state, I mean the value does not change in the children states even when you go child right away, Here is a plunker with an example of what I am talking about

@christopherthielen
Copy link
Contributor

Thanks for the plunker. I'll take a look!
On Mon, May 16, 2016 at 8:25 AM Sam Langberg [email protected]
wrote:

@christopherthielen https://github.com/christopherthielen I am not
talking about the resolve value changing the parent state, I mean the value
does not change in the children states even when you go child right away, Here
is a plunker http://plnkr.co/edit/PN4itgU9dDg4LMCDvpms?p=preview with
an example of what I am talking about


You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
#868 (comment)

@christopherthielen
Copy link
Contributor

@sam-soltech views are rendered in their own state's context. So your parent view always renders the parent resolve value. If you create a child view, it renders the child resolve which has the overridden value

http://plnkr.co/edit/60WmZZE8G04PyPmfHgNx?p=preview

@dub34
Copy link

dub34 commented May 17, 2016

Has same issue.
Can you explain, please?

$stateProvider.state('form',
{
url: '/form',
abstract: true,
resolve: {
agentClients: [function () {
return {data: []};
}],
},
views: {
'': {
templateUrl: '/templates/backend/form',
controller: 'AdminAddressSelectController',
controllerAs: 'mainCtrl'
}
}
})
.state('form.addressSelect', {
parent: 'form',
url: '/create/:clientId/step1',
views: {
'address': {
templateUrl: '/templates/backend/form_address_select',
controller: 'AdminAddressSelectController',
controllerAs: 'mainCtrl'
}
},
resolve: {
agentClients: ['adminOrderServices', '$stateParams', function (adminOrderServices, $stateParams) {
return adminOrderServices.getAgentClients($stateParams.clientId); //promise
}]
}
})`

My controller function

.controller('AdminAddressSelectController', ['agentClients', function (agentClients) {}])
In controller i have agentClients == {data: []}. But request from child state resolve agentClients is fire

@hackel
Copy link

hackel commented May 18, 2016

I also just realized I was being hit by this behaviour. I had two states that used an identical state definition aside from the resolves, so I thought I could refactor them into parent/child, with the child only specifying a different set of resolves, so as to avoid code duplication.

What I am understanding from this discussion is that that is not actually possible, is that correct? In my case, both the parent and child resolves were being run, but the parent resolve is what was being injected into the controller.

What I'm after is the ability to override the resolve entirely, so the parent resolve is never even run. Is this not possible somehow?

@lexigren
Copy link

@christopherthielen

This is not broken; it works as designed.
Resolves are hierarchical. You can override parent resolves in a child, but the override is only applicable to the child (and its children). It does not retroactively modify resolves in the parent state/views.

Well, I just created child state, added to its config resolver with same name as parent state has, and parent resolver is still running (along with child's one). What am I doing wrong?

@patmigliaccio
Copy link

Here is a workaround for promises based off @TomerAvni solution: http://plnkr.co/edit/xGdRlobk0yHQi9r2iUrP?p=preview

@webattitude

@christopherthielen
Copy link
Contributor

@lexigren you're not doing anything wrong, maybe just misunderstanding.

Let's say you have two states: parent and parent.child. parent defines resolve foo and parent.child overrides foo.

Both foo resolves exist, and both are fetched. But they are available for injection at different levels of the state hierarchy.

If you inject foo into the parent view, you will get the parent resolve data. If you inject foo into the parent.child view, you will get child resolve data.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests