Check out our new training course on AngularJS for Flex Developers

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!

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!

Adding a Controller to a State - AngularJS UI Router - Part 3

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

When building Angular applications, the views are not useful without an associated controller. An Angular controller provides the business logic behind the view. Thankfully, uiRouter provides a way to associate the state with a controller.

Create the Controllers

The first thing we're going to do is create the controllers. We can create a controller for each of the two views in the application:


angular.module('routerTestApp').controller('state1Controller',
['$scope',function($scope){
$scope.title = "State 1 Controller"
}]
);
angular.module('routerTestApp').controller('state2Controller',
['$scope',function($scope){
$scope.title = "State 2 Controller"
}]
);

These are two simple controllers. Each once contains a $scope variable, title, which will be used to replace the hard coded title used in previous examples.

Next, edit the config block to tell the $stateProvider which controller should go with which view:


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

The object which defines the state has a new property, named controller. This controller's value is a string that refers to the name of the controller. When the view is setup, the controller will be associated with the view.

Create new HTML Templates

For this sample, we'll need two new state templates. Instead of using hard coded titles in the template page, it will use a title whose text is populated by the title $scope variable. This is the template for state1:


<h1>{{title}}</h1>
<a ui-sref="state2">Go to State 2</a>

This is the template for state 2:


<h1>{{title}}</h1>
<a ui-sref="state1">Go to State 1</a>

These two templates will show the dynamic title variable as the header, and also still include the links to switch between states.

Test The App

Now you can load the app and see the changes. This is the initial load:

This is what the app should look like after changing to state 2:

Play with the full code here.

In a real world application, you'll see lots more code in the controllers and interaction in the views.

What's Next?

The next entry in this series will show you how to nest one view inside of another.

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

Setting a Default State - AngularJS UI Router - Part 2

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

The biggest problem with part 1 of this series was that no default state was set, so the app would load a blank screen. That is something that needs to be addressed. I could use two different ways to set up a default state, and I'm going to explain both of them here.

Create a state with no URL

The first approach to creating a default state is to create a state with an empty URL attribute. Do this in the config that defines the states with the $stateProvider:


angular.module('routerTestApp').config(['$stateProvider',
function($stateProvider){
$stateProvider
.state('state1', {
url: '/state1',
templateUrl : 'states/state1.html'
})
.state('state2', {
url: '/state2',
templateUrl : 'states/state2.html'
})
.state('default', {
url: '',
templateUrl : 'states/state1.html'
})
}
]);

The new state is added at the end. The name of the state is default, and the url parameter is an empty string. This will make it so that upon initial load the application--with no URL variables--the state1.hml template will load. Now upon initial load the app, you'll see the correct state loaded even if you don't specify it in the URL:

Play with the app here.

As I congratulated myself on the creativity of this approach, I realized a limitation of this very quickly. If I want state1 to equal the default state, then every property of the state1 object will need to be defined as part of the state2 object, so it feels like some duplication of code.

Set a default state with the $urlRouteProvider

The uiRouter provides a standard way to set the default state. Into the config you can pass a uiRouter service named $urlRouteProvider. This service can be used to specify the default state:


angular.module('routerTestApp')
.config(['$stateProvider','$urlRouterProvider',
function($stateProvider,$urlRouterProvider){
$urlRouterProvider.otherwise("/state1");
// other state setup code here
}
]);

The otherwise() function is called on the urlRouteProvider and it accepts a single string. If no state is loaded, then the otherwise state will be specified.

Load the new app to see the initial state load:

Play with the code here.

I prefer using the $urlRouteProvider to set a default state on the application.

Final Thoughts

The next article in this series will show you how to associate a controller with the state. Controller's are the part of AngularJS that include business logic.

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

Switching Between Two States - AngularJS UI Router - Part 1

This is the first in a series of blog posts about the AngularJS UI Router.

Part of the AngularJS library is a router, named ngRoute. The ngRoute library allowed you to show different views of your Angular application based on the URL in the browser. This works great for simple applications; however it has some distinct limitations. Only a single view can be displayed per URL and nested views are not supported. Applications often have multiple views, such as a header, footer, and some main content. Main content is often split up into multiple sections. The ngRoute directive can only bring us so far.

The AngularUI team has created an alternate router, uiRouter, to addresses some of the limitations of the ngRoute. The ngRoute directive approaches an application as a collection of URLs, each one displaying a different view. The uiRouter looks at an application as a collection of states, and it allows multiple, nested states. This is a series of blog posts about using the uiRouter within an AngularJS application.

Switching Between Two States

