Naučte se AngularJS pomocí těchto 5 praktických příkladů

Pravděpodobně jste již slyšeli o AngularJS – vzrušujícím open source frameworku vyvinutém společností Google, který mění způsob, jakým přemýšlíte o webových aplikacích. Bylo o tom napsáno mnoho, ale ještě jsem nenašel něco, co je napsáno pro vývojáře, kteří preferují rychlé a praktické příklady. To se dnes mění. Níže najdete základní stavební kameny aplikací Angular – Modely, Pohledy, Kontroléry, Služby a Filtry – vysvětlené v 5 praktických příkladech, které můžete upravovat přímo ve svém prohlížeči. Pokud je chcete otevřít ve svém oblíbeném editoru kódu, uchopte zip výše.

Co je AngularJS?

Na vysoké úrovni je AngularJS framework, který spojuje vaše HTML (zobrazení) s JavaScriptovými objekty (modely). Když se vaše modely změní, stránka se automaticky aktualizuje. Platí to i naopak – model, spojený s textovým polem, se aktualizuje při změně obsahu pole. Angular zpracovává veškerý kód lepidla, takže nemusíte ručně aktualizovat HTML nebo poslouchat události, jako to děláte s jQuery. Ve skutečnosti žádný z příkladů nezahrnuje ani jQuery!

Chcete-li používat AngularJS, musíte jej zahrnout na stránku před uzavírací značku . CDN společnosti Google se doporučuje pro rychlejší načítání:

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

AngularJS vám poskytuje velké množství direktiv, které vám umožňují přiřadit prvky HTML k modelům. Jsou to atributy, které začínají ng- a lze jej přidat k libovolnému prvku. Nejdůležitější atribut, který musíte zahrnout do jakékoli stránky, pokud chcete používat Angular, je ng-app :

<body ng-app>

Měl by být přidán do prvku, který obklopuje zbytek stránky, jako je prvek body nebo vnější prvek div. Angular jej vyhledá, když se stránka načte, a automaticky vyhodnotí všechny direktivy, které vidí ve svých podřízených prvcích.

Dost teorie! Nyní se podívejme na nějaký kód.

1. Navigační nabídka

Jako první příklad vytvoříme navigační nabídku, která zvýrazní vybranou položku. Příklad používá pouze direktivy Angular a je to nejjednodušší možná aplikace využívající framework. Klikněte na tlačítko Upravit ", chcete-li zobrazit zdrojový kód. Je připraven k experimentování!

<!-- 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;
}

Ve výše uvedeném kódu používáme direktivy Angular k nastavení a čtení aktivního variabilní. Když se změní, způsobí to, že HTML, který jej používá, se automaticky aktualizuje. V Angularově terminologii se tato proměnná nazývá model. Je k dispozici všem direktivám v aktuálním rozsahu a lze k ní přistupovat ve vašich kontrolérech (více o tom v dalším příkladu).

Pokud jste již dříve používali šablony JavaScriptu, znáte {{var}} syntax. Když framework uvidí takový řetězec, nahradí ho obsahem proměnné. Tato operace se opakuje při každé změně var.

2. Vložený editor

Pro druhý příklad si vytvoříme jednoduchý inline editor – kliknutím na odstavec se zobrazí tooltip s textovým polem. Použijeme ovladač, který inicializuje modely a deklaruje dvě metody pro přepínání viditelnosti tooltipu. Ovladače jsou běžné funkce JavaScriptu, které Angular spouští automaticky a které jsou spojeny s vaší stránkou pomocí ng-controller směrnice:

<!-- 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;
}

Po provedení funkce ovladače získá speciální $scope objekt jako parametr. Přidáním vlastností nebo funkcí je zpřístupníte zobrazení. Pomocí ng-model vazba na textové pole říká Angularu, aby tuto proměnnou aktualizoval, když se změní hodnota pole (to následně znovu vykreslí odstavec s hodnotou).

3. Objednávkový formulář

V tomto příkladu zakódujeme objednávkový formulář s celkovou cenou aktualizovanou v reálném čase pomocí další z užitečných funkcí Angular – filtrů. Filtry umožňují upravovat modely a lze je zřetězit pomocí znaku | . V níže uvedeném příkladu používám měnový filtr k přeměně čísla na správně formátovanou cenu, doplněnou znakem dolaru a centy. Můžete si snadno vytvořit vlastní filtry, jak uvidíte v příkladu #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;
}

ng-repeat vazba (docs) je další užitečnou funkcí rámce. Umožňuje vám procházet řadou položek a generovat pro ně označení. Je inteligentně aktualizován při změně nebo odstranění položky.

Poznámka: Úplnější verzi naleznete v tomto návodu, který je založen na tomto, napsaném pomocí Backbone.js.

