James-Jesse Drinkard
James-Jesse Drinkard

Reputation: 15703

How to setup a parent/child with a resolve and nested states in ui-router?

I'm trying to refactor our current layout to add in a dynamic show/hide section above the header on our page. We are using Angularjs 1.4.2 with ui-router and currently we are using separate route files, although we have a main route. The main.html section of the screen, up to now, was the only section with the dynamic content. In order to get my new route working, I'm having to add it and main to each of the existing routes.

My question is, would a better design be something along this line of a single parent route to handle a resolve, with nested states for the dynamic main content and a new view for my new route and content? As the application grows, do you still continue to put the new routes into the parent route or is there a better way to organize it like we were doing with individual route files or a combination of both?

This is what I'm trying, but it's not working yet, as I'm getting a blank page, but the design for future growth with the application is what I'm trying to get right here:

(function () {
    'use strict';

    angular
        .module('myApp')
        .config(config);

    config.$inject = ['$stateProvider'];
    /* @ngInject */
    function config($stateProvider) {
        $stateProvider
            .state('root', {
                template: '<ui-view></ui-view>',
                abstract: true,
                resolve:{
                    myLoader:  ['myLoader', function(myLoader){
                        // $http returns a promise for the url data
                        return myLoader.load();
                    }]
                }
            }
            .state('main'), {
                url: '/?param1&param2&param3&param4',
                templateUrl: 'app/routes/main/main.html',
                controller: 'MainCtrl',
                redirectTo: 'main'
            })  
            $stateProvider
            .state('basicsearch', {
                url: '/basic-search',
                templateUrl: 'app/routes/basicsearch/basicsearch.html',
                controller: 'searchQuickCtrl'
            })
            $stateProvider
            .state('advancedsearch', {
                url: '/advaned-search',
                templateUrl: 'app/routes/advancedsearch/advancedsearch.html',
                controller: 'advancedSearchkCtrl'
            })
            $stateProvider
            .state('anothersearch', {
                url: '/another-search',
                templateUrl: 'app/routes/anothersearch/anothersearch.html',
                controller: 'anotherSearchCtrl'
            })
            .state('myChange', {
                url: '/myChange?param5&param6&param7&param8',
                views: {
                     "myChangeView": {
                         templateUrl: '/app/routes/myChange/myChange.html',
                         controller: 'myChangeCtrl'
                     }
                }

            }); 
    }
})();

Here is a basic layout of what we have: enter image description here

Upvotes: 1

Views: 1495

Answers (1)

Radim K&#246;hler
Radim K&#246;hler

Reputation: 123861

I am not sure about the issue. But I created working plunker, to show you how we can use state nesting and resolve.

So this is our parent controller for root state and the factory used in resolve

.factory('myLoader', function(){
  return {
    load: function () {return [1,2] }
  };
})
.controller('ParenCtrl', ['$scope', 'myLoader', function ($scope, myLoader) {
  $scope.myLoader = myLoader;
}])
.controller('MainCtrl', ['$scope', 'myLoader', function ($scope, myLoader) {}])
.controller('searchQuickCtrl', ['$scope', 'myLoader', function ($scope, myLoader) {}])
.controller('advancedSearchkCtrl', ['$scope', 'myLoader', function ($scope, myLoader) {}])
.controller('anotherSearchCtrl', ['$scope', function ($scope) {}])

Next, we will use controller for abstract root state, to assign result into scope:

.state('root', {
    template: '<div ui-view></div>',
    abstract: true,
    controller: 'ParenCtrl',
    resolve:{
        myLoader:  ['myLoader', function(myLoader){
            // $http returns a promise for the url data
            return myLoader.load();
        }]
    }
})

And all states will use that as their parent, and inherit the scope:

.state('main', {
    parent: 'root',
    url: '/?param1&param2&param3&param4',
    templateUrl: 'app/routes/main/main.html',
    controller: 'MainCtrl',
    //redirectTo: 'main'
})  

.state('basicsearch', {
    url: '/basic-search',
    templateUrl: 'app/routes/basicsearch/basicsearch.html',
    parent: 'root',
    controller: 'searchQuickCtrl'
})

.state('advancedsearch', {
    url: '/advaned-search',
    templateUrl: 'app/routes/advancedsearch/advancedsearch.html',
    parent: 'root',
    controller: 'advancedSearchkCtrl'
})

.state('anothersearch', {
    url: '/another-search',
    parent: 'root',
    templateUrl: 'app/routes/anothersearch/anothersearch.html',
    controller: 'anotherSearchCtrl'
})

And properly use it inside of this view:

<div >
  <h3>current state name: <var>{{$state.current.name}}</var></h3>

  <h4>resolved in parent</h4>
  <pre>{{myLoader | json }}</pre>

  <h5>params</h5>
  <pre>{{$stateParams | json}}</pre>
  <h5>state</h5>
  <pre>{{$state.current | json}}</pre>

 </div>

while all these controller do not use that resolved value

.controller('MainCtrl', ['$scope', 'myLoader', function ($scope, myLoader) {}])
.controller('searchQuickCtrl', ['$scope', 'myLoader', function ($scope, myLoader) {}])
.controller('advancedSearchkCtrl', ['$scope', 'myLoader', function ($scope, myLoader) {}])
.controller('anotherSearchCtrl', ['$scope', function ($scope) {}])

So, each controller can be provided with stuff resolved in parent (as parameter). That is shown above. But also, becuase parent already used that and assigned that to some $scope variable... all is alraeady in place.

Also check:

Upvotes: 2

Related Questions