Lothre1
Lothre1

Reputation: 3853

Nested Views inside a modal not showing up. (ui-router and ui-bootstrap)

I'm having trouble creating a modal that responds to the application state. From angular ui-router wiki I saw how to achieve my goal but my code is not working and I'm not figuring out why for the following reasons:

  1. when application router is /sign/in the modal is launched
  2. I can see the following output at my console

    => "about.sign"

    => "about.sign.in"

  3. The 2) means that both routers and firing! In other words the modal should be opened about.sign and the template from about.sign.in state should be inserted on it's parent template, more precisely on tag.

I tried both techniques. Named ui-views and anonymous but none worked.

https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions#how-to-open-a-dialogmodal-at-a-certain-state

 var app = angular.module('app.auth',
    [
      'ui.bootstrap',
      'ui.router'
    ]);

  app.config(function($stateProvider){
    $stateProvider
      .state('about',{
        url: '/',
        views:{
          'main': {
            templateUrl: 'about/about.tpl.html'
          }
        }
      })
      .state('about.sign',{
        url: '',
        onEnter: function($stateParams, $state, $modal){
          console.log('about.sign')
          var modalInstance = $modal.open({
            template:'<h1>Modal</h1><div ui-view="bar-view"></div>',
            size: 'sm',
          });
        }
      })
      .state('about.sign.in',{
        url:'sign/in',
        onEnter: function($stateParams, $state, $modal){
          console.log('about.sign.in')
        },
        views: {
          'bar-view': {
            template: 'Sign in #1'
          }
        }

      })
      .state('about.sign.up',{
        url:'sign/up',
        onEnter: function($stateParams, $state, $modal){
          console.log('about.sign.in')
        },
        views: {
          'bar-view': {
            template: 'Sign up #2'
          }
        }

      })
  }); //end of app.config

Am I doing something wrong with those nested views??

Upvotes: 3

Views: 5161

Answers (2)

Sean the Bean
Sean the Bean

Reputation: 5382

I think the accepted answer is a good one. (The trick was naming the view with modal@ instead of just modal - see the docs for an explanation.) But I thought I'd post a more general solution, in case that helps others who come looking here with similar problems. (It took me quite a while to figure this all out.)

One notable difference with my approach is that I decided to create a controller to launch the modal, rather than using the onEnter hook (even though every other example I saw used onEnter), because onEnter doesn't give you access to the scope. This way, the controllers both for the modal and for the child states can inherit from the same parent scope.

Interesting caveat: the named views for the child states cannot reference the parent state. The modal essentially creates its own state, so in my example, ui-view="modal" doesn't exist within the context of the launch_modal state - it exists in the root unnamed template (hence why the child's view must be modal@, not modal@launch_modal).

registerRoutes: function($stateProvider) {
  var modal_instance = null;
  return $stateProvider.state('launch_modal', {
    url: '/',
    template: '',
    controller: [
      '$uibModal', '$state', '$scope', function($modal, $state, $scope) {
        // init scope variables that you want the modal and child states to inherit
        $scope.modal = {
          title: '',
        };

        // launch the dialog
        modal_instance = $modal.open({
          template: '<div ui-view="modal">',
          controller: 'MyModule.Controllers.DialogController',
          scope: $scope
        });

        // make sure we leave the state when the dialog closes
        return modal_instance.result['finally'](function() {
          modal_instance = null;
          if ($state.includes('launch_modal')) {
            return $state.go('^');
          }
        });
      }
    ],
    onExit: function() {
      // make sure the dialog is closed when we leave the state
      if (modal_instance != null) {
        modal_instance.close();
      }
    }
  }).state('launch_modal.child1', {
    url: '',
    views: {
      // note: doing `modal@launch_modal` won't work!
      'modal@': {
        templateUrl: "path/to/child1/template",
        controller: "MyModule.Controllers.Child1ViewController"
      }
    }
  });
}

Hope that helps some people!

Upvotes: 3

maxisam
maxisam

Reputation: 22705

I bumped into this one as well. It turns out you need to use "parent" instead of dot notation.

Here is the plunker

angular.module('app', ['ui.router', 'ui.bootstrap'])
  .config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {

    $urlRouterProvider.otherwise('/');

    $stateProvider
      .state('view', {
        url: '',
        template: '<button class="btn btn-default" ui-sref="modal"> Modal</button> <ui-view />',
        controller: function($scope) {
        }
      })
      .state('modal', {
        //abstract: true,
        parent: 'view',
        url: '/modal',
        onEnter: ['$modal', '$state', function($modal, $state) {
            console.log('Open modal');
            $modal.open({
              template: '<button class="btn btn-danger" ui-sref="signin"> sign-in </button> <button ui-sref="signout" class="btn btn-success"> sign-out </button> <div ui-view="modal"></div>',
              backdrop: false,
              windowClass: 'right fade'
            }).result.finally(function() {
              $state.go('list');
          });
        }]
      })
      .state('signin', {
        url: '/signin',
        parent: 'modal',
        views: {
          'modal@': {
            template: '<p>sign-in</p>'
            }
          }
      })
      .state('signout', {
        url: '/signout',
        parent: 'modal',
        views: {
          'modal@': {
            template: '<p>sign-out</p>'
            }
          }
      })
    }]);

Upvotes: 8

Related Questions