Check out our new training course on AngularJS for Flex Developers

Creating a Custom Filter - Angular uiSelect AutoComplete Directive - Part 3

I have been working with the Angular uiSelect AutoComplete component recently. I wanted to see if I could modify how the component filters the items in the drop down. It can be done pretty easy with an Angular filter. I'm going to modify the sample I used in previous posts about the Angular UI Select box.

Create the Filter

Angular Filters are not complicated. They take an input, process it, and return the modified value for display. The filter from my original examples was this:


<ui-select-choices repeat="state in dataProvider | filter: $select.search">

It is taken directly from the original sample. This takes the value the user typed, $select.search, and looks for it in the label for each state. If the value is found, the item is put in the drop down; otherwise it is removed from the list.

We can modify this algorithm by creating our own customer filter. I'm going to create a filter that filters on the start of the label, but does not look for items in the middle of the word. First create a code stub for the filter:


angular.module('uiSelectTestApp').filter('firstOnlyFilter', function() {
return function(input, searchString) {
}
});

I named this filter firstOnlyFilter. The input to this filter will be an arrayCollection; and the second attribute is a searchString, or $select.search. Now flesh out the filter's implementation"


return input.filter(function(item){
if (item.label.toLowerCase().search(searchString.toLowerCase()) == 0){
return true;
}
return false;
})

This code uses the JavaScript filter function to modify the input array with items that only match the current criteria. The function turns the label into lowercase; then uses the JavaScript string search() method to find the text, which was also put into lowercase. If search returns 0; then the text was found at the first item, and the result should be included in the final array. Otherwise false is returned and the item is removed from the drop down.

The last step is to modify the uiSelectChoices to user the new filter:


<ui-select-choices repeat="state in dataProvider | firstOnlyFilter : $select.search">

This should complete the appropriate changes:

Play with the code here.

Final Thoughts

The use of Angular filters to restrict the items in the drop down makes the uiSelect very extensible, and you can write your own algorithm to make the component work to your specifications.

Sign up for DotComIt's Monthly Technical Newsletter

Creating an Alert with Bootstrap and Angular

One of this blog's more popular posts is about creating an Alert using Bootstrap and AngularJS. I decided to write about something similar--using Bootstrap to create an Alert. I use this a lot in one of the current applications I've built for a client and thought I'd write a little about it.

This post uses Angular, Bootstrap, and uiBootstrap.

Create a Basic Alert

The first sample will create a few basic alerts. First, import the Angular, uiBootstrap, and Bootstrap libraries:


<script src="//code.angularjs.org/1.5.8/angular.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/2.2.0/ui-bootstrap-tpls.min.js"></script>
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">

Then, create an Angular Module:


<script>
angular.module('testAlert', ['ui.bootstrap']);
</script>

The angular module is named testAlert; and the uiBootstrap library is passed into the module as an argument. This means that the Angular application can use all the components defined in the uiBootstrap library.

Finally, use the HTML to create a few alerts:



<body ng-app="testAlert">

<div uib-alert class="alert-danger">Danger Alert</div>

<div uib-alert class="alert-warning">Warning Alert</div>

<div uib-alert class="alert-success">Success Alert</div>

I created three alerts here; each one with a different class which apply different colors to the alert:

Try the code here. This example isn't too complex, and does not even have an Angular controller. Let's fix that.

Dynamically Generate Alerts

Next, I'll show you how to dynamically generate alerts. First, create an Angular controller:


angular.module('testAlert').controller('ctrl',['$scope', function($scope){
$scope.alerts = [];
}]);

The controller is named ctrl. The only thing the controller contains, so far, is an array named alerts.

The UI code will look over the array to create alerts on the page:


<div ng-controller="ctrl">
<div uib-alert ng-repeat="alert in alerts" ng-class="alert.type">{{alert.msg}}</div>
</div>

The alert div loops over all the alerts. It specifies the alert class using a type property on the alert object. A msg property on the alert object contains the message to be displayed in the alert.

Now, let's add some code to create the alerts. Let's put a function in the controller:


