Открыть меню    

Angular 1

Директивы в AngularJS использует для добавления элементам html дополнительной функциональности.
Сервисы AngularJS представляют специальные объекты или функции, выполняющие некоторые общие для всего приложения задачи. встроенные сервисы: $http, $q. Метод factory, принимает в качестве первого параметра принимает название сервиса. Кроме метода factory для создания сервиса можно также использовать метод Module.service. Его применение будет аналогично.

I Введение в AngularJS, привязки данных, директивы, обработка событий, валидация форм

    ng-bind-template

ng-bind-template - директива, которая замещает содержимое элемента данными полученными в результате выполнения выражения привязки, в отличии от ng-bind директива ng-bind-template может содержать несколько {{ }} выражений

HTML

<div ng-bind-template="First item: {{items[0].name}}. Second item: {{items[1].name}}"></div>

    ng-non-bindable

ng-non-bindable - указывает, что содержимое элемента не должно компилироваться и в нем не должна происходить привязка данных

HTML

<div ng-non-bindable>
AngularJS uses {{and}} characters for templates
</div>

Template directives - набор директив, которые могут использоваться для генерации HTML разметки:

    ng-repeat

ng-repeat ВАЖНО: Директива ng-repeat на каждой итерации создает свой локальный scope. Поэтому если нам нужно поменять значение переменной, которая лежит за пределами повторяющегося блока мы должны использовать $parent.var.

С помощью выражения $parent мы можем попросить подняться вверх от текущего scope к родительскому.

  • $index встроенная переменная содержащая номер итерации;
  • Переменная $even содержит значение true если элемент четный, иначе false. Переменная $odd содержит значение true если элемент нечетный, иначе false
  • Переменная $first содержит значение true если элемент первый в коллекции.
  • Переменная $last содержит значение true если элемент последний в коллекции

    ng-if

ng-if выводит или удаляет порцию DOM дерева в зависимости от выражения

<span ng-if="$first" class="class1">First</span>

Для повторения серии элементов, а не только одного элемента используются специальные точки ng-repeat-start и ng-repeat-end

    ng-cloak

ng-cloak - директива, которая предотвращает отображение шаблонов {{ }} до того, как приложение будет полностью загружено.

Директива может быть присвоена элементу body, но рекомендуется применять эту директиву несколько раз для небольших частей страницы.

<h3 ng-cloak>{{firstName}} {{lastName}}</h3>

    ng-options

ng-options - директива используется для динамической генерации элементов <option> для <select>.

Во многих случаях для создания <option> можно использовать ng-repeate.

ng-options предоставляет более эффективный способ создания элементов, который потребляет меньшее количество памяти и дает большую производительность
item for item in items - значение_для_текста for элемент in коллекция
использовать значение item как текст элемента при этом извлекать item из коллекции items

    ng-include

ng-include - директива для скачивания, компиляции и добавления фрагмента HTML в документ

URL должен принадлежать тому же домену, с которого загружена данная страница.
HTML фрагмент загружается с сервера с помощью AJAX запроса.

    ng-switch

ng-switch - директива используется для переключения структуры DOM на основе выражения. В отличии от ng-include данная директива не прозводит загрузку с сервера данных, а делает видимым один из элементов, который есть в DOM.

    ng-show & ng-hide

ng-show & ng-hide - отображает или прячет элемент в соответствии с выражением с помощью display:none

ng-if удаляет или создает порцию DOM основываясь на выражении (ng-if полностью удаляет элемент из DOM дерева, а не скрывает его).

    ng-class

  • ng-class - позволяет динамически устанавливать классы для HTML элементов через привязку данных и выражения;
  • ng-class - если выражение содержит массив, каждый элемент массива будет восприниматься как отдельный класс;
  • ng-class - если в качестве значения директива получает объект, каждый ключ объекта, содержащий значение true будет использоваться как имя класса

    ng-style

ng-style - позволяет установить CSS стили на определенный элемент

ng-class-even и ng-class-odd директивы работают в связке с ng-repeat и позволяют устанавливать определенные классы на четные и нечетные строки

    Обработка событий

  • ng-blur
  • ng-change
  • ng-click
  • ng-copy
  • ng-cut
  • ng-paste
  • ng-dblclick
  • ng-focus
  • ng-keydown
  • ng-keypress
  • ng-keyup
  • ng-mousedown
  • ng-mouseenter
  • ng-mouseleave
  • ng-mousemove
  • ng-mouseover
  • ng-mouseup
  • ng-submit

AngularJS предоставляет множество оберток над стандартными событиями JavaScript.

<button ng-click="count = count + 1">
Increment
</button>
<span>
count: {{count}}
</span>

    $event

<div class="test" ng-mouseenter="handleEvent($event)" ng-mouseleave="handleEvent($event)">
    {{type}}
</div>
$scope.handleEvent = function (event) {
    console.log(event);
    $scope.type = event.type;
}

$event - объект с описанием произошедшего события, также этот объект предоставляет методы для управления событиям.

    Атрибуты полезные, например, для манипуляции с элементами формы (Boolean attributes)

Boolean attributes - атрибуты, которые изменяют состояние элементов не зависимо от значения. Например, disabled, readonly и т.д.

Для того чтобы установить подобные атрибуты с помощью привязок используются директивы:

  • ng-checked используется для установки значения выбран\не выбран для input
  • ng-disabled используется для disable элемента применяется для input и button
  • ng-readonly используется для атрибута readonly применяется к input
  • ng-selected используется для атрибута selected в option

    ВАЛИДАЦИЯ ФОРМЫ (Проверка пользовательского ввода)

Атрибуты позволяющие валидировать данные формы

Валидация данных в AngularJS приложениях проходит с использованием стандартных HTML5 атрибутов, например type, required.

Для правильной работы валидации необходимо:

  1. Добавить на страницу форму.
  2. Установить атрибут name для формы. С помощью name можно будет об-ращаться к форме (свойствам формы, то есть есть объект, который доступен через значение name и посредством которого мы можем получить, через его свойства, сведения о состоянии формы) и получать дополнительные сведенья о корректности введенных данных.
  3. Установить атрибут novalidate для отключения валидации встроенной в браузер.

    Отслеживание корректности данных

  • $pristine - true если пользователь не взаимодействовал с элементами фор-мы
  • $dirty - true если пользователь взаимодействовал с элементами формы (то есть пользователь ввел какие-либо данные)
  • $valid - true если форма валидна (также к элемент ?)
  • $invalid - true если форма содержит не правильные данные (также к элемент ?)
  • $error - содержит информацию об ошибках

Отключение кнопки submit, если есть ошибка в введенных данных

<button type="submit" ng-disabled="myForm.$invalid">

myForm.$invalid для того чтобы работало данное выражение форма должна использовать атрибут name="myForm"

$invalid содержит значение, определяющее валидность формы: false - ошибок в форме нет, true - форма заполнена неправильно

    Стили валидации формы

При оформлении формы с проверкой введенных данных можно использо-вать следующие стили:

  • ng-pristine - этот класс используют все элементы с которыми еще не взаимо-действовал пользователь.
  • ng-dirty - применяются к тем элементам, с которыми взаимодействовал пользователь.
  • ng-valid - класс добавляется ко всем валидным элементам
  • ng-invalid - класс добавляется ко всем элементам которые неудачно прошли валидацию
/* Правило для всех элементов с которыми не взаимодействовал пользова-тель */
form.ng-pristine { border: 1px dotted gray; }

ОТМЕТЬТЕ:

angular.isDefined - функция, которая позволяет проверить наличие свойства объекта.

    Валидация формы с информацией об ошибке на элементе

ng-submit - событие срабатывающие при отправке формы.

<form name="myForm" novalidate ng-submit="addNewUser(newUser)">
<input name="userEmail" type="email" class="form-control" required ng-model="newUser.email">

<!-- Если userName был изменен myForm.userEmail.$dirty == true и его формат неправильный
myForm.userEmail.$invalid == true
Отобразить div в котором вывести сообщение соответствующее типу ошибки -->

<div class="error" ng-show="myForm.userEmail.$invalid && myForm.userEmail.$dirty">
    <span ng-show="myForm.userEmail.$error.email">Введите праивльный email</span>
    <span ng-show="myForm.userEmail.$error.required">Поле не должно быть пустым</span>
</div>
  • myForm.userEmail.$error.email - если ошибка связана с типом email;
  • myForm.userEmail.$error.required - если ошибка связана с типом required;

далее в примере более рациональное использование - функционал валидации выносим в контроллер:

<input name="userEmail" type="email" class="form-control" required ng-model="newUser.email">

<div class="error" ng-show="myForm.userEmail.$invalid && myForm.userEmail.$dirty">
    {{getError(myForm.userEmail.$error)}}
</div>

Для многих элементов форм ошибки могут быть одинаковыми, для того чтобы избавиться от дублирования кода можно использовать методы подобные getError:

$scope.getError = function (error) {
    if (angular.isDefined(error)) {
        if (error.required) {
            return "Поле не должно быть пустым";
        } else if (error.email) {
            return "Введите праивльный email";
        }
    }
}

Проверка идет на наличие свойств объекта $error конкретного элемента.

    валидация формы происходит только после нажатия на кнопку submit

В данном примере валидация формы происходит только после нажатия на кнопку submit. Для этого на форме установлена директива ng-submit с функцией, которая в качестве параметра принимает объект заполняемый в форме и свойство определяющее валидность формы.

HTML

<form name="myForm" novalidate ng-submit="addNewUser(newUser, myForm.$valid)">
<input name="userEmail" type="email" class="form-control" required ng-model="newUser.email">
<!--
Если userName был изменен myForm.userEmail.$dirty == true и его формат не правильный myForm.
userEmail.$invalid == true. Отобразить div в котором вывести сообщение соответствующее типу ошибки
-->
<div class="error" ng-show="showError">
    {{getError(myForm.userEmail.$error)}}
