Learn AngularJS With These 5 Practical Examples

Download

By now you've probably heard of AngularJS - the exciting open source framework, developed by Google, that changes the way you think about web apps. There has been much written about it, but I have yet to find something that is written for developers who prefer quick and practical examples. This changes today. Below you will find the basic building blocks of Angular apps - Models, Views, Controllers, Services and Filters - explained in 5 practical examples that you can edit directly in your browser. If you prefer to open them up in your favorite code editor, grab the zip above.

What is AngularJS?

On a high level, AngularJS is a framework that binds your HTML (views) to JavaScript objects (models). When your models change, the page updates automatically. The opposite is also true - a model, associated with a text field, is updated when the content of the field is changed. Angular handles all the glue code, so you don't have to update HTML manually or listen for events, like you do with jQuery. As a matter of fact, none of the examples here even include jQuery!

To use AngularJS, you have to include it in your page before the closing <body> tag. Google's CDN is recommended for a faster load time:

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>

AngularJS gives you a large number of directives that let you associate HTML elements to models. They are attributes that start with ng- and can be added to any element. The most important attribute that you have to include in any page, if you wish to use Angular, is ng-app:

<body ng-app>

It should be added to an element that encloses the rest of the page, like the body element or an outermost div. Angular looks for it when the page loads and automatically evaluates all directives it sees on its child elements.

Enough with the theory! Now let's see some code.

1. Navigation Menu

As a first example, we will build a navigation menu that highlights the selected entry. The example uses only Angular's directives, and is the simplest app possible using the framework. Click the "Edit" button to see the source code. It is ready for experimentation!

<!-- Adding the ng-app declaration to initialize AngularJS -->
<div id="main" ng-app>
    <!-- The navigation menu will get the value of the "active" variable as a class.
         The $event.preventDefault() stops the page from jumping when a link is clicked. -->

    <nav class="{{active}}" ng-click="$event.preventDefault()">

        <!-- When a link in the menu is clicked, we set the active variable -->

        <a href="#" class="home" ng-click="active='home'">Home</a>
        <a href="#" class="projects" ng-click="active='projects'">Projects</a>
        <a href="#" class="services" ng-click="active='services'">Services</a>
        <a href="#" class="contact" ng-click="active='contact'">Contact</a>
    </nav>

    <!-- ng-show will show an element if the value in the quotes is truthful,
         while ng-hide does the opposite. Because the active variable is not set
         initially, this will cause the first paragraph to be visible. -->

    <p ng-hide="active">Please click a menu item</p>
    <p ng-show="active">You chose <b>{{active}}</b></p>
</div>
*{
    margin:0;
    padding:0;
}

body{
    font:15px/1.3 'Open Sans', sans-serif;
    color: #5e5b64;
    text-align:center;
}

a, a:visited {
    outline:none;
    color:#389dc1;
}

a:hover{
    text-decoration:none;
}

section, footer, header, aside, nav{
    display: block;
}

/*-------------------------
    The menu
--------------------------*/

nav{
    display:inline-block;
    margin:60px auto 45px;
    background-color:#5597b4;
    box-shadow:0 1px 1px #ccc;
    border-radius:2px;
}

nav a{
    display:inline-block;
    padding: 18px 30px;
    color:#fff !important;
    font-weight:bold;
    font-size:16px;
    text-decoration:none !important;
    line-height:1;
    text-transform: uppercase;
    background-color:transparent;

    -webkit-transition:background-color 0.25s;
    -moz-transition:background-color 0.25s;
    transition:background-color 0.25s;
}

nav a:first-child{
    border-radius:2px 0 0 2px;
}

nav a:last-child{
    border-radius:0 2px 2px 0;
}

nav.home .home,
nav.projects .projects,
nav.services .services,
nav.contact .contact{
    background-color:#e35885;
}

p{
    font-size:22px;
    font-weight:bold;
    color:#7d9098;
}

p b{
    color:#ffffff;
    display:inline-block;
    padding:5px 10px;
    background-color:#c4d7e0;
    border-radius:2px;
    text-transform:uppercase;
    font-size:18px;
}