4. Okamžité vyhledávání

Tento příklad umožní uživatelům filtrovat seznam položek zadáním do textového pole. Toto je další místo, kde Angular září, a je to perfektní případ použití pro psaní vlastního filtru. Abychom toho dosáhli, musíme nejprve naši aplikaci přeměnit na modul.

Moduly představují způsob, jak organizovat aplikace JavaScript do samostatných komponent, které lze kombinovat novými a zajímavými způsoby. Angular spoléhá na tuto techniku ​​pro izolaci kódu a vyžaduje, aby ji vaše aplikace dodržovala, než budete moci vytvořit filtr. Existují pouze dvě věci, které musíte udělat, abyste svou aplikaci změnili na modul:

  1. Použijte angular.module("name",[]) volání funkce ve vašem JS. Tím se vytvoří instance a vrátí se nový modul;
  2. Předejte název modulu jako hodnotu ng-app směrnice.

Vytvoření filtru je pak stejně jednoduché jako volání filter() metoda na objektu modulu vráceného 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://tutorialzine.com/2013/07/50-must-have-plugins-for-extending-twitter-bootstrap/',
            title: '50 Must-have plugins for extending Twitter Bootstrap',
            image: 'https://tutorialzine.com/media/2013/07/featured_4.jpg'
        },
        {
            url: 'https://tutorialzine.com/2013/08/simple-registration-system-php-mysql/',
            title: 'Making a Super Simple Registration System With PHP and MySQL',
            image: 'https://tutorialzine.com/media/2013/08/simple_registration_system.jpg'
        },
        {
            url: 'https://tutorialzine.com/2013/08/slideout-footer-css/',
            title: 'Create a slide-out footer with this neat z-index trick',
            image: 'https://tutorialzine.com/media/2013/08/slide-out-footer.jpg'
        },
        {
            url: 'https://tutorialzine.com/2013/06/digital-clock/',
            title: 'How to Make a Digital Clock with jQuery and CSS3',
            image: 'https://tutorialzine.com/media/2013/06/digital_clock.jpg'
        },
        {
            url: 'https://tutorialzine.com/2013/05/diagonal-fade-gallery/',
            title: 'Smooth Diagonal Fade Gallery with CSS3 Transitions',
            image: 'https://tutorialzine.com/media/2013/05/featured.jpg'
        },
        {
            url: 'https://tutorialzine.com/2013/05/mini-ajax-file-upload-form/',
            title: 'Mini AJAX File Upload Form',
            image: 'https://tutorialzine.com/media/2013/05/ajax-file-upload-form.jpg'
        },
        {
            url: 'https://tutorialzine.com/2013/04/services-chooser-backbone-js/',
            title: 'Your First Backbone.js App – Service Chooser',
            image: 'https://tutorialzine.com/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;
}

Filtry se řídí filozofií Angular.js – každý kus kódu, který napíšete, by měl být samostatný, testovatelný a znovu použitelný. Tento filtr můžete použít ve všech svých zobrazeních a dokonce jej zkombinovat s ostatními prostřednictvím řetězení.

5. Přepínatelná mřížka

Další oblíbenou interakcí uživatelského rozhraní je přepínání mezi různými režimy rozvržení (mřížka nebo seznam) kliknutím na tlačítko. To je v Angular velmi snadné. Kromě toho představím další důležitý pojem – Služby . Jsou to objekty, které může vaše aplikace použít ke komunikaci se serverem, rozhraním API nebo jiným zdrojem dat. V našem případě napíšeme službu, která komunikuje s API Instagramu a vrátí pole s aktuálně nejoblíbenějšími fotografiemi.

Všimněte si, že aby tento kód fungoval, budeme muset na stránku zahrnout jeden další soubor Angular.js:

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

To zahrnuje modul ngResource pro snadnou práci s AJAX API (modul je vystaven jako $resource proměnná v kódu). Tento soubor je automaticky zahrnut v editoru níže.

<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;
}

Služby jsou zcela samostatné, což umožňuje psát různé implementace, aniž by to ovlivnilo zbytek vašeho kódu. Při testování můžete například upřednostnit vrácení pevně zakódované řady fotografií, což by urychlilo vaše testy.

Další čtení

Pokud jste dosáhli tohoto bodu, již jste pochopili základy vývoje s Angular. Pokud se však chcete stát profesionálem, musíte se toho hodně naučit. Zde je seznam zdrojů, které vám pomohou ve vašem pátrání:

  • Domovská stránka AngularJS
  • Průvodce AngularJS
  • Oficiální výukový program AngularJS
  • Seznam spousty a spousty dalších zdrojů, videí a výukových programů

No