</div>

jQuery or Javascript

$scope.addNewUser = function (userDetails, isvalid) {
    if (isvalid) {
        $scope.message = userDetails.name + " " + userDetails.email;
    }
    else {
        $scope.message = "Error";
        $scope.showError = true;
    }
}

// Для многих элементов форм ошибки могут быть одинаковыми,
//для того чтобы избавиться от дублирования кода
// можно использовать методы подобные getError
$scope.getError = function (error) {
    if (angular.isDefined(error)) {
        if (error.required) {
            return "Поле не должно быть пустым";
        } if (error.email) {
            return "Введите праивльный email";
        }
    }
}

    Дополнительные свойства и директивы для валидации формы

Если у элемента атрибут type равен email, url, number то не нужно указывать ng-pattern, так как angular уже использует его для проверки на валидность содержимое элемента.

<input name="sample"
ng-model="inputValue"
ng-required="requireValue"
ng-minlength="3"
ng-maxlength="10"
ng-pattern="matchPattern" />

Данные свойства могут пригодиться при проверке объекта $error

    ng-change

ng-change позволяет задать выражение, которое будет срабатывать при смене значения элемента

    checkbox

  • ng-true-value - определяет значение, которое будет задано через привязку данных (ng-model) свойству модели, когда элемент checkbox будет выбран;
  • ng-false-value - значение для модели в случае если выделение будет снято

HTML

<input name="sample" type="checkbox" ng-model="inputValue" ng-true-value="'yes'" ng-false-value="'no'" />
{{inputValue}}

II Контроллеры и scope

  • Что такое контроллер и scope
  • Наследование контроллеров
  • Использование событий для передачи данных между scope
  • Как работает обновление привязок?
  • Явное обновление scope (методы $apply, $watch)

Контроллер (MVC) - логика приложения, соединяет представление и модель.

Содержит поведение (behaviors)и данные.

Контроллер должен:

  • Содержать логику инициализирующую scope.
  • Содержать логику/behaviors необходимую представлению для отображения данных.
  • Содержать логику/behaviorsнеобходимую для обновления данных на осно-ве действий пользователя

Контроллер не должен:

  • Содержать логику, которая манипулирует DOM (это задача представления)
  • Содержать логику, которая управляет сохранением данных (это задача мо-дели)

    Создание контроллера

Для создания контроллера используется функция controller вызываемая на модуле. В качестве параметров в функцию controller передается имя контроллера и функция, которая будет создавать контроллер. Функция создающая контроллер также называется фабричной функцией, которая посредством внедрения зависимостей получает $scope и прочие сервисы:

angular.module("exampleApp", []).controller("defaultCtrl", function ($scope) { });

    as

defaultCtrl as ctrl - синтаксис определение scope-less контроллера. Теперь для обращения к данному контроллеру в разметке следует использовать переменную ctrl. Но при этом мы теряем все возможности, которые дает нам scope.

