angularjs ui-router nested views

One of the benefits of ui-router over ngRoute is that it provides support for nested states and views. Nested states have parent child relationship. This means properties of the parent state are available to the child state. There are several methods for nesting states. The following are the 2 common methods.

1. Using 'dot notation'. In the example below "students" is a child of "studentParent" state. Notice in the child state name "studentParent.students" we are using . (DOT)

.state("studentParent", {

// parent state
config properties

})

.state("studentParent.students", {

// child state
config properties

})

2. Using the parent property with the parent name as string. In the example below, we are using parent property in the child state to specify who the parent is for this state.

.state("studentParent", {

// parent state
config properties

})

.state("students", {

parent: "studentParent",

// child state
config properties

})

Let us understand nested states and views with an example. Here is what we want to do. On the students and studentDetails pages, we want to display student totals i.e the total number of male students, female students and grand total at the top of the pages in addition to the data these pages are already displaying.

Students page should be as shown below

StudentDetails page should be as shown below

Step 1 : Create a studentTotals class. We will use this class as a container for student totals.

SqlCommand cmd = newSqlCommand("SELECT
COALESCE(Gender, 'GrandTotal') AS Gender, COUNT(*) AS Total FROM tblStudents
GROUP BY ROLLUP(Gender)", con);

con.Open();

SqlDataReader rdr = cmd.ExecuteReader();

while (rdr.Read())

{

switch (rdr["Gender"].ToString())

{

case"Male":

totals.males = Convert.ToInt32(rdr["Total"]);

break;

case"Female":

totals.females = Convert.ToInt32(rdr["Total"]);

break;

default:

totals.total = Convert.ToInt32(rdr["Total"]);

break;

}

}

}

JavaScriptSerializer js = newJavaScriptSerializer();

Context.Response.Write(js.Serialize(totals));

}

Step 3 : In script.js create "studentParent" state which will be the parent for "students" and "studentDetails" state. The parent state has it's own controller and template which we will create in just a bit. The resolve property retrieves student totals. This data will be available for the child states as well. We also have set abstract property to true. This makes the parent state an abstract state. Abstract states cannot be activated. They are implicitly activated when a child state is activated. The URL of the abstract state will be prepended to the URL of all the child states. The parent resolve property resolved dependencies and the custom data exposed via data property will be available for use by child states.

.state("studentParent", {

url: "/students",

controller: "studentParentController",

controllerAs: "stdParentCtrl",

templateUrl: "Templates/studentParent.html",

resolve: {

studentTotals: function ($http) {

return $http.get("StudentService.asmx/GetStudentTotals")

.then(function (response) {

return response.data;

})

}

},

abstract: true

})

Step 4 : In script.js, modify students and studentDetails to be child states. As the URL from the parent abstract state is prepended to the child state URL, the redundant part of the URL (/students) has been removed from both the child states (students & studentDetails).

Step 6 : Create student parent template. Add a new html file. Name it studentParent.html. On this template student totals are displayed using three <h3> elements. We also have a <ui-view> element into which the child states (i.e. students & studentDetails) will inject their content.

<h3>Total
Male Students : {{stdParentCtrl.males}}</h3>

<h3>Total
Female Students : {{stdParentCtrl.females}}</h3>

<h3>Grand
Total : {{stdParentCtrl.total}}</h3>

<ui-view></ui-view>

Step 7 : In index.html modify students link. Notice the student state name is prepended with parent state name.