14

As Angular is SPA that's terrific, but what if I need some other page not related to index.html, how is realised by UI-Router states with different ui-views?

For example, I have index.html:

<!DOCTYPE html>
<html data-ng-app="npAdmin">
<head>
...
</head>
<body>
   <header>
      <data-user-profile class="user-profile"></data-user-profile>
  </header>

  <section class="content-wrapper">
      <aside data-main-menu></aside>
      <div class="main-content" data-ui-view></div>
  </section>

  <footer class="row"></footer>
...
</body>
</html>

app.js:

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

app.config(['$httpProvider', '$stateProvider', '$urlRouterProvider', function($httpProvider, $stateProvider, $urlRouterProvider) {

    $stateProvider
    .state('dashboard', {
        url: '/dashboard',
        templateUrl: '/app/dashboard/dashboard.html',
        controller: 'DashboardCtrl'
    })
    .state('crm', {
        url: '/crm',
        templateUrl: '/app/crm/crm.html',
        controller: 'CrmCtrl'
    })
...

Now I need login.html which is totally different from index.html (don't need index's header, footer, sidebar) but config stateProvider only looks to index.html ui-view and changes content to it by states. How to combine login.html?

It seems not that hard, but I don't get it.

swolfish
  • 771
  • 1
  • 9
  • 25

2 Answers2

15

As you expected, it is not so difficult, there is a plunker.

The trick is to move the common stuff for all views inside of the specific template e.g. common.html and create the abstract state. Other words, the index.html will remain clean:

<body>

    <div ui-view=""></div>
</body>

And its previous content (content of the index.html) would be moved to common.html. The state definition could look like this:

$stateProvider
  .state('common', {
    templateUrl: 'tpl.common.html',
    abstract: true,
  })
  .state('dashboard', {
    url: '/dashboard',
    parent: 'common',
    ...
  })
  .state('crm', { 
    url: '/crm',
    parent: 'common',
    ...
  })
  .state('login', {
    url: '/login',
    templateUrl: 'tpl.login.html',
  });

$urlRouterProvider.otherwise('/crm');

What is interesting (I'd say) is that we introduced abstract state, without url. So the all current logic will remain, just the abstract will play role of a layout template.

Check more here: example

Radim Köhler
  • 122,561
  • 47
  • 239
  • 335
  • Now I know how to use abstract state property. Thank you. – swolfish Jul 24 '14 at 08:27
  • Great to see that;) `ui-router` is incredible and really powerful... Enjoy it ;) – Radim Köhler Jul 24 '14 at 08:30
  • @KillABug I would suggest, issue question. Show more details. You will surelly get an answer. Or let me know I can check it as well. Comment is not suitable for investigating why is your code not working... Does it make sense? – Radim Köhler Mar 16 '15 at 09:19
  • @KillABug If I will know, I will help. But your question will target more guys than must me, so you will get answer... I am sure – Radim Köhler Mar 16 '15 at 09:24
  • @RadimKöhler http://stackoverflow.com/questions/29073389/loading-angular-js-internal-routes-and-controllers – KillABug Mar 16 '15 at 09:46
4

I also had this problem before, refer to THIS if you're interested. In that link, I also handled returnUrl and 401 http status code in case the user is not authorized.

In your case, I suggest you design your application like this:

index.html:

<!DOCTYPE html>
<html data-ng-app="npAdmin">
<head>
...
</head>
<body data-ui-view>

</body>
...
</html>

main.html:

  <header>
      <data-user-profile class="user-profile"></data-user-profile>
  </header>

  <section class="content-wrapper">
      <aside data-main-menu></aside>
      <div class="main-content" data-ui-view></div>
  </section>

  <footer class="row"></footer>

login.html

(include your html for this view)

App.js:

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

app.config(['$httpProvider', '$stateProvider', '$urlRouterProvider', function($httpProvider, $stateProvider, $urlRouterProvider) {

    $stateProvider
    .state('login',{
       url:"/login",
       templateUrl: '/app/login.html',
       controller: 'LoginCtrl'
     })
    .state('main',function(){
       url:"/",
       templateUrl: '/app/main.html',
       controller: 'MainCtrl',
       abstract: true //you could use abstract state or not depending on your design 
    })
    .state('main.dashboard', { //inherit from your main
        url: '/dashboard',
        templateUrl: '/app/dashboard/dashboard.html',
        controller: 'DashboardCtrl'
    })
    .state('main.crm', { //inherit from your main
        url: '/crm',
        templateUrl: '/app/crm/crm.html',
        controller: 'CrmCtrl'
    })

Explanation:

As angular is SPA, your index.html should cover all views of your application. There are multiple ways to ensure that. In this example, the login is also a state in your application that is separated from your main. By utilizing state inheritance in angular router, you could further have child states in your main which are dashboard and crm in this case.

Community
  • 1
  • 1
Khanh TO
  • 48,509
  • 13
  • 99
  • 115
  • 1
    In addition to the above, if you're going to have a separate Login page make sure you implement an Authentication Check service. This way, if the user isn't logged in they'll be re-directed to the login page. See [this video](http://youtu.be/hqAyiqUs93c) for a basic implementation. – Ed B Jul 24 '14 at 07:58
  • @Ed B: Sure, because that's not included in the question. And we will check that for all requests, not only for login page. – Khanh TO Jul 24 '14 at 07:59
  • Thanks a lot, very exhaustive answer but with some minor mistakes. Radim's solution with abstract state seems as perfect solution. – swolfish Jul 24 '14 at 08:26
  • @swolfish: I did not test the code, I just gave the idea. And as I said, there are multiple ways to do that. It's just a matter of design. – Khanh TO Jul 24 '14 at 08:28