var module = angular.module("exampleApp", [])
.controller("defaultCtrl", function () {
    this.dataValue = "------";
    this.changeValue = function () {
        this.dataValue = "Hello world!";
};
<div class="panel-heading" ng-controller="defaultCtrl as ctrl">
    <div class="input-group">
        <button class="btn btn-default"
        type="button" ng-click="ctrl.changeValue()">change</button>
        <input class="form-control" ng-model="ctrl.dataValue" />
    </div>
</div>

    Наследование контроллеров

Каждый раз, когда создается контроллер появляется отдельный экземпляр scope и scope явно между собой никак не связаны. То есть, если для одного контроллера создать два экземпляра – у них будет отличный scope (ниже val будет привязано к своему scope):

    <div ng-controller="defaultCtrl"> {{val}}…</div>

    <div ng-controller="defaultCtrl"> {{val}}…</div>

Если вы хотите взаимодействовать между контроллерами, то существует два способа:

  • 1 – Наследование
  • 2 – Использование событий

    Наследование (inheritance)

Для того чтобы реализовать наследование необходимо использовать соответствующую разметку в DOM-дереве. То есть дочерние контроллеры будут дочерними по отношению к родительским.

Например, если у контроллера нет value, то он поднимется по иерархии и посмотрит у родительского контроллера наличие переменной value.

Особенность работы контроллеров находящихся в иерархии: дочерний value перекроет value родительского контроллера, не меняя value родительского контроллера.

Если вы хотите позаботиться о том, чтобы переменная была ОБЩАЯ, то у данной переменой надо сделать ссылочный тип (например, объект и работаем с его свойствами и меняется один общий объект в разных контролле-рах).

    Использование событий

    $broadcast

$broadcast(name, args) – передача события дочерним scope (генерация вниз от текущего контроллера) или $broadcast - отправка события всем scope от rootScope (если вызов от $rootScope.$broadcast); name – имя события (любое название), args – дополнительный объект с данными

HTML

    <div ng-controller="senderCtrl">
        <p>Sender</p>
        <input ng-model="messageInput" />
        <button ng-click="send()">Send</button>
    </div>
    <div ng-controller="receiverCtrl">
        <p>Receiver 1</p>
        <p>Received message - {{info}}</p>
    </div>
    <div ng-controller="receiverCtrl">
        <p>Receiver 2</p>
        <p>Received message - {{info}}</p>
    </div>
app.controller("senderCtrl", function ($scope,  $rootScope) {
    $scope.send = function () {
        // $broadcast - отправка события всем scope от rootScope
        $rootScope.$broadcast("messageEvent", {
            message: $scope.messageInput
        });
    }
});
app.controller("receiverCtrl", function ($scope) {
        // обработка (создание обработчика) события messageEvent на текущем scope
        // args – это тот объект, который "бросался" при вызове .$broadcast (см. выше)
    $scope.$on("messageEvent", function (event, args) {
        $scope.info = args.message;
    })
});

    $emit

$emit(name, args) – передача события родительским scope (движемся вверх: от текущего scope и выше (родителю)) name – имя события, args – дополнительный объект с данными

<div ng-controller="testCtrl">
    <p>Controller 1</p>
    <p>Received message - {{info}}</p>
    <div ng-controller="testCtrl">
        <p>Controller 2</p>
        <p>Received message - {{info}}</p>
        <div ng-controller="testCtrl">
            <p>Controller 3</p>
            <p>Received message - {{info}}</p>
            <input ng-model="messageInput" />
            <button ng-click="send()">Send</button>
        </div>
    </div>
</div>
app.controller("testCtrl", function ($scope) {
    // обработка события messageEvent на текущем scope
    $scope.$on("messageEvent", function (event, args) {
        //event.stopPropagation(); // останавливаем распространение события
        $scope.info = args.message;
    })
    $scope.send = function () {
        // $emit - отправка события от текущего scope к родительским scope
        $scope.$emit("messageEvent", {
            message: $scope.messageInput
        });
    }
});

    $on

$on(name, handler) – установка обработчика на событие

name – имя события, handler –функция обрабатывающая полученное собы-тие

    Scope

Scope – объект, который позволяет связать пользовательский интерфейс с контроллером.

Scope организовываются в иерархическую структуру, в котором корневым элементом выступает $rootScope. Дочерние элементы формируются на основе созданных контроллеров в соответствии с размещением в DOM дереве.

Controller <-> scope <-> view

В котроллерах scope используется для двух задач:

  • 1 - передача в представление данных
  • 2 - определение поведения контроллера
// данные
$scope.fruit = "Apple";
// поведение
$scope.getFruit = function (fruit) { …
};

    $watch (наблюдение)

$scope.$watch("counter", function()…);

// если в качестве первого параметра мы передаем функцию, то эта функция будет запущена
// и будут рассмотрены данные возвращаемые функцией
// смотри пример 4 из раздела Директивы


Выше мы просим $scope наблюдать (watch) за изменениями в свойстве counter. Вызывая метод $watch мы создаем $$watchers (специальный объект, который отслеживает изменения в $scope); вторым параметром мы определяем функцию, которая будет вызываться в момент изменения свойства counter. У каждого $scope есть свои $$watchers: console.log($scope.$$watchers)

angular.module("simpleApp", []).controller("defaultCtrl", function ($scope, $interval) {

    $scope.counter = 0;

    // $watch регистрирует функцию в коллекции $$watchers текущего scope.
    // функция будут выполняться при каждом изменении свойства counter.
    $scope.$watch("counter", function (newValue, oldValue) {
        console.log("Старое значение - " + oldValue + ", новое значение - " + newValue);
    });

    $scope.increment = function (val) {
        $scope.counter += val;
    }

    // $interval - сервис, который используется для запуска задач через заданный интервал времени.
    $interval(function () {
        $scope.counter++;
    }, 1000);



    // $scope.$$watchers - коллекция объектов, которые выполняют отслеживание изменений в scope
    // При первом запуске происходит обход данной коллекции и объекты запоминают состояние scope на момент запуска приложения.
    // Если пользователь взаимодействует с приложением, например, нажимает по кнопке и выполняет метод в scope через ng-click, происходи обход
    // всех объектов $$watchers. Если watcher фиксирует изменения в scope соответствующие элементы интерфейса обновляются.
    // Для явного обхода $$watchers могут использоваться методы $apply или $digest (рассматриваются в следующих примерах)
    // В данном приложении будет создано два объекта watcher - для выражения {{counter}} на строке 40 и для $scope.$watch("counter", fn) на строке 13

    //console.log($scope.$$watchers)
});
<body ng-controller="defaultCtrl">
    {{counter}}{{counter}}
    <button ng-click="increment(1)">+1</button>
</body>

    $digest

setInterval(function () {
    $scope.counter++;
    $scope.$digest(); // обходим watchers функции и запускаем их при наличии изменений.
}, 1000);

    $apply

Вместо метода $digest рекомендуется использовать метод $apply. Их задачи схожи. Задача метода $apply произвести обход всех watchers, начиная с корневого scope.

$apply - используется для запуска выражения на определенном scope, обход watchers начинается с корневого scope

//обход watchers начинается с корневого scope
$scope.$apply(function () {
    $scope.counter++;
});

Использование $digest и $apply может пригодиться при интеграции angularjs с другими библиотеками или самописным js кодом.

    Интеграция angularjs и библиотеки jquery UI

При использовании посторонних библиотек часто приходиться самостоятельно контролировать процесс обновления scope.

В следующих примерах показано как можно обновлять scope при использовании библиотеки jQuery UI

Например реализуем связывание checkbox с кнопкой реализованной на jQuery UI.

// $watch(expression, handler) - установка обработчика, который будет срабатывать при смене указанного значения
$scope.$watch("buttonEnabled", function (newValue) {
    $('#jQueryUI button').button({
        disabled: !newValue
    });
});
<input type="checkbox" ng-model="buttonEnabled" />
<button class="btn btn-primary">
    <h4>Click me!</h4>
</button>

Реализуем счетчик при клике по кнопке, которая находится за пределами контроллера.

$(document).ready(function () {
    $('#jQueryUI button').button().click(function (e) {
        // angular.element(angularRegion) – этим мы превратили элемент в JQlite объект. У JQlite
        // есть, например, метод scope() (получаем доступ к scope)
        // angular.element(angularRegion).scope() - получение scope, который используется элементом разметки с id="angularRegion"
        // $apply - применяет изменения на объекте scope
        angular.element(angularRegion).scope().$apply("clickHandler()");
    });
});

…
$scope.clickHandler = function () {
    $scope.clickCounter++;
}
…

III Фильтры

Фильтры предназначены для преобразования данных перед тем как их обработает директива и перед их отображением во View.

Фильтры не меняют исходные данные в scope

Фильтры применяются для форматирования, сортировки и выборки данных из scope.

<td>{{ item.price | currency }}</td>
currency -  фильтр

    Типы фильтров:

  • Для работы с единичными данными
  • Для обработки коллекций

Встроенные фильтры для работы с единичными данными:

  • currency – форматирование валюты
  • date – форматирование даты
  • json – генерирует JSON строку из JavaScript объекта
  • number – форматирование числовых значений
  • uppercase – преобразует строку в верхний регистр
  • lowercase – преобразует строку в нижний регистр

Встроенные фильтры коллекций:

  • filter – выборка данных из массива
  • limitTo – ограничение количества элементов
  • orderBy – сортировка элементов

Фильтры трансформируют информацию до того как она будет обработана директивой, что позволяет одну и ту же информацию по разному отображать в различных частях приложения. Фильтры не меняют данные в scope, а только подготавливают их для отображения.

Форматирование данных в контроллере не желательно, так как значения меняются в источнике после чего теряется возможность выводить данные в другом представлении в другом формате.

    Фильтры для работы с единичными данными

<!-- Для применения фильтра используется символ | -->
<!-- expression | filter : parameters -->
<!--<td>{{item.price | currency}}</td>-->
{{item.price | currency : "UAH "}}
<!--используем параметр UAH (UAH ставится перед числовым значением)-->

Фильтр number - фильтр используется для форматирования числовых значений, автоматически добавляет запятую для разделения тысяч.

<td>{{item.count | number}}</td>
<td>{{item.tax | number : 1}}</td> <!--  здесь определяем до какой точности округляем значение -->

Фильтр date - Форматирование даты и времени

$scope.getExpiryDate = function (days) {
    var now = new Date();
    return now.setDate(now.getDate() + days);
    // получаем от текущей даты + количество дней (days)
}
<td>{{getExpiryDate(item.expiration) | date : "dd MMM yy"}}</td> <!—09 May 15  -->
<!--Форматирование дат используя date фильтр
| уууу | представление года 4-х значное число
| уу   | представление года 2-х значное число
| ММММ | представление месяца прописью
| МММ  | сокращенное представление месяца прописью
| ММ   | представление месяца числом
| М    | сокращенное представление месяца числом
| dd   | представление дня месяца с использованием 2-х чисел
| d    | представление дня месяца с использованием одного числа
| ЕЕЕЕ | полное название дня недели
| ЕЕЕ  | сокращенное полное название дня недели
| НН   | представление часов  с использованием 2-х чисел формат 24
| Н    | представление часов с использованием одного чисел формат 24
| hh   | представление часов с использованием 2-х чисел формат 12
| h    | представление часов с использованием одного чисел формат 12
| mm   | представление минут с использованием 2-х чисел
| m    | представление минут с использованием одного числа
| ss   | представление секунд с использованием 2-х чисел
| s    | представление секунд  с использованием одного числа
| a    | указатель a.m/p.m
| Z    | 4-х значное представления временной зоны-->
<!--Сокращения для форматирования даты
medium - MMM d, y h:mm:ss a
short - M/d/yy h:mm a
fullDate - EEEE, MMMM d,y
long-date - MMMM d, y
mediumDate - MMM d, y
shortDate - M/d/yy
mediumTime - h:mm:ss a
shortTime - h:mm a-->

И другие фильтры.

    Локализация

Для локализации (если вы хотите, например, чтобы показывались рубли и дата по-русски) необходимо подключить файлы:

Например, для немецкой

<script src="../Libraries/angular-locale_de-de.js"></script>

Далее мы просто используем нужные фильтры.

    Фильтры для коллекций:

limitTo

limitTo: значение - фильтр позволяющий отобрать из коллекции нужное количество элементов (например, первые n записей массива). При использо-вании отрицательного значения (в нашем случае это llimitValue) записи берутся с конца.

<tr ng-repeat="item in items | limitTo : limitValue">
....

    Используем filter

filter в документации по angularjs

1. Рассмотрим пример.

Фильтруем массив по наличию свойства у объекта:

$scope.items = [
{ name: "Item 1", price: 10.9, category: "Category 1", count: 10, tax: 1.12, expiration: 10 },
....
];
<!-- Получение только тех элементов, которые относятся к Category 1 -->
<tr ng-repeat="item in items | filter: { category : 'Category 1'}">
....

2. Для более тонкой настройки мы передаем не объект, а функцию:

Используем тот же массив что и выше.

$scope.selectItems = function (item) {
    return item.category == "Category 2" || item.name == "Item 5";
};
// Если функция selectItems возвращает true, то элемент отображается в интерфейсе – иначе нет
<!-- Использование функции для фильтрации элементов коллекции -->
<tr ng-repeat="item in items | filter : selectItems">

    orderBy

orderBy - фильтр для сортировки элементов массива.

1. Параметр – имя свойства по которому будет происходить сортировка. Имя свойства для сортировки задается в одинарных кавычках. С помощью символов + или - можно указать направление сортировки.

Например, item in items | orderBy : '-count'

2. Также использование orderBy может быть с функцией

Рассмотрим пример. Для сортировки элементов используется функция sortFunc элементы для которых эта функция вернула меньшее значение будут находиться в начале

// возвращает значение 0 если свойство expiration < 10 иначе возвращается свойство price
$scope.sortFunc = function (value) {
    return value.expiration < 10 ? 0 : value.price;
};
<tr ng-repeat="item in items | orderBy:sortFunc">
    <td>{{item.name}}</td>

Таким образом все элементы, у которых свойство expiration < 10 будут выведены вначале списка.

3. Фильтрация с помощью нескольких условий.

Передается массив – в качестве первого параметра передается функция (sortFunc), по которой происходит сортировка, а далее происходит сорти-ровка по параметру (price).

<!-- Сортировка с помощью функции sortFunc после сортировка по цене по возрастанию-->
<tr ng-repeat="item in items | orderBy:[sortFunc, 'price']">
<td>{{item.name}}</td>

    Цепочка фильтров

То есть мы можем для отфильтрованных данных применить фильтр.

<!-- Директива использует цепочку фильтров -->
<tr ng-repeat="item in items | orderBy:[sortFunc, '+price'] | limitTo : 3">

    Пользовательские фильтры

module.filter() – метод для создания пользовательских фильтров.

angular.module("moduleName")
.filter("filterName", function(){  // создание фильтра filterName
    return function(value, parameters) {

    // здесь определяем логику фильтра. Возвращенное значение будет отображено в представлении.
    // !!!
    // value - данные для которых применяется фильтр
    // parameters - параметры передаваемые фильтру
    // !!!

    return value;
}
});

Пример:

$scope.items = [
    { name: "Item 1", price: 10.9, category: "Category 1", count: 10, tax: 1.12, expiration: 10 },
    { name: "Item 2", price: 1.1, category: "Category 1", count: 8, tax: 0.55, expiration: 12 },
    ...
];
// При вызове метода module без второго аргумента (массива зависимостей) AngularJS произво-дит поиск
// уже существующего модуля для которого создается фильтр.

// Для создания фильтра используется функция filter.
// Первый параметр - имя фильтра
// Второй параметр - функция задача которой вернуть "worker function". Worker function будет использоваться для фильтрации значения.
angular.module("exampleApp")
.filter("changeCase", function () {
    // value - данные для которых применяется фильтр (в нашем случае это item.category )
    // toUpper - аргумент передаваемый фильтру
    return function (value, toUpper) {
        // проверка переменной value на наличие строки
        if (angular.isString(value)) {
            var processedValue = toUpper ? value.toUpperCase() : value.toLowerCase();
            return processedValue;
        } else {
            return value;
        }
    };
});
<!-- Использование пользовательского фильтра. Этот фильтр в качестве параметра принимает булево значение. -->
<td>{{item.category | changeCase : false}}</td>

Пример для обработки коллекций:

<!-- Пользовательский фильтр для обработки коллекций -->
<tr ng-repeat="item in items | skipItems : 3">
// данный фильтр пропускает первые три элемента

angular.module("exampleApp")
.filter("skipItems", function () {

    return function (value, count) {
        // isArray - проверка, что переменная является массивом
        // isNumber - проверка, что переменная является числом
        if (angular.isArray(value) && angular.isNumber(count)) {
            if (count > value.length || count < 1) {
                return value;
            } else {
                return value.slice(count);
            }
        } else {
            return value;
        }
    }

});

    Системный сервис $filter

В следующем пример мы увидим, как можно пользоваться уже существующими фильтрами.

Существует специальный сервис ($filter), который предоставляет логику фильтров зарегистрированных в модуле. С помощью $filter мы можем получить доступ как к системным фильтрам, так и к своим фильтрам зарегистрированным в модуле.

angular.module("exampleApp")
.filter("skipItems", function () {
    return function (value, count) {
        if (angular.isArray(value) && angular.isNumber(count)) {
            if (count > value.length || count < 1) {
                return value;
            } else {
                return value.slice(count);
            }
        } else {
            return value;
        }
    }
})
 // сервис $filter применяется для использования других фильтров
.filter("take", function ($filter) {
    return function (data, from, count) {
        var arr = $filter("skipItems")(data, from);
        return $filter("limitTo")(arr, count); // limitTo – стандартный системный фильтр
    }
});

<!-- Фильтр take извлекает из коллекции 3 элемента начиная с 3 -->
<tr ng-repeat="item in items | take : 2 : 3">
....

Item 3
Item 4
Item 5

IV Директивы

  • Создание пользовательской директивы – link function
  • Использование jqLite (урезанная версия библиотеки jQuery)
  • Создание пользовательской директивы – Directive Definition Object
  • Работа со scope

Директива – маркер на элементе DOM дерева, который говорит AngularJS компилятору о том, что к элементу необходимо привязать особое поведение или трансформировать элемент и его дочерние элементы.

AngularJS предоставляет ряд стандартных директив ngModel, ngBind, ngRepeat и т.д

Компиляция в AngularJS – привязка обработчиков событий (event listeners) к HTML элементам для того чтобы сделать их интерактивными.

    Создание пользовательской директивы

1-й пример Создание пользовательской директивы:

module.directive("directiveName", function(){
    // в разметке будет использоваться directive-name
    return function(scope, element, attributes) {
        // 1-й аргумент – Scope директивы
        // 2-й – HTML элемент директивы (уже является jqLite объектом)
        // 3-й –  атрибуты HTML элемента
        // в терминологии angularjs данная возвращаемая функция называется
        // Link function
}
});
angular.module("exampleModel", [])
// Создание пользовательской директивы происходит при помощи метода directive
// 1 аргумент - имя директивы
// 2 аргумент - фабричная функция для создания директивы
.directive("orderedList", function () {
    // link function - производит связывание директивы и HTML разметки.
    // Данная функция вызывается каждый раз когда новый экземпляр директивы со-здается AngularJS
    // Аргументы функции (не предоставляются через Dependecy Injection):
    // 1 - scope к которому применяется директива
    // 2 - HTML элемент, к которому применяется директива
    // 3 - атрибуты HTML элемента
    return function (scope, element, attributes) {

        var attrValue = attributes["orderedList"];  // получаем значение атрибута (ordered-list="items")
        var data = scope[attrValue];                // получаем значение из scope (scope[items])

        if (angular.isArray(data)) {
            // angular.element – метод JQlite преобразовывает строку или DOM элемент в jQuery объект
            var e = angular.element("<ol>");
            element.append(e); // добавляем ol к элементу для которого установлена ди-ректива
            for (var i = 0; i < data.length; i++) {
                // Создаем li элементы заполняя их данными из массива data
                e.append(angular.element('<li>').text(data[i].name));
            }
        }
    }
})
.controller("defaultCtrl", function ($scope) {
    $scope.items = [
        { name: "Table", price: 44.10 },
        { name: "Chair", price: 21.20 }
    ];
});
<div ordered-list="items"></div>

    Создание более универсальный пользовательской директивы

2-й пример более универсальный (сделаем пример выше более универсальным в плане: при помощи display-property укажем свойство модели, которое должно использоваться для отображения текста пользователю):

angular.module("exampleApp", [])
.directive("orderedList", function () {
    return function (scope, element, attributes) {
        var data = scope[attributes["orderedList"]];
        var prop = attributes["displayProperty"];

        if (angular.isArray(data)) {
            var elem = angular.element("<ol>");
            element.append(elem);
            for (var i = 0; i < data.length; i++) {
                // получение значение для отображения из свойства, указанного в атрибуте customProperty
                elem.append(angular.element('<li>').text(data[i][prop]));
            }
        }
    }
})
<!-- display-property определяет свойство модели,
которое должно использоваться для отобра-жения текста пользователю -->
<div ordered-list="items" display-property="price"></div>

<!-- AngularJS автоматически убирает приставу data-* -->
<!--<div data-ordered-list="items" data-display-property="name"></div>-->

    Директива использует выражение

3-й пример использует выражение: задача не просто взять значение display-property, а применяли это значение вместе с условием фильтра

       angular.module("exampleApp", [])
            .directive("orderedList", function () {
                return function (scope, element, attributes) {

                    var data = scope[attributes["orderedList"]];
                    var expression = attributes["displayProperty"]; // "price | currency"

                    if (angular.isArray(data)) {
                        var e = angular.element("<ol>");
                        element.append(e);
                        for (var i = 0; i < data.length; i++) {

// scope.$eval([expression], [locals]) выполняет выражение на текущем scope
// [expression] выражение которое нужно выполнить
// [locals] объект который содержит переменные для переопределения значений в scope
// или другими словами – это объект, который является источником данных

                            e.append(angular.element('<li>').text(scope.$eval(expression, data[i])));
                            //e.append(angular.element('<li>').text(data[i][expression]));
                        }
                    }
                }
            })
<div ordered-list="items" display-property="price | currency"></div>

    Директива, которая реагирует на изменения в scope

4-й пример – создание директив, которые будут реагировать на изменения в scope. Изменения будут отслеживаться при помощи метода watch.

angular.module("exampleApp", [])
    .directive("orderedList", function () {
        return function (scope, element, attributes) {

            var data = scope[attributes["orderedList"]];
            var expression = attributes["displayProperty"];

            if (angular.isArray(data)) {

                var e = angular.element("<ul>");
                element.append(e);

                for (var i = 0; i < data.length; i++) {

                    // немедленно-запускаемая функция, которая выступает в роли области видимости
                    // и избежать замыкание на переменной i;
                    (function () {
                        var item = angular.element('<li>');
                        e.append(item);
                        var index = i;

                        var watcherFunction = function (watchScope) {
                            return watchScope.$eval(expression, data[index]);
                        }

                        // scope.$watch - Устанавливаем отслеживание изменений в scope на
                        // основе watcherFunction, которая возвращает значение с учетом фильтров.
                        // Функция будет вызываться каждый раз, когда меняется scope.
                        // Если функция возвращает новое значение, то элемент отображающий это значение обновляется.
                        scope.$watch(watcherFunction, function (newValue, oldValue) {
                            item.text(newValue);
                        });
                    }());
                }
            }
        }
    })
.controller("defaultCtrl", function ($scope) {

    $scope.items = [
        { name: "Table", price: 44.10 },
        { name: "Chair", price: 21.20 },
        { name: "Pillow", price: 12.20 },
        { name: "Picture", price: 112.70 },
        { name: "Lamp", price: 28.31 }
    ];

    $scope.changePrices = function () {
        for (var i = 0; i < $scope.items.length; i++) {
            $scope.items[i].price++;
        }
    }
});
<body ng-controller="defaultCtrl">
    <div class="panel panel-primary">

        <div class="panel-heading">
            <h3>Items</h3>
        </div>

        <div class="panel-body">
            <button class="btn btn-success" ng-click="changePrices()">
                <h4>Change Prices</h4>
            </button>
        </div>

        <!-- нам необходимо отслеживать изменения в свойстве ordered-list -->
        <div class="panel-body h3">
            <div ordered-list="items" display-property="price | currency"></div>
        </div>
    </div>
</body>

    jqLite

В поставку библиотеки angularjs входит урезанная версия библиотеки jQuery – jqLite и которая используется для создания, манипулирования и управления элементами HTML разметки.

При создании директив в возвращаемой функции 2-й параметр HTML элемент директивы, который уже является jqLite объектом.

angular.module("exampleApp", [])
.directive("customDirective", function () {
    return function (scope, element, attributes) {
        // element - jqLite объект
        var elements = element.children(); // получение всех дочерних элементов

        for (var i = 0; i < elements.length; i++) {
            // если элемент содержит текст Red сделать его красным цветом
            if (elements.eq(i).text() == "Red") {
                elements.eq(i).css("color", "red");
            }
        }
    }
});
<div class="panel panel-primary">
    <div class="panel-heading">
        <h3>Colors</h3>
    </div>
    <div class="panel-body h3">
        <ul custom-directive>
            <li>Black</li>
            <li>Red</li>
            <li>Yellow</li>
            <li>Navy</li>
            <li>Green</li>
        </ul>
    </div>
</div>

    jqLite методы для навигации по DOM

  • children() - возвращает jqLite объект, который содержит набор дочерних элементов
  • eq(index) - возвращает jqLite объект, который содержит элемент по опреде-ленному индексу из коллекции элементов
  • find(tag) - находит все элементы с указанным именинам тега
  • next() - возвращает следующий элемент (который находится на том же уровне, что и тот, для которого вызывается метод)
  • parent() - возвращает родительский элемент

    jqLite методы для манипулированиями стилями

  • addClass(name) - добавляет CSS класс
  • attr(name)/attr(name, value) - возвращает значение для атрибута первого элемента из jqLite объекта или устанавливает значение для всех элементов
  • css(name)/css(name, value) - возвращает значение css свойства первого элемента из jqLite объекта или устанавливает значение css свойства для всех элементов
  • hasClass(name) - возвращает true если любой из элементов jqLite объекта принадлежит к указанному классу
  • prop(name)/prop(name, value) - возвращает значение свойства первого элемента из jqLite объекта или устанавливает значение свойства для всех элементов
  • removeAttr(name) - удаляет атрибут из всех элементов jqLite объекта
  • removeClass(name) - удаляет элементы jqLite объекта из определенного css класса
  • text()/text(value) - возвращает конкатенированный текст всех элементов jqLite объекта или устанавливает значение для всех элементов jqLite объекта
  • toggleClass(name) - выполняет инверсию css класса для элементов jqLite объекта. Те элементы который были включены в класс - удаляются, а те ко-торые не были включены - добавляются.
  • val()/val(value) - возвращает атрибут value для первого элемента из jqLite объекта или устанавливает атрибут value для всех элементов jqLite объекта.

Пример – удаляем все элементы b из разметки.

.directive("removeB", function () {
    return function (scope, element, attributes) {
        var elements = element.find("b");
        for (var i = 0; i < elements.length; i++) {
            elements.eq(i).remove(); // удаление элементов b
        }
    }
})
<div remove-b>
    <b>Tincidunt</b> integer <b>eu</b> augue augue nunc elit dolor,
    luctus placerat scelerisque euismod, <b>iaculis</b> eu lacus nunc mi elit,
    vehicula ut laoreet ac, aliquam sit amet <b>justo</b> nunc tempor, metus vel.
</div>

    jqLite позволяет нам обрабатывать события:

  • on(events, handler) регистрирует обработчик для одного или нескольких событий
  • off(events, handler) удаляет зарегистрированный обработчик
  • triggerHandler(event) запускает все обработчики для события указанного в аргументах.

Пример - задача директивы сгенерировать ul с li и повесить обработчик на кнопку(при клике на кнопку текст элементов li окрашивается в красный цвет):

angular.module("exampleApp", [])
    .directive("customDirective", function () {
        return function (scope, element, attributes) {

            var elem = angular.element("<ul>");
            element.prepend(elem); // добавляем ul перед элементами, размещенными в element

            for (var i = 0; i < scope.colors.length; i++) {
                elem.append(angular.element("<li>")
                    .append(angular.element("<span>")
                    .text(scope.colors[i])));
            }

            var button = element.find("button");
            button.on("click", function (e) {
                element.find("li").toggleClass("red");
            });
        }
    })

.controller("defaultCtrl", function ($scope) {
    $scope.colors = ["Red", "Black", "Navy", "Green", "Yellow"];
})
<div custom-directive class="h4">
    <button class="btn btn-danger">Change color</button>
</div>

    Использование jQuery

Необходимо подключить jQuery ДО angularjs. При загрузке angular проверяет наличие jQuery если библиотека есть то jqLite не используется.

    Подход для создания директив Derective Definition Object

Данный подход дает нам больший контроль над создаваемой директивой.

Функция возвращает объект, который определяет поведение директивы – такой подход называют Derective Definition Object. Благодаря свойствам этого объекта angularjs понимает как работает директива.

    Основные свойства Directive Definition Object:

  • link определяет ссылочную функцию для директивы (то есть эта такая же функция как в примерах выше, return function (scope, element, attributes) { … } )
  • replace определяет замещать ли содержимое элемента к которому была применена директива сгенерированным контентом
  • restrict определяет ограничения на применение директивы
  • scope создает новый scope или isolated scope для директивы
  • template определяет шаблон который будет вставлен в HTML разметку
  • templateUrl определяет адрес внешнего шаблона для создания HTML разметки

    Различные варианты для отображения директивы в интерфейсе (restrict)

restrict определяет способ применения директивы (restrict: "EACM"):

  • E - как элемент
    (<ordered-list list-source="items" property="price | currency" />)
  • A - как атрибут
    (<div ordered-list="items" property="price | currency"></div>)
  • C - как класс
    (<div class="ordered-list: items" property="price | currency" />; здесь items это значение передаваемое директиве)
  • M - как комментарий
    (<!-- directive: ordered-list items--> ; здесь items это зна-чение передаваемое директиве)

    Пример использование свойства template

angular.module("exampleApp", [])
    .directive("orderedList", function () {
        return {
            link: function (scope, element, attributes) {
                scope.data = scope[attributes["orderedList"]];
            },
            restrict: "A",
            // шаблон для создания разметки
            template: "<ol><li ng-repeat='item in data'>{{item.name}}</li></ol>"
            // templateUrl: "template.html"
        }
    })
.controller("defaultCtrl", function ($scope) {
    $scope.items = [
        { name: "Table", price: 44.10 },
        { name: "Chair", price: 21.20 },
        { name: "Pillow", price: 12.20 },
        { name: "Picture", price: 112.70 },
        { name: "Lamp", price: 28.31 }
    ];
});
<div ordered-list="items">
</div>

Свойство templateUrl может быть функцией, которая отражает template в зависимости от условия, например:

templateUrl: function (element, attributes) {
    // выбор внешнего шаблона на основе атрибута template
    return attributes["template"] == "table" ? "tableTemplate.html" : "template.html";
}
<div  ordered-list="items" template="table">
    This is where the list will go
</div>

    Свойство replace

  • true - элемент, которому будет присвоена директива будет заменен разметкой сгенерированной по шаблону
  • false - в элемент, к которому присвоена директива, будет добавлена разметка

    Как работает scope в директивах

Каждый раз применяя директиву создается ее экземпляр.

    Одинаковый scope у директив

Чтобы директивы обладали одинаковым поведением или имели доступ к одним и тем же данным необходимо разместить их в одном контроллере.

var app = angular.module("exampleApp", []);

// Директива scopeExample добавляет в разметку input с двунаправленной привязкой свой-ству data
// В разметке данного примера будет создано два экземпляра директивы, но так обе дирек-тивы принадлежат одному контроллеру
// они будут работать с одним и тем же свойством data
app.directive("scopeExample", function () {
    return {
        template: "<div class='panel-body'>Type something: <input ng-model='data' /></div>",
        scope: true
    }
})

app.controller("defaultCtrl", function ($scope) {
});
<div ng-controller="defaultCtrl" class="panel panel-primary">
    <div class="panel-body" scope-example></div>
    <div class="panel-body" scope-example></div>
</div>

    Разный scope директив

Для того чтобы каждая директива имела свой scope при создании директивы в Directive Definition object установите scope: true. Таким образом binding будет связан со scope директив, а не со scope контроллера. Наследование scope показано на изображении.

var app = angular.module("exampleApp", []);

// Директива scopeExample добавляет в разметку input с двунаправленной привязкой свой-ству data
// В разметке данного примера будет создано два экземпляра директивы, но так обе дирек-тивы принадлежат одному контроллеру
// они будут работать с одним и тем же свойством data
app.directive("scopeExample", function () {
    return {
        template: "<div class='panel-body'>Type something: <input ng-model='data' /></div>",
        scope: true
    }
})
app.controller("defaultCtrl", function ($scope) {
});
<div ng-controller="defaultCtrl" class="panel panel-primary">
    <div class="panel-body" scope-example></div>
    <div class="panel-body" scope-example></div>

    Изолированный scope в директивах

В этом случае связей между scope контроллера и scope директив нет. То есть scope директив не участвует в наследовании. Для этого достаточно для свойства scope указать { }.

angular.module("exampleApp", [])
.directive("scopeExample", function () {
    return {
        template: function () {
            return angular.element(document.querySelector("#customTemplate")).html();
        },
        scope: {} // В данном случае директива создается с изолированным scope - данный scope не участвует в наследовании.
    }
})
.controller("defaultCtrl", function ($scope) {
    $scope.data = { caption: "Caption" };
});
<div ng-controller="defaultCtrl" class="panel panel-primary">
    <div class="panel-body" scope-example></div>
    <div class="panel-body" scope-example></div>
</div>

    Привязки scope директив к элементам разметки (связка между DOM элементами и изолированным scope в директивах), @, =, &

    @ однонаправленная привязка

@ - однонаправленная привязка к атрибутам DOM элемента, например,

<script type="text/ng-template" id="customTemplate">
    <div class="panel panel-success">
        <!-- 1) Определение привязки на свойство property на isolated scope -->
        <div class="panel-heading"><p>This is {{property}}</p></div>
    </div>
</script>
angular.module("exampleApp", [])
.directive("scopeExample", function () {
    return {
        template: function () {
            return angular.element(document.querySelector("#customTemplate")).html();
        },
        scope: {
            // 2) с помощью @ указываем one-way binding на атрибут propname
            // Данный механизм упрощает взаимодействие директивы и контроллера, позволяя получить нужные данные из атрибута DOM элемента
            property: "@propname"
        }
    }
})
.controller("defaultCtrl", function ($scope) {
    $scope.data = { color: "Green" };
});
<div ng-controller="defaultCtrl" class="panel panel-warning">
    <div class="panel-heading">
        Binding source: <input ng-model="data.color" />
    </div>
    <!-- 3) в качестве значения для атрибута propname используется значение свойства data.color -->
    <div class="panel-body" scope-example propname="{{data.color}}"></div>