$scope.addAlert = function(type, msg){
$scope.alerts.push({
type : type,
msg : msg
})
}

The addAlert function will accept a type and a message; then add those to the alert array.

Add some buttons to the UI to create the alerts:


<button ng-click="addAlert('alert-warning','Warning Alert')" >Add Warning Alert</button>
<button ng-click="addAlert('alert-danger','Warning Alert')" >Add danger Alert</button>
<button ng-click="addAlert('alert-success','Warning Alert')" >Add Success Alert</button>

You can play around with this here:

Close an Alert

You probably don't want alerts to stay around in your app forever, so we need a way to dismiss them. One way is to use the close method on the alert:


<div uib-alert ng-repeat="alert in alerts" ng-class="alert.type" close="closeAlert($index)">{{alert.msg}}</div>

When adding a close method an 'x' button will appear as part of the alert. When it is clicked; we can execute the closerAlert() method inside the controller. The value we pass into the closerAlert() method is the index of the alert which was clicked.

This is the closeAlert() method:


$scope.closeAlert = function(index) {
$scope.alerts.splice(index, 1);
};

Look at the finished app:

Play with the full code here!

Dismiss an Alert Automatically

There is one more alert related property I wanted to examine. You can set up the alerts to vanish automatically using dismiss-on-timeout:


<div uib-alert ng-repeat="alert in alerts" ng-class="alert.type" close="closeAlert($index)" dismiss-on-timeout="5000">{{alert.msg}}</div>

the value of the dismiss-on-timeout attribute is in milliseconds, and will hide the alert in about 5 seconds with the value of 5000.

Try out the code here.

I have mixed feelings about this approach, because I have concerns about changing the UI without user interaction. I think showing the user a message and automatically removing it could be confusing. What if the user didn't read the message yet?

Final Thoughts

The alert is a nice component that allows you to give non-intrusive feedback to your users. I like it better than a popup warning, which can interrupt the users flow. What do you think?

Sign up for DotComIt's Monthly Technical Newsletter

Themes - Angular uiSelect Directive - Part 2

I have been writing some blog posts about the Angular uiSelect component. One interesting thing about this component is that it is built to support different themes, each providing a different look and feel.

These themes are provided in the form of a CSS; then you can just change a property to modify the look and feel. The previous example of my . In my previous post, I used the bootstrap theme. In this post I wanted to demonstrate two other themes.

The examples in this post build off the examples in the previous post.

The Select2 Theme

The first theme is the Select2 theme. Select2 is a JQuery component designed to replaced select boxes. Putting it in place is pretty easy. First add the select2.css:


<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/select2/3.4.5/select2.css">

Then change the theme property on the uiSelect:


<ui-select ng-model="selectedCountry" theme="select2" title="Choose a State">
<ui-select-match placeholder="Select or search for a State in the list...">{{$select.selected.label}}</ui-select-match>
<ui-select-choices repeat="state in dataProvider | filter: $select.search">
<span ng-bind-html="state.label | highlight: $select.search"></span>
</ui-select-choices>
</ui-select>

This will show the uiSelect in the new theme:

The main difference with the select2 theme is that it adds a filter box in the drop down instead of as part of the main label display.

Play with the code here.

The Selectize Theme

A third theme is provided as part of the uiSelect directive; named selectize. This theme is my preferred approach because it reminds me of my days as a Flex Developer. The approach to use this theme is similar to the select2 theme. First, load the style sheet:


<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/selectize.js/0.8.5/css/selectize.default.css">

Then add the theme property on the uiSelect:


<ui-select ng-model="selectedCountry" theme="selectize" title="Choose a State">
<ui-select-match placeholder="Select or search for a State in the list...">{{$select.selected.label}}</ui-select-match>
<ui-select-choices repeat="state in dataProvider | filter: $select.search">
<span ng-bind-html="state.label | highlight: $select.search"></span>
</ui-select-choices>
</ui-select>

Run the code, and you'll see the third theme:

Play with the code here.

