Angular JS – Nesting Views with the “@” syntax (SPA Routing)

With the “@” syntax I can more easily define how nested states (or should be better to say nested views) should be managed. With this syntax I don’t have to define states for the templates, but just which view/controller I want to be displayed at any level of the templates hierarchy.

For example let’s imagine to have this main page:

<div ng-app="app">
    <div ui-view="">
    </div>
</div>

I can define a “root” state in this way:

$stateProvider.state("root", {
    url: "/",
    views: {
        "": {
            templateUrl: 'app/views/layout.html',
            controller: 'layoutController'
        },
        "topArea@root": {
            templateUrl: 'app/views/rootTopArea.html',
            controller: 'rootTopAreaController'
        },
        "bottomArea@root": {
            templateUrl: 'app/views/rootBottomArea.html',
            controller: 'rootBottomAreaController'
        },
        "bottomTopArea@root": {
            templateUrl: 'app/views/rootBottomTopArea.html',
            controller: 'rootBottomTopAreaController'
        },
        "bottomBottomArea@root": {
            templateUrl: 'app/views/rootBottomBottomArea.html',
            controller: 'rootBottomBottomAreaController'
        }
    }
});

What I’m saying is that for the view “” (the main one with no name) I want to load the layout.html view and I want to use the “layoutController” controller. Now let’s imagine that the layout.html view is made in this way:

<h2>This is the layout</h2>
{{value}}
 
<ul>
    <li>
        <div ui-view="topArea">
        </div>
    </li>
    <li>
        <div ui-view="bottomArea">
        </div>
    </li>
</ul>

Here we have two nested sub-view called “topArea” and “bottomArea”. With the “@” syntax I don’t have to specify new states (as when using the “.” syntax), but I just have to say that the “topArea” sub-view (for the “root” state, so “topArea@root”) should load the rootTopArea.html view and should use the “rootTopAreaController” controller. The same thing I’m doing for the “bottomArea” sub-view.

Finally let’s say that the rootBottomArea.html view is made in the same way as the layout.html view: with two sub-view inside. These are third level sub-views, so how should e manage this?

<h2>This is the root bottom area</h2>
{{value}}
 
<ul>
    <li>
        <div ui-view="bottomTopArea">
        </div>
    </li>
    <li>
        <div ui-view="bottomBottomArea">
        </div>
    </li>
</ul>

Simple, in the same way! We don’t need to follow the hierarchical structure of the sub-views’ tree, we just need to refer the sub-views’ names within our configuration. So we just have to specify the view and the controller for the “bottomTopArea@root” and the “bottomBottomArea@root” views.

And here is the final result:

Angular JS – Contollers and Valuesets (SPA Routing)

I can specify a controller for each state, to provide a model for its templates. An interesting thing is that I can use valuesets to call a state with a set of parameters. To do this I have to specify the valueset name in the state’s route (preceded by “:”). Then i have to pass the actual values as parameters when I’m creating the state link with the ui-sref directive (within a JSon object). This object is finally received by the controller of the target state as a parameter, so I can map its values on the $state object.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Angular JS</title>
    <link href="Content/bootstrap.min.css" rel="stylesheet" />
</head>
<body data-ng-app="myApp">
    <div class="container">
        <div class="row">
            <div class="col-md-12 well">
                <div ui-view="">
                </div>
            </div>
        </div>
    </div>
 
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
    <script src="Scripts/angular-ui-router.min.js"></script>
 
    <script>
        angular.module("myApp", ["ui.router"]).config(function ($stateProvider, $urlRouterProvider) {
 
            //Every undefined route
            //will be resolved with "/"
            $urlRouterProvider.otherwise("/");
 
            $stateProvider.state("root", {
                url: "/",
                template: "This is the home page"
                        + "<ul>"
                        + "<li><a ui-sref='detail({id: 1})'>Go to articleId 1</a></li>"
                        + "<li><a ui-sref='detail({id: 2})'>Go to articleId 2</a></li>"
                        + "<li><a ui-sref='detail({id: 3})'>Go to articleId 3</a></li>"
                        + "</ul>"
            }).state("detail", {
                url: "/detail/articleId/:id",
                template: "This is the detail page for articleId {{articleId}}"
                        + "<br/><a ui-sref='root'>Go to home</a>",
                controller: function ($scope, $stateParams) {
                    $scope.articleId = $stateParams.id;
                }
            });
        });
    </script>
</body>
</html>

Angular JS – Nesting States (SPA Routing)

I can have the ui-view directive inside the templates of a state. Doing in this way I can have sub-states which can define the templates for its views in a recursive way. A state which can contain other states can be considered as a template and works for its sub states as the page works for it.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Angular JS</title>
    <link href="Content/bootstrap.min.css" rel="stylesheet" />
