Передача данных между сущностями AngularJS
Один из самых распространенных вопросов по AngularJS – передача данных от одной сущности другой. На самом деле вариантов не так много. Попробуем их все рассмотреть.
Для начала определимся какими типами сущностей будем оперировать: это будет контроллер, сервис и представление.
Из контроллера в представление
Посредством $scope
либо для нового синтаксиса контроллера через this в контроллере:
а в шаблоне представления получим через переменную:
для нового синтаксиса:
Из представления в контроллер
Для этого мы используем two-way data binding с помощью директив ng-bind и ng-model в представлении:
теперь при изменении значения поля ввода будет изменяться name.first значение в контроллере.
Из представления в представление
Фактически осуществляется посредством контроллера (рассмотрено выше), но переменную-свойство в контроллере можем не создавать: она будет создана автоматически. Итого имеем связь представление – контроллер – представление. Важно, чтобы 2 элемента представления, которые собираемся синхронизировать, находились в рамках одного контроллера.
Из контроллера в сервис
тут все просто: инжектим сервис в контроллер и задаем значение свойству, либо вызываем сеттер-метод (что ИМХО более верно):
Из сервиса в контроллер
Мы не можем изменять данные контроллера в сервисе, но мы можем “залинковать” объект-свойство сервиса на свойство контроллера, тогда в случае изменений внутри этого объекта – данные контроллера также будут обновлены:
из-за того, что нужно было имитировать изменения сервиса, пример получился не самый простой. Вот полный код примера, надеюсь с ним будет проще.
Из сервиса в представление
Осуществляется посредствам контроллера, то есть сервис – контроллер – представление.
и представление соответственно:
Из представления в сервис
Аналогично предыдущему: представление – контроллер – сервис.
Из сервиса в сервис
Один сервис инжектит другой и там вызывается сеттер-метод:
Из контроллера в контроллер
Самый интересный случай, когда в зависимости от специфики может быть решен различными способами. Возможные варианты:
- из родительского контроллера в дочерний
- и дочернего в родительский
- из контроллера в контроллер, когда оба находятся на одном уровне (либо в разных ветках иерархии)
Рассмотрим каждый отдельно.
Из родительского контроллера в дочерний
тут также могут быть 2 случая:
- открытый scope дочернего контроллера
- изолированный scope дочернего контроллера
В случае с открытым – все просто: дочерний контроллер наследует свойства scope родителя по умолчанию:
– как видим nameController является дочерним по отношению к userController, поэтому мы легко можем получить доступ к свойствам родителя:
С случае с изолированным scope(который мы можем получить при создании директивы) нам необходимо “залинковать” необходимые свойства родительского в дочерний. Представление будет выглядеть следующим образом:
а код директивы и контроллера:
В этом месте мы описываем связывание:
– это означает, что name будет взято из атрибута директивы.
Из дочернего контроллера в родительский
Вот этот вариант уже поинтереснее, тут не все так просто, ибо не смотря на то, что мы можем получить доступ к свойствам scope родителя, этот доступ будет только на чтение. При попытке изменить какое-либо свойство родителя, мы лишь получим создание одноименного свойства в дочернем контроллере. Это объясняется прототипной моделью наследования scope.
Возможны 2 решения:
- изменяя не само свойство, а свойство свойства (знаю, сильно запутано, но на примере не должно быть сложно) То есть, вместо $scope.name, используем $scope.name.first.
- через специально созданный сеттер-метод в родительском котроллере. Пример. :
Случай с изолированным скоупом полностью аналогичен предыдущему (Из родительского контроллера в дочерний), только линковка будет делаться не односторонняя:
Передача данных между контроллерами находящимися на разных ветках
т.е., грубо говоря, когда мы не можем применить к ним характеристику родитель-ребенок. Решение будет заключаться в нахождении общего родительского контроллера( вплоть до $rootScope ), посредством которого и будет осуществлена передача: контроллер А – общий родительский контроллер – контроллер Б.
Передача данных дочернего и родительского контроллера уже рассмотрена выше, поэтому даю просто общий пример связки (контроллерА – родительский контроллер – контроллерБ)
Также передача данных между несвязанными напрямую контроллерами может происходить с помощью событий.
Общение контроллеров с помощью событий
Это на столько отдельный случай, что я решил посвятить ему целый пункт.
Сразу оговорюсь: если вы еще только начинаете работать с AngularJS – не используйте этот подход . При неумелом его использовании может получиться такой “салат” из зависимостей, что потом сами не разберетесь.
Передавать события (с данными) можно посредством двух методов $scope.$emit и $scope.$broadcast. Отличие $emit от $broadcast заключается в том, что $emit передает события вверх по цепочке в скоупы всех родительских контроллеров, а $broadcast наоборот – в дочерние:
Чтобы словить событие необходимо использовать метод $scope.$on.
Полный пример работы с событиями.
Общение контроллеров с помощью специального сервиса
Существует еще один способ общения между сервисами, который напомнил Владимир Гомонов ( благодарю за дополнение! ), – это использование специального сервиса, который будет инжектиться в оба контроллера и в итоге получим связку: контроллер А – сервис – контроллер Б. Взаимодействие контроллер – сервис и сервис – контроллер уже рассмотрено выше, поэтому только выложу общий пример.
Вроде бы все возможные варианты передачи данных в рамках AngularJS перечислил, если что-то забыл – пишите – добавим.