This theme is my preferred theme and the one I used on a client project. It reminds most of the work I did with Flex AutoComplete components.

Theming the uiSelect component is very easy; because the directive developers did all the hard work for us.

Sign up for DotComIt's Monthly Technical Newsletter

Where can I find an Angular AutoComplete Component?

I have extensive experience with creating AutoComplete components in the Flex Space. When it came time to use one in an Angular project; I was lost. The client wanted something similar to the Flextras AutoCompleteComboBox, which supported type ahead filtering of the drop down list and a drop down button.

However, every JavaScript AutoComplete component I could find was either missing the down arrow or didn't work. My Google-Fu was failing me.

UI Bootstrap has both a drop down and a type ahead component. I was working on combining them into a single component when a Facebook friend turned me onto the Angular uiSelect component. It was exactly what we were after.

Basic Setup

The first thing we need to do is load the JavaScript libraries:


<script src="//code.angularjs.org/1.5.8/angular.js">
</script>
<script src="//code.angularjs.org/1.5.8/angular-sanitize.js">
</script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-select/0.19.4/select.min.js">
</script>

This loads the Angular library, the uiSelect library, and the angular sanitize library. Angular Sanitize is an Angular library used to parse HTML into Tokens. It is required as part of uiSelect.

Some Style sheets are needed too:


<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/angular-ui-select/0.19.4/select.min.css">
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.css">

These are a custom style sheet for the uiSelect directive and the style sheet for Bootstrap. The uiSelect has multiple themes that can apply to it. For the moment, I'm only using the bootstrap theme.

All the libraries and CSS sheets are loaded from remote CDNs instead of being hosted locally.

Create the Angular Code

The next step is to create the Angular code. First, create the Angular module:


angular.module('uiSelectTestApp', ['ngSanitize', 'ui.select']);

The uiSelect and ngSantitize directives are passed as configuration arguments into our main module. Next, create a controller:


angular.module('uiSelectTestApp').controller('ctrl', ['$scope',function($scope){
$scope.dataProvider = [
{id:1, label:'Alabama'},
{id:2, label:'Alaska'},
. . .
{id:50, label:'Wyoming'},
]
}]);

The controller is named ctrl. It contains a single $scope variable, which is an array. The array contains a list of US states, and will be used to populate the uiSelect component. That is enough for the JavaScript code.

Create the HTML

The last step is to add the uiSelect component to the page. First, create the Angular app and controller to the page:


<body ng-app="uiSelectTestApp">
<div ng-controller="ctrl">
</div>
</body>

This code adds the ngApp attribute on the body tag and the ngController attribute on a div tag inside of it. Inside the div, the uiSelect component is implemented:


<ui-select ng-model="selectedCountry" theme="bootstrap" title="Choose a State">
<ui-select-match placeholder="Select or search for a State in the list...">{{$select.selected.label}}</ui-select-match>
<ui-select-choices repeat="state in dataProvider | filter: $select.search">
<span ng-bind-html="state.label | highlight: $select.search"></span>
</ui-select-choices>
</ui-select>

There is lots going on here, so let's dissect it one piece at a time. First the main uiSelect:


<ui-select ng-model="selectedCountry" theme="bootstrap" title="Choose a State">
</ui-select>

The main uiSelect component contains an ngModel, which acts just like the ngModel on every other angular component you've used. The theme specifies the theme, in this case I specified it to bootstrap. I'm not going to explore themes in this post, but I hope to do so in more detail later. The last attribute is title, which acts like a tooltip when mousing over the component.

Next look at the match:


<ui-select-match placeholder="Select or search for a State in the list...">{{$select.selected.label}}</ui-select-match>

The uiSelectMatch directive is used to display a label. If an item is selected in the drop down; it will display to the user. If no item is selected the placeholder will display as a prompt. The $select is a service specific to the uiSelect component. We are using it to drill into the selected object and display its label.

The uiSelectChoices is used to populate the items that appear in the drop down:


<ui-select-choices repeat="state in dataProvider | filter: $select.search">
<span ng-bind-html="state.label | highlight: $select.search"></span>
</ui-select-choices>

