---
title: 'Модальное диаложество в AngularJS'
url: 'https://graker.ru/news/2015/07/13/angularjs_modal_dialogs'
language: ru
type: post
author: 'Роман Грачев'
published: '2015-07-13T12:54:56+03:00'
updated: '2017-03-13T13:09:43+03:00'
categories:
  - Программирование
---

# Модальное диаложество в AngularJS

Диалоговое окно — важный элемент в интерфейсах многих приложений. В этой заметке мы посмотрим, как в AngularJS вызвать модальный диалог, передать в него данные и получить результат (то есть действие пользователя) на примере бутстрапных диалогов [Modal](http://angular-ui.github.io/bootstrap/#/modal) из ui.bootstrap (по ссылке есть примеры).

![](https://graker.ru/sites/default/files/styles/max_1300x1300/public/legacy/images/ngbootstrapdlg.png.webp?itok=R3X4NZrV)

Для начала скачаем и подключим к странице скрипт Angular UI Bootstrap целиком, или кастомную сборку (не забудьте включить в нее Modal). Примерно вот так:

```
<script src="/js/libs/ui-bootstrap/ui-bootstrap-custom-tpls-0.12.1.js"></script>
```

Теперь подключим модуль к нашему приложению AngularJS:

```javascript
var myAppWithDialogs = angular.module('myAppWithDialogs', ['ui.bootstrap']);
```

Чтобы использовать модальные диалоги в каком-нибудь контроллере, нужно внедрить зависимость от сервиса $modal:

```javascript
myAppWithDialogs.controller('myRowsController', function ($scope, $modal) {
  //...
}
```

Сервис $modal позволяет нам заранее создавать темплейты для диалогов, а потом указывать их при вызове. Создадим темплейт для диалога удаления чего-нибудь:

```
<div class="modal-header">
  <h3 class="modal-title">Delete this item?</h3>
</div>
<div class="modal-body">
  Are you sure to delete this item?
  <div class="form-group" ng-show="isRecurrent">
    <label>
      <input type="radio" ng-model="deleteType" value="this">
      This occurence
    </label><br />
    <label>
      <input type="radio" ng-model="deleteType" value="future">
      Future occurences
    </label><br />
    <label>
      <input type="radio" ng-model="deleteType" value="all">
      All occurences
    </label><br />
  </div>
</div>
<div class="modal-footer">
  <button class="btn btn-danger" ng-click="ok()">Delete</button>
  <button class="btn btn-default" ng-click="cancel()">Cancel</button>
</div>
```

Сразу вставим в диалог радиокнопки, чтобы пользователь выбрал, как он хочет удалить item — этот экземпляр, этот и будущие или вообще все. Ну, как будто item — это рекуррентная календарная запись. На примере этих радиокнопок мы увидим, как передать в диалог какую-то информацию, и как забрать из диалога то, что пользователь в нем ввел или выбрал.
Обратите внимание на директивы ng-click у кнопок диалога — функции, указанные в этих директивах, должны быть реализованы в контроллере диалога. Именно они будут закрывать диалог и подтверждать или отменять удаление.
Сохраним этот темплейт где-нибудь, например в tpls/delete-dialog.html относительно корня приложения.

Теперь напишем контроллер:

```javascript
myAppWithDialogs.controller('modalDialogController', function ($scope, $modalInstance, recurrence) {
  $scope.recurrence = recurrence;
  $scope.isRecurrent = (recurrence !== null);
  $scope.deleteType = 'this';
  $scope.ok = function () {
    if ($scope.isRecurrent) {
      $modalInstance.close($scope.deleteType);
    } else {
      $modalInstance.close('all');
    }
  };
  $scope.cancel = function () {
    $modalInstance.dismiss('cancel');
  };
});
```

В контроллер, помимо $scope, мы внедряем $modalInstance — экземпляр модального окна, с которым будет работать контроллер и передаем значение recurrence, которое указывает является ли наш элемент рекуррентным (т.е. увидит пользователь радиокнопки, или нет).
Затем мы определяем на $scope несколько значений, с которыми связаны элементы диалога (через директивы и ). Так, $scope.isRecurrent связан с тем, видимы наши радиокнопки или нет, а в $scope.deleteType попадет выбор пользователя из трех радиокнопок.

Функция ok(), связанная в темплейте диалога с соответствующей кнопкой, вызывает метод $modalInstance.close(). Этот метод закрывает диалог и позволяет вернуть какое-нибудь значение (в данном случае, выбранную радиокнопку).
Функция cancel() вызывает метод $modalInstance.dismiss(), также закрывающий диалоговое окно с возможностью указать причину.
Разница между close() и dismiss() заключается в том, что *promise*, возвращаемый при открытии модального окна, в случае вызова close() будет *resolved*, а в случае dismiss() — *rejected*.

Теперь допустим у нас есть где-то кнопка «удалить» напротив какой-то строки row (которая должна удалить эту строку). И есть функция deleteItem(row), которая вызывается по клику на эту кнопку. Откроем в ней наш новый диалог:

```javascript
$scope.deleteItem = function (row) {
  var modalInstance = $modal.open({
    templateUrl: '/tpls/delete-dialog.html',
    controller: 'modalDialogController',
    size: 'sm',
    resolve: {
      recurrence: function () {
        return row.recurrence;
      }
    }
  });
}
```

Что тут происходит? Мы вызываем метод open() из сервиса $modal, который открывает наше диалоговое окно. В качестве аргумента мы передаем объект со следующими настройками:

- templateUrl — путь к html-темплейту диалога;
- controller — контроллер диалога;
- size — размер окна; значение будет подставлено в класс с префиксом «modal-» — например, если size = sm, класс будет modal-sm;
- resolve — тут перечисляются значения, которые будут определены и переданы модальному окну; в данном случае мы передаем значение recurrence, которое у нас является свойством объекта row.

Описание остальных параметров можно найти на странице компонента.
Глядя на параметры templateUrl и controller, нетрудно догадаться, что мы можем использовать разные темплейты с одним контроллером, или один контроллер с разными темплейтами. В общем, сервис $modal достаточно гибок в работе.

Наконец, теперь, когда окно открыто, осталось лишь получить выбор пользователя (т.е. подтверждение или отмену). Это нетрудно сделать в той же функции deleteItem() с помощью возвращенного нам объекта modalInstance:

```javascript
$scope.deleteItem = function (row) {
    //show confirmation dialog
    var modalInstance = $modal.open({
      templateUrl: '/tpls/delete-dialog.html',
      controller: 'modalDialogController',
      size: 'sm',
      resolve: {
        recurrence: function () {
          return row.recurrence;
        }
      }
    });

    modalInstance.result.then(function (deleteType) {
      switch (deleteType) {
        case 'this':
          //удаляем только этот экземпляр
          break;
        case 'future':
          //удаляем этот и будущие экземпляры
          break;
        case 'all':
          //удаляем все экземпляры
          break;
      }
    });
  };
```

Суть в том, что свойство modalInstance.result — есть не что иное, как обычный *promise*. Соответственно, мы можем вызвать modalInstance.result.then() и передать 2 коллбэка — для *resolved* и для *rejected*. В данном случае мы используем только один коллбэк для *resolved*, поскольку нам не нужно ничего делать, если пользователь нажал Cancel. А в аргументе коллбэка будет то, что мы передали в контроллере диалога аргументом close(), то есть радиокнопка, выбранная пользователем.

Примерно так можно организовать модальные окна с помощью UI Bootstrap в AngularJS. На первый взгляд решение может показаться несколько громоздким. Но, в силу использования темплейтов и контроллеров, оно довольно гибкое и позволяет относительно компактно определить все нужные нашему приложению диалоги.