The first sample will show you how to switch between two separate states.

Create the Application Skeleton

First, import the Angular library and the ui-router library:


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

</script>

I reference both libraries from hosted CDN instead of copying them locally as part of my application. Next, create an Angular app in a script block:


<script>
angular.module('routerTestApp', ['ui.router']);
</script>

Configure the UI Router

A single configuration argument is passed into the Angular module, ui.router. This tells the Angular Module--routerTestApp--to load the uiRouter code and make it available to the application. This sample will create two UI States, state1 and state2. The router configuration is done in an Angular config block.


angular.module('routerTestApp').config(['$stateProvider',
function($stateProvider){
$stateProvider
.state('state1', {
url: '/state1',
templateUrl : 'states/state1.html'
})
.state('state2', {
url: '/state2',
templateUrl : 'states/state2.html'
})
}
]);

A service named $stateProvider is passed into the config block using the Angular dependency injection syntax. The $stateProvider service is part of the UI-router and is analogous to the $routeProvider used as part of ngRoute. The $stateProvider is used to define the two states of the application. In this case, a URL is defined. This is the value that will display in the URL when the state is displayed. A templateURL is used to refer to an HTML page that contains the view code that will be displayed when the state is active.

Create the HTML

First, we'll create the two HTML templates. The first is state1.html:


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

This template is very simple. It includes a header with simple text stating 'State 1'. The anchor link is a bit more interesting. It includes an Angular directive named uiSref. This directive tells the uiRouter library to load a new state and change the URL whenever that link is clicked. The value of the uiSref directive is the name of the state that should be loaded. If the state doesn't exist, the anchor is immediately disabled and does not present the user with a link.

The state2.html template is almost identical to state1.html:


<h1>State 2</h1>
<a ui-sref="state1">Go to State 1</a>

The header says 'State 2' instead of 'State 1'. The link goes back to state1 instead of state2. The main index needs some HTML to make this work too. First, add the ngApp directive to the body tag:


<body ng-app="routerTestApp">

Then, add the ui-view:


<div ui-view></div>

The uiView directive tells the uiRouter directive to put the view here.

Test the App

Load the app in a browser, be sure to specify the current state with a URL like '/Index.html#/state1'. You should see the first state:

Click the link to open state 2. State2 should open:

The URL should change and state2 is displayed on the screen.

Play with the code here.

Final Thoughts

The Plunker is a bit tricky to work with given the current codebase, because no default state is specified for the initial load of the application. Without a default state, neither state1 or state2 will be displayed unless you specify the state in the URL when you load the application.

The next article in this series will address that.

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

Why use Angular's $log instead of JavaScript's console.log()?

console.log() is a great debugging technique when writing JavaScript code. It immediately prints a variable or text to the browser's console and you can use that to keep track of code that has run flawlessly. AngularJS provides a $log service that provides the same functionality. Why would we want to use one over the other?

Using console.log()

I use console.log() a lot, and it is probably sprinkled all over the samples I've written about in this blog. It is easy to use.

In any part of JavaScript, just add a line like this to output a constant:


console.log('some constant');

Use a line like this to output a variable:


var myVar = 'Some Variable';
console.log(myVar);

You can play with this code here.

It is a really simple sample, but can be valuable when you want to drill down into an object being returned from a service or something similar.

Using AngularJS's $log service

Angular requires more setup. First, import the Angular library:


<script data-require="angular.js@1.5.7" data-semver="1.5.7" src="https://code.angularjs.org/1.5.7/angular.js"></script>

Then in a Script block, create an Angular module:


angular.module('mySimpleApp',[]);

Create an Angular controller, injecting the $log service into the controller function:


angular.module('mySimpleApp').controller('myController', ['$log',function($log){
}]);

Finally, perform the outputs similar to how we did in our console.log() example:


$log.debug('some constant')

var myVar = 'Some Variable';
$log.debug(myVar);

You can play with this code here.

You'll see the two samples operate very similarly.

Why use $log?

The main reason for using $log is for encapsulation purposes. When writing tests for your Angular controllers or services, it is better to have an object which you can easily mock. The Angular log service can be replaced during the Angular injection process. console.log() cannot be. I still use console.log() for quick and dirty debugging, primarily because that is what I learned first. For a more testable, encapsulated approach, go with the $log service.

Why does my Angular $scope.$on() event Listener run twice?

I was working with a client on an app; and had to communicate between two different controllers. When something happened in one controller--the main app--I wanted to update the view in the main navigation. You can use Angular's $broadcast() to dispatch an event and $on to execute an event listener. The problem is; my event listener was executing twice. Why was this?