</div>

    = двунаправленная привязка

= двунаправленная привязка

angular.module("exampleApp", [])
.directive("scopeExample", function () {
    return {
        template: function () {
            return angular.element(document.querySelector("#customTemplate")).html();
        },
        scope: {
            //2) = определяет двунаправленную привязку между isolated scope и scope контролле-ра
            property: "=source"
        }
    }
})
.controller("defaultCtrl", function ($scope) {
    $scope.data = { value: "Some Value" };
});
<div ng-controller="defaultCtrl" class="panel panel-warning">
    <div class="panel-heading">
        Type something: <input ng-model="data.value" />
    </div>
    <!--3) при изменении модели () будет изменятся значение свойства data.value-->
    <div class="panel-body" scope-example source="data.value"></div>
</div>

Еще один пример двунаправленной привязки:

// Создание нового модуля exampleApp
var module = angular.module("exampleApp", []);

// контроллер defaultCtrl
module.controller("defaultCtrl", function ($scope) {

    $scope.buttons = {
        names: ["Button #1", "Button #2", "Button #3"],
        totalClicks: 0
    };

    $scope.$watch('buttons.totalClicks', function (newVal) {
        console.log("Total click count: " + newVal);
    });
});
// Директива привязывает обработчики на событие click для элементо button
module.directive("triButton", function () {
    return {
        // scope использует ДВУНАПРАВЛЕННУЮ ПРИЯЗКУ для того чтобы получить доступ
        // к атрибуту "=counter" того DOM элемента, к которому привязан triButton
        scope: { counter: "=counter" },
        link: function (scope, element, attrs) {
            element.on("click", function (event) {
                console.log("Button click: " + event.target.innerText);
                scope.$apply(function () {
                    scope.counter++;
                });
            });
        }
    }
});
<body ng-controller="defaultCtrl">
<div class="well">