The repeat attribute is a loop. It looks over all the items in the dataProvider--which was defined in the controller's $scope. It applies a filter onto those items; using a default one from the uiSelect's $select directive. The default filter searches for any string typed into the uiSelectMatch entry and restricts the display list as such.

The span inside the uiSelectChoices determines what is displayed inside the drop down. For my purposes, I just put the label; but you could expand this to include images or other items. The highlight filter is applied to the label, which will highlight the search items in the filtered drop down.

Test The App

Try the app.

This is the app in its default state:

This is the app with nothing typed in:

This is the app with some filtering:

Final Thoughts

I was surprised that I had time finding this component when searching through Google for Angular AutoComplete or TypeAhead directives. That is what made me want to write a few blog posts about this component. More are coming, and in the next article I'll talk about skinning of this component.

Sign up for DotComIt's Monthly Technical Newsletter

Using an Angular HTTP Interceptor

I was working on a client project. Two separate AngularJS applications were using the same set of services. The services were using cookies to keep track of server side session data. A small subset of users used both applications and both applications were hosted on the same domain. Did you guess why this is a problem?

For users who used both applications, the services could not tell which application the user was using, as such service requests from one app were sometimes being run based on login privileges of the second. This was causing all sorts of problems.

The services team decided the best way to accommodate this was to force an argument onto every service request for one of the applications. The server would use this argument to keep the sessions straight. I don't know all the specifics of how the services were implemented, so can't comment on that. But, I can tell you how we modified the Angular code to address this issue. We used an AngularJS HTTP Intercepter .

What is an Interceptor?

The purpose of an AngularJS HTTP Interceptor is to modify all HTTP requests performed by AngularJS. Y This will affect all requests including REST operations, loading ngInclude files, or loading directive templates. You can set up an interceptor to modify requests before they are sent to the server. You can use it to modify responses before the response is sent back to the result method. An interceptor can also handle request and response errors.

When you need to change every service request the application makes to add a new URL argument, this is a great way to do that.

Creating the Interceptor

A request interceptor must be created in an Angular config block, so here is a config block:


angular.module('testModule').config([''$httpProvider", function($httpProvider){
}]);

The $httpProvider service is passed into the config function using Angular's dependency injection syntax. The interceptor must be created on the $httpProvider service; which will affect all http requests coming from the app:


$httpProvider.interceptors.push(function(){
return {
"request" : function(config){
return config
}
}
}

This is a basic request interceptor, which performs no functionality yet. $httpProvider.interceptors is an array; and a function is pushed onto it. The function returns an object, which contains properties for each type of interceptor--request, response, requestError, responseError. In this case I only defined a request interceptor.

The value of the request is another function. This function accepts a single argument, config that contains configuration data of the request. Then it returns that configuration object. We have not written code to modify the argument yet, but will next.

Modify the HTTP Request

How you modify the HTTP Requests depends on what you want to do. For my purposes, I wanted to append a query string argument to the request. Without knowing the nature of the request, I won't know if existing arguments already exist or not. My first order of business is to determine if I need to add a question mark to the URL--to start a query string--or an ampersand to add to an existing query string:


var queryStringPrepend = "?";
if(config.url.search('\\?') != -1){
var queryStringPrepend = "&"
}

This creates an argument named queryStringPrepend. This is the value to place before our special query string variable that must be added to all service calls. I default the argument to a question mark for creating a brand new query string. Then I perform a regex search on the config's url to see if it already contains a question mark or not. If the question mark exists then change the queryStringPrepend value to an ampersand. The next step is to add the query string to the configuration's URL:


Config.url += queryStringPRepend + "SpecialVariable=SpecialValue"

This uses simple string concatenation. Next return the config argument:


return config

I found this worked for exactly what I wanted.

After some experimentation I realized the new URL parameter was being added to all the templates loaded, not just service calls. It wasn't needed there, so I decided to use another regex to fix it. Check to see if the page being loaded is an html page by looking for .html in the url:


If(config.url.search('\\.html') != -1){
return config;
}

If it is, return request without any modification. Put this code first in the method.

Final Thoughts

Since this deals with service calls, I wasn't sure the best way to put a live sample together; so I did not do that for this post. Overall, I feel like I implemented a kludge in the UI in order to accommodate for what I perceive as a failing on the services. But, in application development there are a lot of moving parts beyond your control and you have to choose the best solution from given options.

Sign up for DotComIt's Monthly Technical Newsletter

Aligning Left Text, Center Text, and Right Text with CSS

I was building out an application for a client who wanted pagination on some tabled data. I needed to create a pagination bar that included a previous button on the left, a next button on the right, and a message in the center. In the old days I could implement something like this using tables, however I wanted to see if there was a CSS way to get the same layout. I found two separate, but great, implementations of this. This blog post will explain both of them.

Aligning Text with Float

My first attempt to implement this was to use the CSS float property. The float means that text moves out of its normal place in the container, usually moving to the left or right of the container. I could float some text to the right, some text to the left and keep some in the middle. Here is the CSS Style for left aligned text:


.leftAligned {
float : left;
}

This puts left aligned text to the left side of the div to contain our pagination header. Right aligned text is similar:


.rightAligned{
float : right;
text-align : right;
}

The float argument is specified as right so the rightAligned block will move to the right of the container. I also specified a text-align property to move the text all the way over to the right within the rightAligned container.

Finally, we need centered text:


.centeredText {
margin: 0 auto;
text-align : center;
}

This margin property is used to make sure that the centeredText can align right up against the right and left segments.

Use these in HTML:


<div>
<div class="leftAligned">Previous/div>

<div class="rightAligned">Next</div>
<div class="centeredText">Displaying 10-20 of 50</div>
</div>

Reviewing the code, you need to put the left and right aligned divs first, and the centered text last. This bugged a bit of my OCD, but worked almost perfectly.

Play with the code here. The Plunker sample uses some Angular code to simulate the forward and next navigation for sample purposes.

As the user was paginating through the data the previous and next links might disappear. When that happened the centered text would change spots. This wouldn't do. We could fix that by adding explicit widths to each div, which worked for the sample application. However my real world app was having problems with that, I believe due to other CSS conflicts. So, I went searching for a second way to accomplish this layout.

Aligning Text with CSS Tables

My second approach to was to use CSS Tables. You can use the display property in CSS to specify table, or table row or table cell.

First, create a table:


.table {
display:table;
width : 100%;
}

The table CSS style specifies the display type as table and width of 100%. This parallels the HTML table tag.

Next, create a table row:


.tr {
display:table-row;
width : 100%;
}

The table row specifies the display type as table-row. This parallels the HTML tr tag.

Now, the left-aligned text:


.leftAligned {
display:table-cell;
width : 25%;
}

The left aligned text specifies table-cell as the display type. The width is given as 25%. Next, the right aligned cell:


.rightAligned {
display:table-cell;
text-align : right;
width : 25%;
}

The right aligned table cell specifies table-cell as the display. It also right aligns the text with the text-align property and specifies the cell width as 25%.

Finally, create the centered text:


.centeredText {
display:table-cell;
text-align : center;
width : 50%;
}

The centered text also has a table-cell type. It also specifies the text-align to center the text; and the width is 50%. The three table cells have a combined width of 100%.

Finally, we need to create the table in the HTML. We'll use divs as the controllers. First, create the table div:


<div class="table">
</div>

Inside of that, put a table row:


<div class="tr">
</div>

Now put the main content inside the table row:

Previous
Displaying 11-20 of 50
Next

Play with the Code Here. As with the previous sample, this uses some AngularJS magic to make the forward and next buttons work.

Final Thoughts

I was surprised at how hard it was to find resources to explain how to do this, so hopefully this helps someone. Both approaches work well, depending what you're doing. The limitation of the second approach--which is specifying table widths--is that if you have text that is expanding or contracting to unknown widths you may have odd layouts as the text grows beyond their containers. But, I'm sure that can be worked around if you run into the problem.

Sign up for DotComIt's Monthly Technical Newsletter

Passing Parameters to a State - AngularJS UI Router - Part 7

This is part of a series of posts about the AngularJS UI Router. Check out part 1, part 2, part 3, Part 4, Part 5, and Part 6.

This is the last entry in this series. I'm going to modify the sample from the previous section to show you how to pass parameters to a state. We'll pass parameters to a headerController and use it to hide the link to the active state.

Resolve the Parameter

First, we'll need to modify the state definitions in the $stateProvider to pass a value to the views controller. In this case, we'll pass a hard coded string representing the name of the state. Here is the state definition for state1:


.state('state1', {
url: '/state1',
views : {
"header" : {templateUrl : 'states/08header.html',
controller:'headerController',
resolve : {
selectedNav : function(){
return "state1"
}
}
},
"mainContent" : {
templateUrl : 'states/07state1.html',
controller : 'state1Controller'
},
"footer" : {templateUrl : 'states/07footer.html'}
}
})

The header view definition is the one that changed the most. First, a controller, headerController, was added. We'll look at headerController shortly. Then a resolve property was added. The resolve property defines services, on demand, that will be passed to the view's controller. The new services are defined by a function, which returns the value of the service. Whenever the state is activated, the service functions are executed, and once all the service functions are activated the controller is initialized.

The definition for state2 is almost identical to state1:


.state('state2', {
url: '/state2',
views : {
"header" : {templateUrl : 'states/08header.html',
controller:'headerController',
resolve : {
selectedNav : function(){
return "state2"
}
}
},
"mainContent" : {
templateUrl : 'states/07state2.html',
controller : 'state2Controller'
},
"footer" : {templateUrl : 'states/07footer.html'}
}
})

The header view of state2 uses the same controller. It also passes in the same selectedNav service. The main difference is the value of that service. For the state1, the value of selectedNav is state1. For state2 the value of the selectedNav is state2.

Let's look at the headerController:


angular.module('routerTestApp').controller('headerController',
['$scope','selectedNav',function($scope,selectedNav){
$scope.selectedNav = selectedNav;
}]
);

The headerController is a simple controller. It accepts two arguments, the $scope and the selectedNav. All the code does is copy the selectedNav simple value into a $scope variable so it can be accessed inside the view template.

Remove the link to the Active State

To remove the link to the active state, open up the header.html file. You should see two links, similar to this:


<a ui-sref="state1" >State 1</a>
<a ui-sref="state2" >State 2</a>

To remove the states, the ng-hide will be used. We'll just put together a condition that compares the selectedNav controller variable to the actual selected state:


<a ui-sref="state1" ng-hide="selectedNav == 'state1'">State 1</a>
<a ui-sref="state2" ng-hide="selectedNav == 'state2'">State 2</a>

This will make things work.

Test the App

Load the app:

The app loads in the default state, state1. You'll see that the link to open state1 is hidden while state1 is displayed. Then click the State 2 link:

Once the app enters State2 the State2 link is hidden, and there is only a link to move back to state1.

Play with the App here!

Resolve can be a very powerful tool and is not limited to simple values. Objects, functions, promise objects, or other services can all be passed as values to a view controller using resolve.

Final Thoughts

The uiRouter is a great tool for Angular developers who want flexibility when piecing their applications together. As this white paper has shown, the complexity easily scales up and down depending upon your needs. I wish I had discovered it earlier than I did.

We wrote 20 pages on the Angular uiRouter, sign up to get it!

Add Multiple Views to a Single App - AngularJS UI Router - Part 6

This is part of a series of posts about the AngularJS UI Router. Check out part 1, part 2, part 3, Part 4, and Part 5.

One aspect of the uiRouter is that you can add multiple views to a single Angular module. In this sample we're going to create an angular app that has a header, footer, and a main content area, all defined as separate views..

Create the Multiple Views Config

In previous samples, we had a lot of nested views. While nested views will work perfectly fine even if you have multiple views, I pulled them out of this sample for simplicity sake. To define the router, go back to the config block:


$stateProvider
.state('state1', {
url: '/state1',
views : {
"header" : {templateUrl : 'states/header.html'},
"mainContent" : {
templateUrl : 'states/state1.html',
controller : 'state1Controller'
},
"footer" : {templateUrl : 'states/footer.html'}
}
})
.state('state2', {
url: '/state2',
views : {
"header" : {templateUrl : 'states/header.html'},
"mainContent" : {
templateUrl : 'states/state2.html',
controller : 'state2Controller'
},
"footer" : {templateUrl : 'states/footer.html'}
}
})

Each state is defined with a name and an object defining that state's properties. The state has two properties. The first defines the state's URL. This is the URL that will show in the browser bar when the state is loaded. The second piece is an object defining the views. Each element of the view object refers to an active view. It tells the router that in the current state, which template each view will display. All view properties are valid here including the templateUrl and controller. I specify the controller on the mainContent views, but for the sake of this sample the header and footer do not have a custom controller.

Create the Template HTML

Let's create the HTML templates. First, the header.html:


<a ui-sref="state1">State 1</a> | <a ui-sref="state2">State 2</a>

I created a simple navigation bar, which includes link to switch between the two states. Now, the footer.html:


Copyright notice, contact link, and other footer stuff here!

I just put in some placeholder text for common things you might find in the footer of a public web site. Both footer.html and header.html are reused between the two states.

Finally, take a look at the state1.html, which will be the main content for the state1 view:


<h1>{{title}}</h1>
Other State 1 content

The state1.html template references the title from the controller; just as it did in previous samples. It also displays some static placeholder text for state 1 content.

This is state2.html which will be displayed when the app is displaying state 2:


<h1>{{title}}</h1>
Other State 2 Content

The template for state2 is almost identical to state1. The main different is that the description text for fake content refers to State2 instead of state1.

Create the Views in the Main Template

Finally, you'll have to add all three views to the main template of your HTML application.


<div ui-view="header"></div>
<div ui-view="mainContent"></div>
<div ui-view="footer"></div>

Previously, we just added the uiView directive with no value. However, when you have multiple views, you must name them so the uiRouter knows which one to direct at which app.

Test the App

Load the app, and you should see this:

The app loaded into the default state of State 1. You can see the navigation bar up top and the footer at the bottom, with the main content in the center. Click the State 2 link:

The main content changed, but the footer and header stayed the same.

Play with the code here.

What's Next

The next section of this series will discuss how to pass parameters to a state.

We wrote 20 pages on the Angular uiRouter, sign up to get it!

Deeper Nesting with Views - AngularJS UI Router - Part 5

This is part of a series of posts about the AngularJS UI Router. Check out part 1, part 2, part 3, and Part 4.

In the previous section of this series, we showed a sample of nesting a view inside another. This can be done for as many levels as you need, with a view put inside a view which is inside a view. This entry will show a sample of views that are nested 3 levels deep.

Define the Deeper Subviews

First, we need to define a subview that goes multiple levels deep. For completeness, here are all the defined states of state1:


$stateProvider
.state('state1', {
url: '/state1',
templateUrl : 'states/state1.html',
controller : 'state1Controller'
})
.state('state1.subview1', {
url: '/state1subview1',
templateUrl : 'states/state1subview1.html',
})
.state('state1.subview1.deeper', {
url: '/state1subview2deeper',
templateUrl : 'states/state1subview1deeper.html',
})
.state('state1.subview2', {
url: '/state1subview2',
templateUrl : 'states/state1subview2.html',
})
}]);