TLDR: There were two instance of the controller.

The App Architecture

I'm going to explain the app architecture. The app had a navigation section and a footer section, both were always displayed to the user. Between those two sections on the page were section for the main content. This was populated using an Angular router based on the URL. For the purposes of this sample, I'll just create a single simple view. The page will look like this:

  • Header
  • Main Content
  • Footer

Let's create the app outline. First, import the Angular library:


<script src="//code.angularjs.org/1.5.6/angular.min.js"></script>

This is often the first thing to do in any Angular applications. Now create the Angular app:


angular.module('sampleApp',[]);

Create a controller for the navigation and the main content:


angular.module('sampleApp').controller('navigation',['$scope', function($scope){
}]);

angular.module('sampleApp').controller('mainContent',['$scope','$rootScope', function($scope,$rootScope){
}]);

The navigation controller accepts the $scope service as an argument. The mainContent controller accepts both the $scope and the $rootScope as arguments.

The view looks like this:


<body ng-app="sampleApp">

<div ng-controller="navigation">
</div>

<div ng-controller="mainContent">
</div>

<div ng-controller="navigation">
</div>

Notice here that the footer and the header use the same controller. That was the root of my confusion; and the reason the event listener in the navigation was dispatched twice. Don't do that.

Populate the App

I'm going to give this app skeleton enough code to demonstrate the problem. First, in the mainContent controller:


$scope.someValue = '';
$scope.onButtonClick = function(){
$rootScope.$broadcast('somethingHappened',{value : $scope.someValue});
};

I created a single #scope variable named someValue; and a single $scope function, onButtonClick(). The $scope function will execute in response to a button click, and will use the $broadcast() function on the $rootScope to dispatch an event.. Here is the mainContent view code:


<input type="text" ng-model="someValue">
<button ng-click="onButtonClick()">Do Something</button>

Enter some text in the input, click the button, and that will execute the onButtonClick() function in the mainContent Controller.

So, far so good. No create an event listener inside the navigation controller:


$scope.savedValue = '';
$scope.$on('somethingHappened', function(event, args){
$scope.savedValue = args.value;
});

The navigation controller includes a single $scope variable, named savedValue. It uses the $on() function to listen to the 'somethingHappened' event. The function takes the value dispatched as part of the event and copies it into $scope.savedValue; which in turn updates the navigation view. This is the navigation view code:


Saved Value displayed in Nav Div: {{savedValue}}<br/>

Run this App and play with the code.

It took me some head-scratching to figure out why the $on() in the navigation controller was executing. I didn't realize the same controller was used in both the footer and header. Two instances of the controller means two event listeners, which means the method gets executed twice.

In my case it was easy to discover; but if you are using Angular Routes; or any code that dynamically associates views and controllers, the second instance of the view may not be obvious. Using the same controller code for two different views is a technically valid Angular approach, but in practice I prefer not to use a controller with multiple views.

Why doesn’t ngChange fire when using ngPattern?

I was working on an app for a client recently. This app had a form, and when the user typed in one of the text inputs; I wanted to fire off an ngChange function to run some validation code. However, the ngChange was not firing consistently. It turned out to be a conflict with ngPattern. This post will explain the problem, and how to solve it.

Simple Sample

First step, I'm going to start with a simple sample demonstrating ngChange on a text Input. First, import the angular library:


<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.0-beta.1/angular.min.js"></script>

Next, create an angular module:


angular.module('ngChangeTest',[]);

Every angular module needs at least one controller:


angular.module('ngChangeTest').controller('someController',['$scope', function($scope){
}]);

The controller, someController, is created as part of the ngChangeTest module. It uses Angular's dependency injection syntax to pass the $scope service into the controller function. $scope is an easy way to share data and functions between a view and a controller.

Inside the controller, create a single variable and a single function:


$scope.inputText = '';
$scope.onTextChange = function(){
console.log($scope.inputText);
};

The inputText will be used with the ngModel of the textInput which we'll create in the view. The onTextChange() function will be executed when the ngChange event is fired in the view.

This is the HTML view code:


<body ng-app="ngChangeTest">
<div ng-controller="someController">
<input type="text" ng-model="inputText" ng-change="onTextChange()"> <Br/>
</div>

The ngChangeTest module is put on the body tag with the ngApp directive. The controller is added to a div. Inside the div is a text input. ngModel is associated with the $scope variable, inputText. And ngChange is associated with the onTextChange() function.

Try this out:

This works as I would have expected. For every keypress, the onTextChange() event is fired.

The problem with ngPattern

