2

1. Html:

<html>
<head><title>MyApp</title></head>
<body data-ng-app="app">
    <ul><li><a data-ui-sref="home" style="cursor:pointer;">Home</a></li></ul>

    <div data-ui-view="header"></div>
    <div data-ui-view="container"></div>
    <div data-ui-view="footer"></div>


    <script src="/Scripts/angular-1.2.9/angular.min.js"></script>
    <script src="/Scripts/angular-1.2.9/animate/angular-animate.js"></script>
    <script src="/Scripts/angular-1.2.9/ui-router/angular-ui-router.js"></script>
    <script src="/Scripts/angular-1.2.9/ui-bootstrap/ui-bootstrap-custom-tpls-0.11.0.js"></script>
    <script src="/_temp/app/app.js"></script>
 </body>
</html>

2. App.js:

'use strict';

var $stateProviderRef = null;
var $urlRouterProviderRef = null;
var app = angular.module('app', ['ui.router']);


 app.factory('menuItems', function ($http) {
    return {
        all: function () {
            return $http({
              url: '/_temp/app/jsonData/statesJson.js',
              method: 'GET'
            });
        }
    };
});

app.config(function($locationProvider, $urlRouterProvider, $stateProvider) {
    $urlRouterProviderRef = $urlRouterProvider;
    $stateProviderRef = $stateProvider;
    $locationProvider.html5Mode(false);
    $urlRouterProviderRef.otherwise("/");
});

app.run(['$q', '$rootScope', '$state', 'menuItems',
    function ($q, $rootScope, $state', menuItems) {
        menuItems.all().success(function (data) {
            angular.forEach(data, function (value, key) {
                $stateProviderRef.state(value.name, value);
            });
                **$state.go("home");   <--- SOLUTION**
        });
    }]);

3. Data:

[
  {
    "name": "root",
    "url": "/",
    "parent": "",
    "abstract": true,
    "views": {
        "header": { "template": "header.html" },
        "footer": { "template": "footer.html" }
    }
},
{
    "name": "home",
    "url": "/home",
    "parent": "root",
    "views": {
        "container@": { "template": "home page" }
    }
  }
]

PROBLEM:

The nested views were suppose to show up on load not the click event. I tried to play with the $urlRouterProviderRef.otherwise("/") to no avail.

What must I correct to have the 3 nested views appear on load not a click event.

Thanks.

UPDATE:

I made a Plunker to help show the problem PLUNKER LINK


Radim Köhler

Sweet!! I updated my plunker to reflect multiple states if anyone wants to see for ideas.

  • Just remembering something I read about abstract states: You specify what the default view for an abstract state is by giving it an empty string for the URL. You might try changing the `url` for the `home` state to be `url: ''` – Sunil D. Jul 01 '14 at 00:12
  • made home url: "", did not work –  Jul 01 '14 at 00:15
  • I made a plunker, see above –  Jul 01 '14 at 00:22
  • After reading that the empty URL didn't work, I went and created [my own plunker](http://plnkr.co/edit/VPthp2foqlezgRuoi8gG?p=preview). Take a look, it's working properly when I use an empty string for the URL in the `home` state. I used the most recent/stable version of Angular and UI-Router. I also hard coded the loading of the data in `app.run()`. I'm not sure what else is different... – Sunil D. Jul 01 '14 at 00:48
  • Doesn't work when data is remote and not a local variable. Promise issue? –  Jul 01 '14 at 00:51
  • Yes, I think so. I was googling that topic and found [this](http://stackoverflow.com/questions/24500688/angularjs-dynamic-default-nested-views-from-json-data-not-appearing). I think that it's too late to do the state configuration by the time the promise is resolved. – Sunil D. Jul 01 '14 at 00:52
  • Your link is this very issue, LOL :-0 –  Jul 01 '14 at 00:54
  • You should just load the data you need before you load your main module. There's a bunch of ways to do this. [See this question](http://stackoverflow.com/questions/16286605/initialize-angularjs-service-with-asynchronous-data). – dave walker Jul 01 '14 at 05:34

1 Answers1

0

The issue here is: timing. The $urlRouterProviderRef.otherwise("/") instruction is used/executed too soon, before the $http.get() returns the state definition, i.e. before states are defined in foreach: $stateProviderRef.state(value.name, value);

If this would preceed the otherwise trigger, it would work (as you experienced for example with local variable, which is available soon enough).

But we can use another feature: $state.go (older doc link, but like it more than new)

So, with this "dynamic" state definition and $state.go ... we can achieve the desired

app.run(['$q', '$rootScope', '$state', 'menuItems',
  function ($q, $rootScope, $state, menuItems) {
    menuItems
      .all()
      .success(function (data) 
      {
          angular.forEach(data, function (value, key) {
              $stateProviderRef.state(value.name, value);
          });
          $state.go("home")
      });
  }]);

See working plunker.

NOTE: the above const "home" could be just anohter part of the passed JSON definition... e.g. instead of [] use an object with default : "home" and states : [...]. Because do not send array anyway. See Anatomy of a Subtle JSON Vulnerability, cite:

One common mitigation is to make sure that your JSON service always returns its response as a non-array JSON object.

Radim Köhler
  • 122,561
  • 47
  • 239
  • 335