The default state is state1. Under state1, there are two states, state1.subview and state1.subview2. This code adds a brand new state, state1.subview.deeper. This is defined as proof of principle that the states can nest multiple levels deep. I left out state2 from the code here as it doesn't apply to this sample.

Modify the Subview HTML

Open up state1subview1.html file:


Sub view 1

<a ui-sref="state1.subview1">Hide Deeper Subview</a>
<a ui-sref="state1.subview1.deeper">Show Deeper Nested View 1</a>

<div ui-view></div>

The top is just text specifying the file being displayed. Next come two links. The first one loads the subview1, essentially hiding the second nested subview. The second links loads the second nested subview. Finally, the uiView directive is used on a div so uiRouter knows where to put the view that is three levels deep.

This is the template for the deeper nested view:


Deeper Even

It is simple with no additional functionality.

Test the Deeply Nested Views

Load the app and click the "Show Nested View 1" link:

Click the Show Deeper Nested View link, and the view that is two levels deep should display:

Play with the code here. You can use this approach to build complicated structures with dynamic content.

What's Next

The next entry in this series will show you how to add multiple views to a single application.

We wrote 20 pages on the Angular uiRouter, sign up to get it!

Nested Views - AngularJS UI Router - Part 4

This is part of a series of posts about the AngularJS UI Router. Check out part 1, part 2, part 3.