</head>
<body data-ng-app="myApp">
    <div class="container">
        <div class="row">
            <div class="col-md-8 well">
                <div ui-view="main">
                </div>
            </div>
            <div class="col-md-4 well">
                <div ui-view="thirdrail">
                </div>
            </div>
        </div>
    </div>
 
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
    <script src="Scripts/angular-ui-router.min.js"></script>
 
    <script>
        angular.module("myApp", ["ui.router"]).config(function ($stateProvider, $urlRouterProvider) {
 
            //Every undefined route
            //will be resolved with "/"
            $urlRouterProvider.otherwise("/");
 
            $stateProvider.state("template", {
                views: {
                    "main": {
                        template: "This is the template main"
                            + "<div ui-view='subView1'></div>"
                    },
                    "thirdrail": {
                        template: "This is the template thirdrail"
                            + "<div ui-view='subView2'></div>"
                    }
                }
            }).state("template.home", {
                url: "/",
                views: {
                    "subView1": {
                        template: "This is the home sub-view 1."
                                + "<a ui-sref='template.page1'>Go to page1</a>"
                    },
                    "subView2": {
                        template: "This is the home sub-view 2"
                    }
                }
            }).state("template.page1", {
                url: "/page1",
                views: {
                    "subView1": {
                        template: "This is the page 1 sub-view 1."
                                + "<a ui-sref='template.home'>Go to home</a>"
                    },
                    "subView2": {
                        template: "This is the page 1 sub-view 2"
                    }
                }
            });
        });
    </script>
</body>
</html>

In this example the “template” state defines two views (“subView1” and “subView2”) inside its templates. This means that it can be considered as a template and so we aren’t associating any route to it. Indeed a direct call to that state would be useful because we would not be able to know the content of its views. instead, we associate our routes to its sub states (defined as .) so, for example, when we go to the “/” route we know that we have to use the the “template” state for the “main” and “thirdrail” views of the page and we know that we have to use the “template.home” state for the “subView1” and “subView2” views of the parent state (and without worrying about which particular template of the parent state contains “subView1” and “subView2”).

Angular JS – States (SPA Routing)

States are the Angular mechanism to handle SPA routing. Instead of using the default routing provided by Angular there is an extension named ui-router that is far better. It can be downloaded from NuGet.

Once downloaded the extension we can configure the routing within the config() method of our application module. We can define a set of status with, for each one, a deep link URL and a template (as for the custom directives we can use the templateUrl property to refer a template located in an external html file).

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Angular JS</title>
    <link href="Content/bootstrap.min.css" rel="stylesheet" />
</head>
<body data-ng-app="myApp">
    <div class="container">
        <div class="row">
            <div class="col-md-12 well">
                <div ui-view="">
                </div>
            </div>
        </div>
    </div>
 
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
    <script src="Scripts/angular-ui-router.min.js"></script>
 
    <script>
        angular.module("myApp", ["ui.router"]).config(function ($stateProvider, $urlRouterProvider) {
 
            //Every undefined route
            //will be resolved with "/"
            $urlRouterProvider.otherwise("/");
 
            $stateProvider.state("root", {
                url: "/",
                template: "This is the home page. <a ui-sref='page1'>Go to page1</a>"
            }).state("page1", {
                url: "/page1",
                template: "This is the page 1. <a ui-sref='root'>Go to home</a>"
            });
        });
    </script>
</body>
</html>

The template is loaded inside the DIV with the ui-view property, whose acts as placeholder. In this case I created 2 states related to the route “/” and the route “/page1”. Within a template I can let ui-router to generate the deep links to refer the other states. This can be done using the ui-sref property, with the logic name of the target state.


Using multiple views

I can have multiple views related to the same route. This can be useful if I have a generic layout, for example a main column and a third rail column, but I just want to concentrate only on the contents.

In these cases I can define a view for the main column’s content and another view for the third rail’s content. Then I can assign a template (or a templateUrl) to each view, based on the current route. To do this I have to use the views property instead of the template property (or templateUrl). Whithin the views property I can then define the template (or templateUrl) related to each view I want to manage in my page.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Angular JS</title>
    <link href="Content/bootstrap.min.css" rel="stylesheet" />
</head>
<body data-ng-app="myApp">
    <div class="container">
        <div class="row">
            <div class="col-md-8 well">
                <div ui-view="main">
                </div>
            </div>
            <div class="col-md-4 well">
                <div ui-view="thirdrail">
                </div>
            </div>
        </div>
    </div>
 
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
    <script src="Scripts/angular-ui-router.min.js"></script>
 
    <script>
        angular.module("myApp", ["ui.router"]).config(function ($stateProvider, $urlRouterProvider) {
 
            //Every undefined route
            //will be resolved with "/"
            $urlRouterProvider.otherwise("/");
 
            $stateProvider.state("root", {
                url: "/",
                views: {
                    "main": { template: "This is the home page. <a ui-sref='page1'>Go to page1</a>" },
                    "thirdrail": { template: "This is the home page thirdrail" }
                }
            }).state("page1", {
                url: "/page1",
                views: {
                    "main": { template: "This is the page 1. <a ui-sref='root'>Go to home</a>" },
                    "thirdrail": { template: "This is the page 1 thirdrail" }
                }
            });
        });
    </script>
</body>
</html>