In the code above, we are using Angular's directives to set and read the active variable. When it changes, it causes the HTML that uses it to be updated automatically. In Angular's terminology, this variable is called a model. It is available to all directives in the current scope, and can be accessed in your controllers (more on that in the next example).

If you have used JavaScript templates before, you are familiar with the {{var}} syntax. When the framework sees such a string, it replaces it with the contents of the variable. This operation is repeated every time var is changed.

2. Inline Editor

For the second example, we will create a simple inline editor - clicking a paragraph will show a tooltip with a text field. We will use a controller that will initialize the models and declare two methods for toggling the visibility of the tooltip. Controllers are regular JavaScript functions which are executed automatically by Angular, and which are associated with your page using the ng-controller directive:

<!-- When this element is clicked, hide the tooltip -->
<div id="main" ng-app ng-controller="InlineEditorController" ng-click="hideTooltip()">

    <!-- This is the tooltip. It is shown only when the showtooltip variable is truthful -->
    <div class="tooltip" ng-click="$event.stopPropagation()" ng-show="showtooltip">

        <!-- ng-model binds the contents of the text field with the "value" model.
         Any changes to the text field will automatically update the value, and
         all other bindings on the page that depend on it.  -->
        <input type="text" ng-model="value" />
    </div>

    <!-- Call a method defined in the InlineEditorController that toggles
     the showtooltip variable -->
    <p ng-click="toggleTooltip($event)">{{value}}</p>

</div>
// The controller is a regular JavaScript function. It is called
// once when AngularJS runs into the ng-controller declaration.

function InlineEditorController($scope){

    // $scope is a special object that makes
    // its properties available to the view as
    // variables. Here we set some default values:

    $scope.showtooltip = false;
    $scope.value = 'Edit me.';

    // Some helper functions that will be
    // available in the angular declarations

    $scope.hideTooltip = function(){

        // When a model is changed, the view will be automatically
        // updated by by AngularJS. In this case it will hide the tooltip.

        $scope.showtooltip = false;
    }

    $scope.toggleTooltip = function(e){
        e.stopPropagation();
        $scope.showtooltip = !$scope.showtooltip;
    }
}
*{
    margin:0;
    padding:0;
}

body{
    font:15px/1.3 'Open Sans', sans-serif;
    color: #5e5b64;
    text-align:center;
}

a, a:visited {
    outline:none;
    color:#389dc1;
}

a:hover{
    text-decoration:none;
}

section, footer, header, aside, nav{
    display: block;
}

/*-------------------------
    The edit tooltip
--------------------------*/