The samples this series has created so far have not demonstrated anything that couldn't be done using the ngRoute directive. Now is the time to advance. We'll add two nested views in the first state.

Define the Nested State

Creating a nested state is just like creating a non-nested state. It supports the sample object properties, such as the name, templateUrl, or controller. There are two ways to tell the uiRouter that this is a nested state. The first is to define the parent state, using a property named parent. I like this because it is a very overt. The second, more common, approach lies in the naming of the state. If a state includes a master state, followed by a period, followed by the state name, then that state is pegged as a substate.

Modify the config block to define two nested views:


$stateProvider
.state('state1', {
url: '/state1',
templateUrl : 'states/state1.html',
controller : 'state1Controller'
})
.state('state1.subview1', {
url: '/state1.subview1',
templateUrl : 'states/state1subview1.html',
})
.state('state1.subview2', {
url: '/state1.subview2',
templateUrl : 'states/state1subview2.html',
})
.state('state2', {
url: '/state2',
templateUrl : 'states/state2.html',
controller : 'state2Controller'
})

I repeated all states in this app for comparison purposes. State1 and state2 are defined with the state name and few properties. The first substate under state1 is named 'state1.subview1'. The second is named 'state1.subview2'. Each view is defined with a url, and a template, but for the purposes of this example I did not give them a custom controller. They will inherit from state1's controller in this case.