<!--
    таким образом значение buttons.totalClicks будет попадать в свойство counter (смотри директиву: scope: { counter: "=counter" })
    таким образом если мы будем менять свойство counter, то будет меняться и totalClicks
-->
    <div class="btn-group" tri-button counter="buttons.totalClicks" source="button.names">
        <button class="btn btn-default" ng-repeat="name in buttons.names">
            {{name}}
        </button>
    </div>
    <h5>Total clicks: {{buttons.totalClicks}}</h5>
</div>

    Привязка изолированного scope к callback функции (&)

getType: "&value" // & значение атрибута будет привязано к функции getType

& означает, что &value хранит не просто значение, а выражение (функцию), которую необходимо запустить

<script type="text/ng-template" id="template">
    <div class="panel panel-success">
        <div class="panel-heading"><p>What is {{fruit}}? {{getType()}}</p></div>
    </div>
</script>
angular.module("exampleApp", [])
.directive("scopeExample", function () {
    return {
        template: function () {
            return angular.element(document.querySelector("#template")).html();
        },
        scope: {
            fruit: "=fruitName",
            getType: "&value" // & значение атрибута будет привязано к функции getType
        }
    }
})
.controller("defaultCtrl", function ($scope) {
    $scope.data = { fruit: "Apple" };
    $scope.getKind = function (value) {
        return value == "Apple" ? " It's a Fruit" : " I don't know :(";
    }
});
<div ng-controller="defaultCtrl" class="panel panel-warning">
    <div class="panel-heading">
        Binding Source: <input ng-model="data.fruit" />
    </div>
    <!-- value - содержит выражение, которое вызывает функцию getKind -->
    <div class="panel-body" scope-example value="getKind(data.fruit)" fruit-name="data.fruit"></div>