.tooltip{
    background-color:#5c9bb7;

    background-image:-webkit-linear-gradient(top, #5c9bb7, #5392ad);
    background-image:-moz-linear-gradient(top, #5c9bb7, #5392ad);
    background-image:linear-gradient(top, #5c9bb7, #5392ad);

    box-shadow: 0 1px 1px #ccc;
    border-radius:3px;
    width: 290px;
    padding: 10px;

    position: absolute;
    left:50%;
    margin-left:-150px;
    top: 80px;
}

.tooltip:after{
    /* The tip of the tooltip */
    content:'';
    position:absolute;
    border:6px solid #5190ac;
    border-color:#5190ac transparent transparent;
    width:0;
    height:0;
    bottom:-12px;
    left:50%;
    margin-left:-6px;
}

.tooltip input{
    border: none;
    width: 100%;
    line-height: 34px;
    border-radius: 3px;
    box-shadow: 0 2px 6px #bbb inset;
    text-align: center;
    font-size: 16px;
    font-family: inherit;
    color: #8d9395;
    font-weight: bold;
    outline: none;
}

p{
    font-size:22px;
    font-weight:bold;
    color:#6d8088;
    height: 30px;
    cursor:default;
}

p b{
    color:#ffffff;
    display:inline-block;
    padding:5px 10px;
    background-color:#c4d7e0;
    border-radius:2px;
    text-transform:uppercase;
    font-size:18px;
}

p:before{
    content:'✎';
    display:inline-block;
    margin-right:5px;
    font-weight:normal;
    vertical-align: text-bottom;
}

#main{
    height:300px;
    position:relative;
    padding-top: 150px;
}

When the controller function is executed, it gets the special $scope object as a parameter. Adding properties or functions to it makes them available to the view. Using the ng-model binding on the text field tells Angular to update that variable when the value of the field changes (this in turn re-renders the paragraph with the value).

3. Order Form

In this example, we will code an order form with a total price updated in real time, using another one of Angular's useful features - filters. Filters let you modify models and can be chained together using the pipe character |. In the example below, I am using the currency filter, to turn a number into a properly formatted price, complete with a dollar sign and cents. You can easily make your own filters, as you will see in example #4.

<!-- Declare a new AngularJS app and associate the controller -->
<form ng-app ng-controller="OrderFormController">

    <h1>Services</h1>

    <ul>
        <!-- Loop through the services array, assign a click handler, and set or
            remove the "active" css class if needed -->
        <li ng-repeat="service in services" ng-click="toggleActive(service)" ng-class="{active:service.active}">
            <!-- Notice the use of the currency filter, it will format the price -->
            {{service.name}} <span>{{service.price | currency}}</span>
        </li>
    </ul>

    <div class="total">
        <!-- Calculate the total price of all chosen services. Format it as currency. -->
        Total: <span>{{total() | currency}}</span>
    </div>

</form>
function OrderFormController($scope){

    // Define the model properties. The view will loop
    // through the services array and genreate a li
    // element for every one of its items.

    $scope.services = [
        {
            name: 'Web Development',
            price: 300,
            active:true
        },{
            name: 'Design',
            price: 400,
            active:false
        },{
            name: 'Integration',
            price: 250,
            active:false
        },{
            name: 'Training',
            price: 220,
            active:false
        }
    ];

    $scope.toggleActive = function(s){
        s.active = !s.active;
    };

    // Helper method for calculating the total price

    $scope.total = function(){

        var total = 0;

        // Use the angular forEach helper method to
        // loop through the services array:

        angular.forEach($scope.services, function(s){
            if (s.active){
                total+= s.price;
            }
        });

        return total;
    };
}
@import url(https://fonts.googleapis.com/css?family=Cookie);

*{
    margin:0;
    padding:0;
}

body{
    font:15px/1.3 'Open Sans', sans-serif;
    color: #5e5b64;
    text-align:center;
}

a, a:visited {
    outline:none;
    color:#389dc1;
}

a:hover{
    text-decoration:none;
}

section, footer, header, aside, nav{
    display: block;
}

/*-------------------------
    The order form
--------------------------*/

form{
    background-color: #61a1bc;
    border-radius: 2px;
    box-shadow: 0 1px 1px #ccc;
    width: 400px;
    padding: 35px 60px;
    margin: 50px auto;
}

form h1{
    color:#fff;
    font-size:64px;
    font-family:'Cookie', cursive;
    font-weight: normal;
    line-height:1;
    text-shadow:0 3px 0 rgba(0,0,0,0.1);
}

form ul{
    list-style:none;
    color:#fff;
    font-size:20px;
    font-weight:bold;
    text-align: left;
    margin:20px 0 15px;
}

form ul li{
    padding:20px 30px;
    background-color:#e35885;
    margin-bottom:8px;
    box-shadow:0 1px 1px rgba(0,0,0,0.1);
    cursor:pointer;
}

form ul li span{
    float:right;
}

form ul li.active{
    background-color:#8ec16d;
}

div.total{
    border-top:1px solid rgba(255,255,255,0.5);
    padding:15px 30px;
    font-size:20px;
    font-weight:bold;
    text-align: left;
    color:#fff;
}

div.total span{
    float:right;
}

The ng-repeat binding (docs) is another useful feature of the framework. It lets you loop through an array of items and generate markup for them. It is intelligently updated when an item is changed or deleted.

Note: For a more complete version, see this tutorial, which is based on this one, written with Backbone.js.

4. Instant Search

This example will allow users to filter a list of items by typing into a text field. This is another place where Angular shines, and is the perfect use case for writing a custom filter. To do this though, we first have to turn our application into a module.

Modules are a way of organizing JavaScript applications into self-contained components that can be combined in new and interesting ways. Angular relies on this technique for code isolation and requires that your application follows it before you can create a filter. There are only two things that you need to do to turn your app into a module:

  1. Use the angular.module("name",[]) function call in your JS. This will instantiate and return a new module;
  2. Pass the name of the module as the value of the ng-app directive.

Creating a filter then is as simple as calling the filter() method on the module object returned by angular.module("name", []).

<!-- Initialize a new AngularJS app and associate it with a module named "instantSearch"-->
<div ng-app="instantSearch" ng-controller="InstantSearchController">

    <div class="bar">
        <!-- Create a binding between the searchString model and the text field -->
        <input type="text" ng-model="searchString" placeholder="Enter your search terms" />
    </div>

    <ul>
        <!-- Render a li element for every entry in the items array. Notice
             the custom search filter "searchFor". It takes the value of the
             searchString model as an argument.
         -->
        <li ng-repeat="i in items | searchFor:searchString">
            <a href="{{i.url}}"><img ng-src="{{i.image}}" /></a>
            <p>{{i.title}}</p>
        </li>
    </ul>
</div>
// Define a new module for our app. The array holds the names of dependencies if any.
var app = angular.module("instantSearch", []);

// Create the instant search filter

app.filter('searchFor', function(){

    // All filters must return a function. The first parameter
    // is the data that is to be filtered, and the second is an
    // argument that may be passed with a colon (searchFor:searchString)

    return function(arr, searchString){

        if(!searchString){
            return arr;
        }

        var result = [];

        searchString = searchString.toLowerCase();

        // Using the forEach helper method to loop through the array
        angular.forEach(arr, function(item){

            if(item.title.toLowerCase().indexOf(searchString) !== -1){
                result.push(item);
            }

        });

        return result;
    };

});

// The controller

function InstantSearchController($scope){

    // The data model. These items would normally be requested via AJAX,
    // but are hardcoded here for simplicity. See the next example for
    // tips on using AJAX.

    $scope.items = [
        {
            url: 'https://blog.daitra.xyz/2013/07/50-must-have-plugins-for-extending-twitter-bootstrap/',
            title: '50 Must-have plugins for extending Twitter Bootstrap',
            image: 'https://blog.daitra.xyz/media/2013/07/featured_4.jpg'
        },
        {
            url: 'https://blog.daitra.xyz/2013/08/simple-registration-system-php-mysql/',
            title: 'Making a Super Simple Registration System With PHP and MySQL',
            image: 'https://blog.daitra.xyz/media/2013/08/simple_registration_system.jpg'
        },
        {
            url: 'https://blog.daitra.xyz/2013/08/slideout-footer-css/',
            title: 'Create a slide-out footer with this neat z-index trick',
            image: 'https://blog.daitra.xyz/media/2013/08/slide-out-footer.jpg'
        },
        {
            url: 'https://blog.daitra.xyz/2013/06/digital-clock/',
            title: 'How to Make a Digital Clock with jQuery and CSS3',
            image: 'https://blog.daitra.xyz/media/2013/06/digital_clock.jpg'
        },
        {
            url: 'https://blog.daitra.xyz/2013/05/diagonal-fade-gallery/',
            title: 'Smooth Diagonal Fade Gallery with CSS3 Transitions',
            image: 'https://blog.daitra.xyz/media/2013/05/featured.jpg'
        },
        {
            url: 'https://blog.daitra.xyz/2013/05/mini-ajax-file-upload-form/',
            title: 'Mini AJAX File Upload Form',
            image: 'https://blog.daitra.xyz/media/2013/05/ajax-file-upload-form.jpg'
        },
        {
            url: 'https://blog.daitra.xyz/2013/04/services-chooser-backbone-js/',
            title: 'Your First Backbone.js App – Service Chooser',
            image: 'https://blog.daitra.xyz/media/2013/04/service_chooser_form.jpg'
        }
    ];

}
*{
    margin:0;
    padding:0;
}

body{
    font:15px/1.3 'Open Sans', sans-serif;
    color: #5e5b64;
    text-align:center;
}

a, a:visited {
    outline:none;
    color:#389dc1;
}

a:hover{
    text-decoration:none;
}

section, footer, header, aside, nav{
    display: block;
}

/*-------------------------
    The search input
--------------------------*/

.bar{
    background-color:#5c9bb7;

    background-image:-webkit-linear-gradient(top, #5c9bb7, #5392ad);
    background-image:-moz-linear-gradient(top, #5c9bb7, #5392ad);
    background-image:linear-gradient(top, #5c9bb7, #5392ad);

    box-shadow: 0 1px 1px #ccc;
    border-radius: 2px;
    width: 400px;
    padding: 14px;
    margin: 45px auto 20px;
    position:relative;
}

.bar input{
    background:#fff no-repeat 13px 13px;
    background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkU5NEY0RTlFMTA4NzExRTM5RTEzQkFBQzMyRjkyQzVBIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkU5NEY0RTlGMTA4NzExRTM5RTEzQkFBQzMyRjkyQzVBIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6RTk0RjRFOUMxMDg3MTFFMzlFMTNCQUFDMzJGOTJDNUEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6RTk0RjRFOUQxMDg3MTFFMzlFMTNCQUFDMzJGOTJDNUEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4DjA/RAAABK0lEQVR42pTSQUdEURjG8dOY0TqmPkGmRcqYD9CmzZAWJRHVRIa0iFYtM6uofYaiEW2SRJtEi9YxIklp07ZkWswu0v/wnByve7vm5ee8M+85zz1jbt9Os+WiGkYdYxjCOx5wgFeXUHmtBSzpcCGa+5BJTCjEP+0nKWAT8xqe4ArPGEEVC1hHEbs2oBwdXkM7mj/JLZrad437sCGHOfUtcziutuYu2v8XUFF/4f6vMK/YgAH1HxkBYV60AR31gxkBYd6xAeF3VzMCwvzOBpypX8V4yuFRzX2d2gD/l5yjH4fYQEnzkj4fae5rJulF2sMXVrAsaTWttRFu4Osb+1jEDT71/ZveyhouTch2fINQL9hKefKjuYFfuznXWzXMTabyrvfyIV3M4vhXgAEAUMs7K0J9UJAAAAAASUVORK5CYII=);

    border: none;
    width: 100%;
    line-height: 19px;
    padding: 11px 0;

    border-radius: 2px;
    box-shadow: 0 2px 8px #c4c4c4 inset;
    text-align: left;
    font-size: 14px;
    font-family: inherit;
    color: #738289;
    font-weight: bold;
    outline: none;
    text-indent: 40px;
}

ul{
    list-style: none;
    width: 428px;
    margin: 0 auto;
    text-align: left;
}

ul li{
    border-bottom: 1px solid #ddd;
    padding: 10px;
    overflow: hidden;
}

ul li img{
    width:60px;
    height:60px;
    float:left;
    border:none;
}

ul li p{
    margin-left: 75px;
    font-weight: bold;
    padding-top: 12px;
    color:#6e7a7f;
}

Filters follow the Angular.js philosophy - every piece of code that you write should be self-contained, testable and reusable. You can use this filter in all your views and even combine it with others through chaining.

5. Switchable Grid

Another popular UI interaction is switching between different layout modes (grid or list) with a click of a button. This is very easy to do in Angular. In addition, I will introduce another important concept - Services. They are objects that can be used by your application to communicate with a server, an API, or another data source. In our case, we will write a service that communicates with Instagram's API and returns an array with the most popular photos at the moment.

Note that for this code to work, we will have to include one additional Angular.js file in the page:

<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular-resource.min.js"></script>

This includes the ngResource module for easily working with AJAX APIs (the module is exposed as the $resource variable in the code). This file is automatically included in the editor below.

Due to changes in the Instagram API, this demo is no longer working. However, the rest of the code, including the AJAX request, is still correct.
<div ng-app="switchableGrid" ng-controller="SwitchableGridController">

    <div class="bar">

        <!-- These two buttons switch the layout variable,
             which causes the correct UL to be shown. -->

        <a class="list-icon" ng-class="{active: layout == 'list'}" ng-click="layout = 'list'"></a>
        <a class="grid-icon" ng-class="{active: layout == 'grid'}" ng-click="layout = 'grid'"></a>
    </div>

    <!-- We have two layouts. We choose which one to show depending on the "layout" binding -->

    <ul ng-show="layout == 'grid'" class="grid">
        <!-- A view with big photos and no text -->
        <li ng-repeat="p in pics">
            <a href="{{p.link}}" target="_blank"><img ng-src="{{p.images.low_resolution.url}}" /></a>
        </li>
    </ul>

    <ul ng-show="layout == 'list'" class="list">
        <!-- A compact view smaller photos and titles -->
        <li ng-repeat="p in pics">
            <a href="{{p.link}}" target="_blank"><img ng-src="{{p.images.thumbnail.url}}" /></a>
            <p>{{p.caption.text}}</p>
        </li>
    </ul>
</div>
// Define a new module. This time we declare a dependency on
// the ngResource module, so we can work with the Instagram API

var app = angular.module("switchableGrid", ['ngResource']);

// Create and register the new "instagram" service
app.factory('instagram', function($resource){

    return {
        fetchPopular: function(callback){

            // The ngResource module gives us the $resource service. It makes working with
            // AJAX easy. Here I am using the client_id of a test app. Replace it with yours.

            var api = $resource('https://api.instagram.com/v1/media/popular?client_id=:client_id&callback=JSON_CALLBACK',{
                client_id: '642176ece1e7445e99244cec26f4de1f'
            },{
                // This creates an action which we've chosen to name "fetch". It issues
                // an JSONP request to the URL of the resource. JSONP requires that the
                // callback=JSON_CALLBACK part is added to the URL.

                fetch:{method:'JSONP'}
            });

            api.fetch(function(response){

                // Call the supplied callback function
                callback(response.data);

            });
        }
    }

});

// The controller. Notice that I've included our instagram service which we
// defined below. It will be available inside the function automatically.

function SwitchableGridController($scope, instagram){

    // Default layout of the app. Clicking the buttons in the toolbar
    // changes this value.

    $scope.layout = 'grid';

    $scope.pics = [];

    // Use the instagram service and fetch a list of the popular pics
    instagram.fetchPopular(function(data){

        // Assigning the pics array will cause the view
        // to be automatically redrawn by Angular.
        $scope.pics = data;
    });

}
*{
    margin:0;
    padding:0;
}

body{
    font:15px/1.3 'Open Sans', sans-serif;
    color: #5e5b64;
    text-align:center;
}

a, a:visited {
    outline:none;
    color:#389dc1;
}

a:hover{
    text-decoration:none;
}

section, footer, header, aside, nav{
    display: block;
}

/*-------------------------
    The search input
--------------------------*/

.bar{
    background-color:#5c9bb7;

    background-image:-webkit-linear-gradient(top, #5c9bb7, #5392ad);
    background-image:-moz-linear-gradient(top, #5c9bb7, #5392ad);
    background-image:linear-gradient(top, #5c9bb7, #5392ad);

    box-shadow: 0 1px 1px #ccc;
    border-radius: 2px;
    width: 580px;
    padding: 10px;
    margin: 45px auto 25px;
    position:relative;
    text-align:right;
    line-height: 1;
}

.bar a{
    background:#4987a1 center center no-repeat;
    width:32px;
    height:32px;
    display:inline-block;
    text-decoration:none !important;
    margin-right:5px;
    border-radius:2px;
    cursor:pointer;
}

.bar a.active{
    background-color:#c14694;
}

.bar a.list-icon{
    background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkYzNkFCQ0ZBMTBCRTExRTM5NDk4RDFEM0E5RkQ1NEZCIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkYzNkFCQ0ZCMTBCRTExRTM5NDk4RDFEM0E5RkQ1NEZCIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6RjM2QUJDRjgxMEJFMTFFMzk0OThEMUQzQTlGRDU0RkIiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6RjM2QUJDRjkxMEJFMTFFMzk0OThEMUQzQTlGRDU0RkIiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7h1bLqAAAAWUlEQVR42mL8////BwYGBn4GCACxBRlIAIxAA/4jaXoPEkMyjJ+A/g9MDJQBRhYg8RFqMwg8RJIUINYLFDmBUi+ADQAF1n8ofk9yIAy6WPg4GgtDMRYAAgwAdLYwLAoIwPgAAAAASUVORK5CYII=);
}

.bar a.grid-icon{
    background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjBEQkMyQzE0MTBCRjExRTNBMDlGRTYyOTlBNDdCN0I4IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjBEQkMyQzE1MTBCRjExRTNBMDlGRTYyOTlBNDdCN0I4Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MERCQzJDMTIxMEJGMTFFM0EwOUZFNjI5OUE0N0I3QjgiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MERCQzJDMTMxMEJGMTFFM0EwOUZFNjI5OUE0N0I3QjgiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4MjPshAAAAXklEQVR42mL4////h/8I8B6IGaCYKHFGEMnAwCDIAAHvgZgRyiZKnImBQsACxB+hNoDAQyQ5osQZIT4gH1DsBZABH6AB8x/JaQzEig++WPiII7Rxio/GwmCIBYAAAwAwVIzMp1R0aQAAAABJRU5ErkJggg==);
}

.bar input{
    background:#fff no-repeat 13px 13px;

    border: none;
    width: 100%;
    line-height: 19px;
    padding: 11px 0;

    border-radius: 2px;
    box-shadow: 0 2px 8px #c4c4c4 inset;
    text-align: left;
    font-size: 14px;
    font-family: inherit;
    color: #738289;
    font-weight: bold;
    outline: none;
    text-indent: 40px;
}

/*-------------------------
    List layout
--------------------------*/

ul.list{
    list-style: none;
    width: 500px;
    margin: 0 auto;
    text-align: left;
}

ul.list li{
    border-bottom: 1px solid #ddd;
    padding: 10px;
    overflow: hidden;
}

ul.list li img{
    width:120px;
    height:120px;
    float:left;
    border:none;
}

ul.list li p{
    margin-left: 135px;
    font-weight: bold;
    color:#6e7a7f;
}

/*-------------------------
    Grid layout
--------------------------*/

ul.grid{
    list-style: none;
    width: 570px;
    margin: 0 auto;
    text-align: left;
}

ul.grid li{
    padding: 2px;
    float:left;
}

ul.grid li img{
    width:280px;
    height:280px;
    display:block;
    border:none;
}

Services are entirely self-contained, which makes it possible to write different implementations without affecting the rest of your code. For example, while testing, you might prefer to return a hard-coded array of photos which would speed up your tests.

Further Reading

If you've reached this point, you have already grasped the basics of developing with Angular. However, there is much to learn if you want to be a pro. Here is a list of resources which will help you in your quest:

Bootstrap Studio

The revolutionary web design tool for creating responsive websites and apps.

Learn more

Related Articles

Awesome! Thanks so much!

Instant search is great. thank you

great examples , like it , i have question : what is the code editor you using to show this examples ? which have button run and edit ? ... thanks for great tuts all time .

Martin Angelov

Thank you! I made the editors myself. They are based on ace.

Ace editor is really a great editor for in the browser for css, javascript, html. I believe Github uses ace for the inline code editor as well.

ram swaroop

Awesome tutorial, keep up the great work!

Doug Turnbull

Thanks! Your readers might like my Angular instant search example that uses Solr. Its a great simple example to get started with Angular and Solr (something we do very frequently)

Very useful, thanks a lot!!!

This is really good man, keep up the good work. I was just about to write about Angular.js. The inline demos are a really neat idea.

Dean Sofer | ProLoser

Your filter instructions are misleading. If you call angular.module('myApp',[]) more than once, you will wipe out your app.
You also don't need to create a filter since you could use the built-in orderBy filter.
You also could use ui-route instead of having 4 css active class selectors for the navigation.

There is a problem with best practices and quality in the community. I'm extending a formal invitation to all bloggers and newsletters and tutorialists to come together to discuss an official AngularUI sponsored project.

https://github.com/angular-ui/community/issues/1

Martin Angelov

Thank you for your comment! A thing to keep in mind is that the article is for beginners who want to get a feeling of how common tasks can be accomplished with Angular. There are surely more efficient ways to do the examples, and I encourage people to continue learning about the framework and improving them.

I have to say, the more I read about AngularJS the more I waana learn it. This is one of best AngularJS post I've seen on Hacker News. Curiously waiting for part 2 :D

Any way you could do the instant search into an ajax search to database? I think that would be more practical for most sites.

Nice simple examples - well done.

The last example that switches between grid and list views is not very efficient since it creates both options and the shows/hides one. A simpler/better approach would be using a single ul with repeater and ng-switch and then enabling the alternate list elements using ng-switch-case.

Martin Angelov

Thank you for your input! Note taken.

Christian

I don't get it. How does Angular know when to call the total() function in the 3. Order form example?

Martin Angelov

Angular keeps track of which models were accessed the first time the function was run. Once one of these models change, the function is executed again, and the HTML is updated with the new total value.

ozgur coruhlu

Nice Tut Martin, I guess its time to explore more on this. Thanks

David Chase

Good article if I may only one suggestion when you introduce the module api and use it with filters, factories, etc you should make the controllers in that same format "app.controller" that way there is no questions about consistency in your app and also to showcase the ability to have controllers as methods of the module.

Awesome article Martin, after seeing these examples I think angular.js is an exciting and powerful javascript mvc to learn but after looking up the web for tutorials on angular.js for beginners I couldn't find any easy to understand tutorials or articles most of them are complex or incomplete
So I request You to write an article similar to the one you wrote on best resources to learn jQuery
Thank you

Jerrie Pelser

Nice examples, thank you Martin!

Beaglesong

Very nice demos! Simple and meaningful.

Hi Martin,
Great Article !!
Just wanted to ask if in the 4th example(Instant Search), it would be possible to also highlight the letters of the search results that match the letter typed in the searchBox.

Martin,
Thanks for excellent examples. Really learning a lot from these.

btw, in InstantSearch sample, any reason why you didn't just use filter function directly like this ?

<li ng-repeat="item in items | filter:searchString">

I am assuming you wanted to demo custom filter solution. Is that so ?

Great tutorial and it helped me and saved lot of my time.Keep up good work.Thanks.
--Nash.

Martin, your examples made it even more clear to me that I need to implement AngularJS for my web app. (otakucms). You certainly have a talent for design combined with code. Thanks!

What I need to understand most is how to do the equivalent of jQuery Ajax (pushing and pulling of data between server and client). So it looks like I need to learn about services. Also, reading your article made the concept of a single page site click in my head.

Great article, thank you.

Love the simplicity, clear comments explaining what is important, and lack of anything unnecessary and unimportant.

Thanks. You have covered every angle of angular…. Just saying...

Eckard Ritter

Instant search is very cool, thank you. You mention it would normally be used with Ajax. I would like to use it for an RSS feed. Found an Angularjs solution for feeds here - but I am very new to Angularjs and have obviously no chance to combine this with your instant search, i.e. searching a feed or even multiple feeds. Do you have something for this problem?

Thank you for sharing, it really helpful!!

Thanks for your post!
But I don't understand how it works with cross domain requests in Switchable Grid example. Browser limited by the same origin policy but in example you are making ajax with instagram url. Can you explain?

what I liked the most in your codes, you use 4 spaces for tabs :) hate 2sp tabs

Arpita Dey

This is so incredibly helpful! Thank you for taking the time to do this!

Abhisek Jana

Here is another tutorial with example and very easy to understand.

http://www.adeveloperdiary.com/angular-js/angular-1-x/the-best-way-to-master-angular-js-part-1/

Limani Bhavik

Thanks for sharing it helps me a lot.. :)

Thank you for this, it helps a lot :)

Shusil Banjade

Thank you very much for this. I want to learn much more about Angular.js , can you suggest me some tutorials / books / sites ??

theandroid

Excellent tutorial. Helped me a lot to get started.

Thank you for sharing this great post.
Check this one as well...
"AngularJS - When JavaScript Met MVC"
https://www.ziptask.com/AngularJS-When-JavaScript-Met-MVC

Awesome. Thank you so much for sharing this.

Madee Hatthapradit

Website that offers complete tutorials on Angular 2. Check it out!
http://tutorialsdojo.com/angular/
http://angular2academy.com/