Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

angular-ui-router nested views and "RangeError: Maximum call stack size exceeded"

I'm using the latest version of IonicFramework which uses angular-ui-router 0.2.8 under the hood. This is my first time using ui-router so I'm probably making a stupid mistake, but I can't figure out what it is. When I navigate to a new state / set of views I've just added, I get a "Maximum call stack size exceeded" error and the Chrome tab crashes.

My base HTML is extremely simple:

<body ng-app="checkinApp" ng-controller="GlobalCtrl">
    <nav-view></nav-view>
</body>

And here is the route config for the relevant screens:

app.config(function($stateProvider, $urlRouterProvider) {

    $stateProvider
    .state('event', {
        url: "/event"
        ,templateUrl: "templates/event.html"
        ,controller: "MainCtrl"
    })
    .state('event.chooseEvent', {
        url: "/choose"
        ,templateUrl: "templates/chooseEvent.html"
        ,controller: "MainCtrl"
    })
    .state('event.eventCheckin', {
        url: "/checkin"
        ,templateUrl: "templates/eventCheckin.html"
        ,controller: "MainCtrl"
    });

     // if none of the above are matched, go to this one
     $urlRouterProvider.otherwise("/event/choose");
});

Simply starting the app with the above route configuration causes the error, no other interaction is necessary.

Here are my views...

event.html:

Note the <nav-view></nav-view> block, where I'm expecting the child views to render.

<side-menus>

    <!-- page content -->
    <pane side-menu-content>
        <header class="bar bar-header bar-positive">
            <button class="button button-icon icon ion-navicon" ng-click="toggleMenu()"></button>
            <h1 class="title">Checkin</h1>
        </header>

        <nav-view></nav-view>

    </pane>

    <side-menu side="left">
        <content>navigation menu content here</content>
    </side-menu>

</side-menus>

eventCheckin.html:

<content has-header="true" on-refresh="refreshAttendees()">

    <!-- for pull to refresh -->
    <refresher></refresher>

    <ul class="list">
        <li
            ng-repeat="person in attendees | orderBy:'firstname' | orderBy:'lastname'"
            item="person"
            class="item item-toggle"
            >
                {{person.lastname}}, {{person.firstname}}
                <label class="toggle">
                    <input type="checkbox" ng-checked="person.arrived" ng-click="toggleArrived(person)" />
                    <div class="track">
                        <div class="handle"></div>
                    </div>
                </label>
        </li>
    </ul>

</content>

chooseEvent.html:

<div><br/><br/><br/>Swipe right to choose an Event</div>

Aside from the call stack infinite recursion, I'm not getting any other errors in the console. Any idea what I'm doing wrong?

like image 946
Adam Tuttle Avatar asked Jan 23 '14 13:01

Adam Tuttle


1 Answers

Your example helped point-pin a bug within the navView directive. I've since put the fix in the nightly build which will go in our next release.

One thing to point out is that the eventmenu state has abstract: true, because the side menu itself its not its own view, but instead a container for views.

An abstract state can have child states but can not get activated itself. An 'abstract' state is simply a state that can't be transitioned to. It is activated implicitly when one of its descendants are activated.

https://github.com/angular-ui/ui-router/wiki/Nested-States-%26-Nested-Views#wiki-abstract-states

Below is an example of using an abstract state for a side menu.

    $stateProvider
      .state('eventmenu', {
        url: "/event",
        abstract: true,
        templateUrl: "event-menu.html"
      })
      .state('eventmenu.home', {
        url: "/home",
        views: {
          'menuContent' :{
            templateUrl: "home.html"
          }
        }
      })
      .state('eventmenu.checkin', {
        url: "/check-in",
        views: {
          'menuContent' :{
            templateUrl: "check-in.html",
            controller: "CheckinCtrl"
          }
        }
      })
      .state('eventmenu.attendees', {
        url: "/attendees",
        views: {
          'menuContent' :{
            templateUrl: "attendees.html",
            controller: "AttendeesCtrl"
          }
        }
      })

For the markup, the main <nav-view> is at the root of the body, and the <nav-bar> is within <pane side-menu-content>. Note that Ionic uses <nav-view> instead of Angular UI Router's <ui-view> because Ionic's navView directive comes with a built-in navigation and animation system.

Next, the event-menu.html (which is an abstract state), has a child navView directive named menuContent, which is where all the other states will plug their views into.

<div ng-controller="MainCtrl">       
  <nav-view></nav-view>
</div>

<script id="event-menu.html" type="text/ng-template">
  <side-menus>

    <pane side-menu-content>
      <nav-bar type="bar-positive"
               back-button-type="button-icon"
               back-button-icon="ion-ios7-arrow-back"></nav-bar>
      <nav-view name="menuContent"></nav-view>
    </pane> 

    <side-menu side="left">
      <header class="bar bar-header bar-assertive">
        <div class="title">Left Menu</div>
      </header>
      <content has-header="true">
        <ul class="list">
          <a href="#/event/check-in" class="item">Check-in</a>
          <a href="#/event/attendees" class="item">Attendees</a>
        </ul>
      </content>
    </side-menu>

  </side-menus>
</script>

I put together a quick codepen here: http://codepen.io/ionic/pen/EtbrF

Also, at the time of writing this the codepen uses the nightly build since some of the requirements for your demo are not in a release yet.

Hope that helps!

like image 171
adam Avatar answered Oct 02 '22 23:10

adam