</div>

V Сервисы

Сервисы – специальные объекты, которые передаются компонентам приложения с помощью внедрения зависимости.

Lazily instantiated – экземпляр сервиса создается только тогда, когда этого требует другой компонент системы.

Singletons – все компоненты взаимодействуют с одним и тем же экземпляром сервиса.

Системные сервисы используют $ как первый символ в имени, например, $http, $parse, $filter

В сервисы помещают ту функциональность (объект), которая не вписывается в один из элементов MVC шаблона – логирование, кэширование, работа с HTTP.

    Способы создания сервисов

  • module.factory() – метод возвращает объект сервиса.
  • module.service() – создается сервис с использованием ключевого слова new.
  • module.provider() – создание объекта сервиса с возможностью его конфигурации перед использованием.

    module.factory()

Мы, для примера, создадим сервис, который будет выполнять логирование. Когда вы хотите, чтобы какой-либо компонент angularjs начал использовать сервис нам необходимо внедрить сервис через внедрение зависимостей в фабричную функция (это можно отнести к контроллерам, директивам, и т.д. например,
.controller("defaultCtrl", function ($scope, logService) { … ).
Предварительно сервис необходимо передать через DI модулю:
angular.module("exampleApp", ["di-rectiveModule", "customServices"])
, так как сервис определен в отдельном модуле customServices.

Файл Index.html:

<script src="directiveModule.js"></script>
<script src="customServices.js"></script>
angular.module("exampleApp", ["directiveModule", "customServices"])

// AngularJS при вызове фабричной функции анализирует названия ее аргументов и производит
// внедрение зависимостей - передает экземпляр сервиса для логирования в параметр с именем logService
.controller("defaultCtrl", function ($scope, logService) {
    $scope.buttons = {
        names: ["Button #1", "Button #2", "Button #3"],
        totalClicks: 0
    };

    $scope.$watch('buttons.totalClicks', function (newVal) {
        // использование сервса
        logService.log("Total click count: " + newVal);
    });

});
<body ng-controller="defaultCtrl">
    <div class="well">
        <div class="btn-group" tri-button counter="buttons.totalClicks" source="button.names">
            <button class="btn btn-default" ng-repeat="name in buttons.names">
                {{name}}
            </button>
        </div>
        <h5>Total clicks: {{buttons.totalClicks}}</h5>
    </div>
</body>

<!--
Total clicks: 2

вывод в консоли:
LOG #0, message = Total click count: 0
LOG #1, message = Button click: undefined
LOG #2, message = Total click count: 1
LOG #3, message = Button click: undefined
LOG #4, message = Total click count: 2
-->

Файл directiveModule.js

// данный модуль использует модуль customServices, в котором определен сервис для логирования
angular.module("directiveModule", ["customServices"])
// AngularJS при вызове фабричной функции анализирует названия ее аргументов и производит
// внедрение зависимостей - передает экземпляр сервиса для логирования в параметр с именем logService
.directive("triButton", function (logService) {
    return {
        scope: { counter: "=counter" },
        link: function (scope, element, attrs) {
            element.on("click", function (event) {
                logService.log("Button click: " + event.target.innerText);
                scope.$apply(function () {
                    scope.counter++;
                });
            });
        }
    }
});

Файл customServices.js

var module = angular.module("customServices", []);
// factory - метод для создания сервисов. В качестве параметров принимает имя сервиса и фабричную функцию для его создания
// Эземпляр, создаваемый фабричной функцией, будет одним на все приложение (singleton)
module.factory("logService", function () {
    var messageCount = 0;
    return {
        log: function (msg) {
            console.log("LOG #" + messageCount++ + ", message = " + msg);
        }
    };
});

Сервис является синглтоном, а поэтому один раз создавшись используется тот же самый сервис. Это можно отследить по тому как увеличивается пе-ременная messageCount: значение меняется и в контроллере (итоговая сумма) и при кликах на кнопку - директива (вывод в консоли).

    module.service()

Используя этот метод мы будем создавать сервисы через функции-конструкторы, а не просто как настроенный объект, который возвращается worker функцией. Это может понадобиться, например, если вы хотите реализовать наследование через прототипы.

Давайте представим, что у нас есть базовый объект, который умеет выполнять логирование и нам необходимо сделать два производных объекта, которые смогут выводить сообщение об ошибках (error) и debug.

customServices.js

var module = angular.module("customServices", []);

// создаем функцию-конструктор
var baseLogger = function () {
    this.messageCount = 0;
    this.log = function (msg) {
        console.log("Type " + this.msgType + " LOG #" + this.messageCount++ + ", message = " + msg);
    }
}

// мы хотим чтобы debugLogger и errorLogger были наследниками от baseLogger
var debugLogger = function () { }
debugLogger.prototype = new baseLogger();
debugLogger.prototype.msgType = "Debug";

var errorLogger = function () { }
errorLogger.prototype = new baseLogger();
errorLogger.prototype.msgType = "Error";

// service - метод для создания сервисов. При использовании данного метода фабричная функция работает как конструктор
// (то есть вызов service говорит нам, что функция вызывается через клю-чевое слово new).
// Для создания сервисов AngularJS будет запускать эту функцию с использованием ключевого слова new
// ниже мы говорим, что сервис называется logService и он должен создаваться с помощью кон-структора debugLogger
// в данном случае сервис также создан как синглтон
module.service("logService", debugLogger)
      .service("errorService", errorLogger);

Index.html

angular.module("exampleApp", ["directiveModule", "customServices"])
// AngularJS при вызове фабричной функции анализирует названия ее аргументов и производит
// внедрение зависимостей - передает экземпляр сервиса для логирования в параметр с именем logService
.controller("defaultCtrl", function ($scope, logService, errorService) {
    $scope.buttons = {
        names: ["Button #1", "Button #2", "Button #3"],
        totalClicks: 0
    };

    $scope.$watch('buttons.totalClicks', function (newVal) {
        if (newVal < 5) {
            // использование сервиса logService
            logService.log("Total click count: " + newVal);
        }
        else {
// при > 5 кликов начнет срабатывать errorService
            errorService.log("Total click count: " + newVal);
        }

    });

});
<body ng-controller="defaultCtrl">
    <div class="well">
        <div class="btn-group" tri-button counter="buttons.totalClicks" source="button.names">
            <button class="btn btn-default" ng-repeat="name in buttons.names">
                {{name}}
            </button>
        </div>
        <h5>Total clicks: {{buttons.totalClicks}}</h5>
    </div>
</body>

    module.provider()

module.provider()

Этот подход широко используется в angularjs, так как позволяет сделать сервисы конфигурируемыми. То есть при помощи метода provider наш создаваемый сервис будет иметь точки входа для конфигурации.

Index.html

angular.module("exampleApp", ["directiveModule", "customServices"])
// .config дополнительная настройка сервисов созданных с помощью функции module.provider
// Для того чтобы получиь провайдер необходимого сервиса необходимо указать в качестве параметра функции аргумент
// с именем [имя сервиса]Provider

// когда мы создали модуль мы указали, что хотим выполнить конфигурацию,
// мы хотим конфи-гурировать logServiceProvider, то есть тот объект который предоставляет нам сервис,
// т. е. мы настраиваем не сам сервис или его экземпляр, а настраиваем дополнительный объект,
// который управляет предоставлением сервиса
// (debugEnabled – вкл (указывает на: нужно работать нашему сервису или нет);
// messageCounterEnabled – выкл (вкл или выкл счетчик))

.config(function (logServiceProvider) {
    logServiceProvider.debugEnabled(true).messageCounterEnabled(false);
})
.controller("defaultCtrl", function ($scope, logService) {
    $scope.buttons = {
        names: ["Button #1", "Button #2", "Button #3"],
        totalClicks: 0
    };

    $scope.$watch('buttons.totalClicks', function (newVal) {
        // использование сервса
        logService.log("Total click count: " + newVal);
    });

});
<body ng-controller="defaultCtrl">
    <div class="well">
        <div class="btn-group" tri-button counter="buttons.totalClicks" source="button.names">
            <button class="btn btn-default" ng-repeat="name in buttons.names">
                {{name}}
            </button>
        </div>
        <h5>Total clicks: {{buttons.totalClicks}}</h5>
    </div>
</body>

customServices.js

var module = angular.module("customServices", []);

// provider - метод для создания сервисов. В качестве аргумента принимает имя сервиса и фаб-ричную функцию.
// Фабричная функция должна вернуть Provider Object, который содержит метод $get и свой соб-ственные свойства.
// Метод $get должен возвращать сервис.
// return {…} возвращается сервис-провайдер, а не сам сервис, на этом провайдере будет вызван
// метод $get -  с помощью этого метода мы получаем доступ к функциональности сервиса,
// а, например, свойства messageCounterEnabled и debugEnabled относятся к конфигурации сервиса,
// который будет создан в будущем
// Подход с использование provider функции позволяет выполнять дополнительную конфигура-цию сервисов (см. 001_index.html)

module.provider("logService", function () {

    var counter = true;
    var debug = true;

    return {
        messageCounterEnabled: function (setting) {
            if (angular.isDefined(setting)) {
                counter = setting;
                return this;
            } else {
                return counter;
            }
        },
        debugEnabled: function (setting) {
            if (angular.isDefined(setting)) {
                debug = setting;
                return this;
            } else {
                return debug;
            }
        },
        $get: function () {
            return {
                messageCount: 0,
                log: function (msg) {
                    if (debug) {
                        console.log("(LOG"
                            + (counter ? " + " + this.messageCount++ + ") " : ") ")
                            + msg);
                    }
                }
            };
        }
    }
});

    Системные сервисы

  • $anchorScroll Используется для скролла окна браузера до определенного якоря
  • $animate Используется для анимации
  • $compile Обрабатывает фрагмент разметки для создания функции, которая может генерировать контент
  • $controller Является оберткой над $injector сервисом, который инстанциируют контроллеры
  • $document Представляет jqLite объект, который содержит объект window.document
  • $exceptionHandler Используется для обработки ошибок, которые возникают в приложении
  • $filter Используется для создания фильтров
  • $http Используется для создания и управления AJAX запросами
  • $injector Создает экземпляр AngularJS компонента
  • $interpolate Обрабатывает строку, которая содержит выражение привязки для создания функции, которая может использоватся для генерации контента
  • $interval Обертка над window.setInterval функцией
  • $location Обертка над объектом location
  • $log Обертка над global console объектом
  • $parse Конвертирует AngularJS выражения в функцию
  • $q Используется для работы с promises
  • $resource Предоставляет возможность работы с RESTfull API
  • $rootElement Предоставляет доступ к корневому элоементу DOM
  • $rootScope Предоставляет доступ к корневому scope
  • $route Используется для работы с путями и множеством view
  • $routeParams Предоставляет информацию о URL
  • $sanitize Заменяет небезопасные HTML символы на безопасные
  • $sce Strict Contextual Escaping. Удаляет опасные элементы и аттрибуты из HTML строки чтобы сделать ее безопасной для отображения
  • $swipe Используется для распознавания гестур
  • $timeout Обертка над window.setTimeout()
  • $window Предоставляет ссылку на DOM объект - window

    Несколько примеров системных сервисов:

    Системный сервис $window

// Для того, чтобы упросить тестирование приложений, в AngularJS придусмотренно ряд сер-висов, которые уменьшают
// зависимость JS кода от внешних объектов. При тестировании разработчику проще изоли-ровать тестируемый код при наличии
// сервисов подобных $window, $document
angular.module("exampleApp", [])
.controller("defaultCtrl", function ($scope, $window) {
    $scope.showHint = function (message) {
        $window.alert(message); // $window - сервис-обертка над глобальным объектом window
    }
});
<body ng-controller="defaultCtrl" class="well">
    <button class="btn btn-primary" ng-click="showHint('Hint!')">Click me!</button>
</body>

    Системный сервис $document

angular.module("exampleApp", [])
.controller("defaultCtrl", function ($scope, $window, $document) {
    // $document - jqLite объект содержащий window.document
    $document.find("button").on("click", function (event) {
        $window.alert(event.target.innerText);
    })
});

    Системные сервисы $interval, $timeout

// $interval и $timeout сервисы-обертки над функциями window.setInterval() и win-dow.setTimeout()
angular.module("exampleApp", [])
.controller("defaultCtrl", function ($scope, $interval, $timeout, $window) {

    $interval(function () {
        $scope.time = new Date().toTimeString();
    }, 1000);

    $scope.testHandler = function () {
        $timeout(function () {
            $window.alert("Hello world");
        }, 2000);
    }
})
<body ng-controller="defaultCtrl" class="well">
    <div class="panel panel-default">
        <h4 class="panel-heading">Time</h4>
        <div class="panel-body">
            <button class="btn btn-default" ng-click="testHandler()">Test</button>
            The time is: {{time}}
        </div>
    </div>
</body>

    Системный сервис $location

$location сервис для получения достпа к текущему адресу страницы.

Данный сервис работает над той частью адреса, который идет после первого символа #

Методы:

  • absUrl() возвращает полный url
  • hash()/hash(target) возвращает или устанавливает hash секцию url
  • host() возвращает имя хоста (mysite.com)
  • path()/path(target) возвращает или устанавливает path секцию url
  • port() возвращает номер порта (по умолчанию 80)
  • protocol() возвращет protocol (http)
  • replace() все изменения в текущем url заменяются записью из истории
  • search()/search(term, param) возвращает или устанавливает search секцию url
  • url()/url(target) возвращает или устанавливает path, query string, hash
  • События:

    • $locationChangeStart перед изменением url, можно препятствовать изменениям url
    • $locationChangeSuccess генерируется после изменения url
    angular.module("exampleApp", [])
    .controller("defaultCtrl", function ($scope, $location) {
    
        $scope.$on("$locationChangeSuccess", function (event, newUrl) {
            $scope.url = newUrl;
        });
    
        $scope.setUrl = function (component) {
            switch (component) {
                case "reset":
                    $location.path("");
                    $location.hash("");
                    $location.search("");
                    break;
                case "path":
                    $location.path("/cities/london");
                    break;
                case "hash":
                    $location.hash("north");
                    break;
                case "search":
                    $location.search("select", "hotels");
                    break;
                case "url":
                    $location.url("/cities/london?select=hotels#north");
                    break;
            }
        }
    })
    
    <body ng-controller="defaultCtrl">
        <div class="panel panel-default">
            <h4 class="panel-heading">URL</h4>
            <div class="panel-body">
                <p>The url is : {{url}}</p>
                <div class="btn btn-group">
                    <button class="btn btn-primary" ng-click="setUrl('reset')">Reset</button>
                    <button class="btn btn-primary" ng-click="setUrl('path')">Path</button>
                    <button class="btn btn-primary" ng-click="setUrl('hash')">Hash</button>
                    <button class="btn btn-primary" ng-click="setUrl('search')">Search</button>
                    <button class="btn btn-primary" ng-click="setUrl('url')">URL</button>
                </div>
            </div>
        </div>
    </body>

        Сервис для навигации (по якорю) на странице $anchorScroll

    angular.module("exampleApp", [])
    //.config(function ($anchorScrollProvider) {
    //    $anchorScrollProvider.disableAutoScrolling();
    //})
    .controller("defaultCtrl", function ($scope, $location, $anchorScroll) {
    
        $scope.itemCount = 50;
    
        $scope.items = [];
    
        for (var i = 0; i < $scope.itemCount; i++) {
            $scope.items[i] = "Item " + i;
        }
    
        $scope.show = function (id) {
            // Для того чтобы начать исопльзовать сервис $anchorScroll его достаточно просто опре-делить как зависимость для
            // контроллера. Экземпляр сервиса начинает мониторить изменения свойства $location.hash и выполняет скрол автоматически
            $location.hash(id);
            // если сервис $anchorScroll сконфигурирован с использованием $anchorScrollProvider.disableAutoScrolling();
            // необходимо явно вызвать $anchorScroll() явно, чтобы скролить страницу
            //$anchorScroll();
        }
    });
    <body ng-controller="defaultCtrl">
        <div class="panel panel-default">
            <h4 class="panel-heading">URL</h4>
    
            <div class="panel-body">
                <p id="top">This is the top</p>
    
                <button class="btn btn-primary" ng-click="show('bottom')">
                    Go to bottom
                </button>
    
                <p>
                    <ul>
                        <li ng-repeat="item in items">{{item}}</li>
                    </ul>
                </p>
                <p id="bottom">This is the bottom</p>
    
                <button class="btn btn-primary" ng-click="show('top')">
                    Go to TOP
                </button>
            </div>
        </div>
    

        $log – сервис для логирования

    angular.module("exampleApp", [])
    .controller("defaultCtrl", function ($scope, $log) {
        $scope.log = function () {
            $log.log("Log message");
            $log.debug("debug message");
            $log.warn("warn message");
            $log.error("error message");
            $log.info("info message");
        }
    });
    <body ng-controller="defaultCtrl" class="well">
        <button class="btn btn-primary" ng-click="log()">LOG!</button>

        Системный сервис $exceptionHandler

    В следующем примере рассмотрим сервис, который мы напрямую не используем, но он часто возникает в приложениях, когда возникают необработанные исключения – это сервис, который позволяет вывести исключения в консоль браузера.

        Сервисы для санитаризации

    Сервисы предназначенные для того чтобы удалять потенциально опасную разметку

    При компиляции выражений AngularJS выполняет замену специальных HTML символов (HTML encode) Это предотвращает внедрение на страницу посторонних сценариев.

    angular.module("exampleApp", [])
    .controller("defaultCtrl", function ($scope) {
        $scope.htmlData = "<p>This is <b onmouseover=alert('Attack!')>dangerous</b> data</p>";
    });
    <body ng-controller="defaultCtrl">
        <div class="well">
            <p><input class="form-control" ng-model="htmlData" /></p>
            <!--
                При компиляции выражений AngularJS выполняет замену специальных HTML символов (HTML encode)
                Это предотвращает внедрение на страницу посторонних сценариев.
            -->
            <!--<p>This is <b onmouseover=alert('Attack!')>dangerous</b> data</p>-->
            <p>{{htmlData}}</p>
        </div>
    </body>

        ngSanitize И ng-bind-html. Автоматическая санитаризация

    Но иногда требуется вывести html-код как есть. Один из самых распространенных способов атаки на веб приложение - это ввод ложных данных, например в поля формы можно ввести javascript сценарий, который может быть выполнен браузером клиента.

    AngularJs содержит несколько сервисов позволяющих повысить безопасность приложения, чаще всего эти сервисы будут использоваться в приложениях, которые позволяют пользователю генерировать html разметку.

    Задача ngSanitize вместе, например, с директивой ng-bind-html удалить потенциально опасную разметку (iframe, script), например, в примере ниже это onmouseover=alert('Attack!').

    <!-- ngSanitize - модуль предоставляющий функциональность для очистки HTML кода от вредо-носного кода -->
    <script src="angular_sanitize.js"></script>
    // В данном примере используется директива ng-bind-html, которая находиться в модуле ngSanitize
    // ng-bind-html (поставляется вместе с sanitize) позволяет выводить на странице HTML раз-метку без выполнения ее кодирования.
    angular.module("exampleApp", ["ngSanitize"])
    .controller("defaultCtrl", function ($scope) {
        $scope.htmlData = "<p>This is <b onmouseover=alert('Attack!')>dangerous</b> data</p>";
    });
    <body ng-controller="defaultCtrl">
        <div class="well">
            <p><input class="form-control" ng-model="htmlData" /></p>
            <!--
                Директива ng-bind-html использует сервис $sanitize, который выполняет дополнительную обработку данных отображаемых на странице
                Директива производит удаление всех небезопасных атрибутов в отображаемой HTML раз-метке. Все встроенные сценарии и стили будут удалены
                чтобы избежать проблем при выполнении кода.
    
                <p>This is <b>dangerous</b> data</p> - разметка, которая будет отображена на странице.
            -->
            <p ng-bind-html="htmlData"></p>
        </div>
    </body>

        Сервис $sanitaze. Санитаризация вручную

    В коде ниже мы записываем результат работы сервиса $sanitaze в переменную. Таким образом мы можем произвести очистку данных (убираем лиш-ний код, который может быть добавлен в разметку), которые пришли извне.

    <script src="angular_sanitize.js"></script>
    angular.module("exampleApp", ["ngSanitize"])
    .controller("defaultCtrl", function ($scope, $sanitize) {
    
        $scope.dangerousData = "<p>This is <b onmouseover=alert('Attack!')>dangerous</b> da-ta</p>";
    
        $scope.$watch("dangerousData", function (newValue) {
            $scope.htmlData = $sanitize(newValue); // удаление проблемной разметки из переменной newValue
        })
    });
    <body ng-controller="defaultCtrl">
        <div class="well">
            <p><input class="form-control" ng-model="dangerousData" /></p>
            <p ng-bind="htmlData"></p>
        </div>
    </body>

        $sce. Отображаем данные без предварительных проверок и очистки

    Сперва необходимо добавить дополнительный сервис $sce. И передаем значение методу trustAsHtml сервиса $sce. Таким образом скрипт в примере ниже сработает.

    angular.module("exampleApp", ["ngSanitize"])
    .controller("defaultCtrl", function ($scope, $sanitize, $sce) {
    
        $scope.html = "<p>This is <b onmouseover=alert('Attack!')>dangerous</b> data</p>";
    
        $scope.$watch("html", function (newValue) {
            $scope.trustedContent = $sce.trustAsHtml(newValue); // значение будет отображаться в разметке без изменений
        })
    });
    <body ng-controller="defaultCtrl">
        <div class="well">
            <p><input class="form-control" ng-model="html" /></p>
            <p ng-bind-html="trustedContent"></p> <!--обработчик onmouseover не будет удален-->
        </div>
    </body>

        Рассмотрим три сервиса angulargs, который активно используются для компиляции VIEW ($parse, $interpolate, $compile)

        $parse

    $parse - конвертирует выражение angularJS в функцию, которую нужно вы-полнить и получить результат работы. Например, в свойстве создаем выражение:
    $scope.expr = "name | uppercase";
    Далее "бросаем" в сервис $parse выражение:
    var fn = $parse($scope.expr);
    и получаем функцию. Далее запускаем функцию,
    $scope.result = fn(model);
    а в качестве значение передаем model, так где-то необходимо взять свойство name.

    // $parse - конвертирует выражение angularJS в функцию.
    
    angular.module("exampleApp", []).controller("defaultCtrl", function ($scope, $parse) {
    
        $scope.expr = "name | uppercase";
        var model = { name: "Ivan", age: 20 };
    
        $scope.parseExpression = function () {
            var fn = $parse($scope.expr);    //     $scope.expr = "name | uppercase";
            $scope.result = fn(model);
        }
    
    });
    <body ng-controller="defaultCtrl">
        <p style="font-style:italic">
            <b>Samples</b>
            <ul>
                <li>name | uppercase</li>
                <li>age + 1</li>
                <li>name + " " + (age | number)</li>
            </ul>
        </p>
        <input type="text" ng-model="expr" />
        <button ng-click="parseExpression()">$parse</button>
        <p>{{result}}</p>
    </body>

        $interpolate

    $interpolate - компилирует строку с разметкой в функцию. Этот сервис ис-польуется для привязки даных.

    // $interpolate - компилирует строку с разметкой в функцию. Этот сервис испольуется для привязки даных.
    
    angular.module("exampleApp", []).controller("defaultCtrl", function ($scope, $interpolate) {
    
        var expr = "My name is {{name}}. I am {{age}} years old.";
        var model = { name: "Ivan", age: 20 };
    
        $scope.interpolateExpression = function () {
            var fn = $interpolate(expr);
            $scope.result = fn(model);
        }
    
    });
    <body ng-controller="defaultCtrl">
        <button ng-click="interpolateExpression()">$interpolate</button>
        <p>{{result}}</p>
       <!—Результат:  My name is Ivan. I am 20 years old.  -->
    </body>
    

        $compile

    В своей работе использует два предыдущих сервиса. Отметьте: если в двух предыдущих в качестве scope используется scope, то в данном случаем пе-редаем непосредственно scope.

    // $compile - компилирует HTML строку или DOM в шаблон и предоставляюе функицю,
    // которая в последствии может использоваться для связик $scope и шаблона вместе.
    
    angular.module("exampleApp", []).controller("defaultCtrl", function ($scope) {
    
        $scope.items = ["item 1", "item 2", "item 3", "item 4"];
    
    }).directive("myList", function ($compile) {
        return function (scpoe, element, attributes) {
    
            var expr = "<ul><li ng-repeat='item in items'>{{item}}</li></ul>";
            var ul = angular.element(expr);
    
            // $compile создает функцию на основе HTML кода, которую можно будет использовать для генерации разметки.
            var compileFn = $compile(ul);
    
            // Выполняем компиляцию. Создаем контент выполняя выражения привязки.
            compileFn(scpoe);
    
            // Добавляем готовый контент на страницу
            element.append(ul);
        }
    });
    <body ng-controller="defaultCtrl">
        <div my-list></div>
    </body>

    Комментарии к статье

    Добавить комментарий