Once you introduce ngPattern into things get a bit more complicated. I'm going to use a regular expression that only accepts lowercase letters. Modify the HTML view to add the ngPattern directive on the input:


<input type="text" ng-model="inputText" ng-change="onTextChange()" ng-pattern="/^[a-z]+$/" >

The ngPattern directive does not modify the input and restrict what the user can enter into the input. It is just a regex used for validating the attribute. I was using this in a client's app which was using Angular form controls to determine whether an input was correct to determine whether the form could be submitted or not. The form was doing other validation in the ngChange method. However, I realized that the ngChange method was not firing consistently after the ngPattern directive was put in.

Try this out:

You can see from the console output that once I start typing uppercase letters; the ngChange function is no longer executing. By default, Angular prevents the ngModel from updating if the input does not meet the ngPattern; and then the ngChange does not execute if the ngModel is not updated. That was the problem I wanted to fix.

Adding in ng-options

We can tell Angular to always update the ngModel by using ngModelOptions:


<input type="text" ng-model="inputText" ng-change="onTextChange()" ng-pattern="/^[a-z]+$/" ng-model-options="{allowInvalid:true}">

The ngModelOptions directive allows us to tweak how Angular interacts with the ngModel. It is an object which contains a list of options and their values. In this case, we only need to change a single property on the options object. Set the allowInvalid to true. This means that Angular will change the ngModel even if the value is not valid according to the ngPattern. The result of this is that the ngChange function will execute regardless of the correctness of the user's input.

Play with this:

Final Thoughts

Sometimes Angular seems like a big block of unknown. There are many different ways to build angular applications and many different faucets that do not seem to be well documented, especially for changes that happen between versions. Finding these little gems is always a treasure.

Make a two column layout in Bootstrap

Multi-column layouts are a common requirement when building building HTML web sites or applications. In the old days, we would just use an HTML table and be done with it. But, these days using CSS for layout is the preferred approach. Using HTML tables for layout is considered a bad idea. Bootstrap provides an easy way to create a two column layout.

Create the two column layout: The Old Way

If I were using HTML tables to create a two column layout, I'd do something like this:


<table style="width:100%">
<tr>
<td>Label</td>
<td><input type="text" /></td>
</tr>
<tr>
<td>Some Other Label</td>
<td><input type="text" placeaholder="second text" /></td>
</tr>
</table>

This is an HTML table with two rows, and two cells in each row. This is simple, but the HTML can get complex if you are nesting tables to get specific layouts. When I started building HTML sites; this was the defacto standard for complex layouts.

Play with this sample!

Nowdays, CSS is considered superior for layout purposes than using lots of nested tables, and Bootstrap is a great framework to help you do that.

Import Bootstrap

The first step is to import the Bootstrap library into your HTML page. Bootstrap is primarily a CSS library, but also has a JavaScript portion. For the purposes of this article, we just need the CSS:


<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" >

Now all of the Bootstrap CSS is available to our application.

The Bootstrap Grid System

Bootstrap includes styles for a grid system consisting of rows and up to 12 columns. These are the associated styles:

  • row
  • Col-md-1
  • Col-md-2
  • Col-md-3
  • Col-md-4
  • Col-md-5
  • Col-md-6
  • Col-md-7
  • Col-md-8
  • Col-md-9
  • Col-md-10
  • Col-md-11
  • Col-md-12

The first style, row, is used to dictate a row in the grid. Inside the row; you would use some combination of the column styles to dictate the length of your cell; across a 12 column row. The col-md-1 style would span across a single column. The col-md-6 style would span across 6 columns. The col-md-12 style would spread across 12 columns, or a whole row. When putting together layouts with this approach, I like to make sure the total number of cells matches 12 for each row.

Create the two column layout: With Bootstrap

Bootstrap styles can be used to create a similar layout to the one created earlier, with a table. First, create the Bootstrap row:


<div id="row">
</div>

Inside the row, we can create two separate columns. For the purposes of this article, both will use col-md-6, meaning each column will span 6 cells, totaling 12 cells:


<div class="col-md-6">
Label
</div>
<div class="col-md-6">
<input type="text" />
</div>

The reason for making sure that the total number of columns used is equal to 12 is so that the next row will be placed under the first row; instead of next to it.

We can use the same approach for the second row:


<div id="row">
<div class="col-md-6">
Some Other Label
</div>
<div class="col-md-6">
<input type="text" placeaholder="second text" />
</div>
</div>

Play with the code here

Final Thoughts

The Bootstrap Grid system can be very powerful, especially when dealing with nested layout elements.

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.