The period in the state name defines it as a substate, not the url property in the state object. The url could be anything you desire and is independent of the state name. For the purposes of these samples, I kept the state name and the url identical.

Create the Nested Views

Let's look at the HTML Behind the two subviews. This is subview 1:


Sub view 1

This is subview 2:


Sub view 2

Both are simplistic for demonstration purposes.

Activate the Nested Views

Let's look at the state1.html that will be used to activate the nested views:


<h1>{{title}}</h1>

<a ui-sref="state1">Hide Subview</a>
<a ui-sref="state1.subview1">Show Nested View 1</a>
<a ui-sref="state1.subview2">Show Nested View 2</a>
<div ui-view></div>

<a ui-sref="state2">Go to State 2</a>

The title display at the top and the link at the bottom to launch state 2 are the same as was used in previous samples. The middle part is new. It includes three hyperlinks, all using the uiSref directive to display and hide states. The first link goes directly to the state1; which is the default state. That will effectively hide all visible substates. The second link will open the first subview, and the third link will open the second subview.

After that we see a div with the uiView directive on it. This is where the subview is displayed in the context of the view. It is similar to how we put the main view on the main index page.

Test the Nested Views

Load this in an app to test the nested views:

The initial state displays all the links, but does not display any of the nested view content. Click the Show Nested View 1 link:

Then click the Nested View 2 Link:

Test out the code here.

What's Next?

The next entrie in this series will show you how to nest subviews more than 1 level deep. Multiple layers of nested views is part of what makes the uiRouter powerful.

We wrote 20 pages on the Angular uiRouter, sign up to get it!

More Entries

All Content Copyright 2005, 2006, 2007, 2008, 2009 Jeffry Houser. May not be reused without permission
BlogCFC was created by Raymond Camden. This blog is running version 5.9.2.002.