У меня есть служба, скажи:
factory('aService', ['$rootScope', '$resource', function ($rootScope, $resource) {
var service = {
foo: []
};
return service;
}]);
И я хотел бы использовать foo
для управления списком, который отображается в HTML:
<div ng-controller="FooCtrl">
<div ng-repeat="item in foo">{{ item }}</div>
</div>
Чтобы контроллер мог определить, когда aService.foo
обновляется, я собрал воедино этот шаблон, где я добавляю aService к контроллеру $scope
и затем использую $scope.$watch()
:
function FooCtrl($scope, aService) {
$scope.aService = aService;
$scope.foo = aService.foo;
$scope.$watch('aService.foo', function (newVal, oldVal, scope) {
if(newVal) {
scope.foo = newVal;
}
});
}
Это кажется длинным, и я повторял это на каждом контроллере, который использует переменные службы. Есть ли лучший способ для просмотра общих переменных?
aService.foo
на html-разметку? (например: plnkr.co/edit/aNrw5Wo4Q0IxR2loipl5?p=preview )Ответы:
Вы всегда можете использовать старый добрый шаблон наблюдателя, если хотите избежать тирании и накладных расходов
$watch
.В сервисе:
И в контроллере:
источник
$destory
событие в области и добавьте незарегистрированный метод кaService
$watch
Сравнение с моделью наблюдателя - это просто выбор: опросить или нажать, и это в основном вопрос производительности, поэтому используйте его, когда важна производительность. Я использую шаблон наблюдателя, когда в противном случае мне пришлось бы «глубоко» наблюдать за сложными объектами. Я присоединяю целые сервисы к области $ вместо просмотра отдельных значений сервисов. Я избегаю $ watch в Angular, как дьявол, этого достаточно в директивах и в привязке данных к angular.В подобном сценарии, когда несколько / неизвестные объекты могут быть заинтересованы в изменениях, используйте
$rootScope.$broadcast
изменяемый элемент.Вместо того, чтобы создавать свой собственный реестр слушателей (которые должны быть очищены при различных $ разрушениях), вы должны быть в состоянии
$broadcast
от рассматриваемой службы.Вы по-прежнему должны кодировать
$on
обработчики в каждом слушателе, но шаблон не связан с несколькими вызовами$digest
и, таким образом, исключает риск продолжительных наблюдателей.Таким образом, слушатели также могут приходить и уходить из DOM и / или других дочерних областей, не меняя при этом службу.
** обновление: примеры **
Широковещательные рассылки имеют смысл в «глобальных» сервисах, которые могут повлиять на множество других вещей в вашем приложении. Хорошим примером является служба пользователя, в которой может иметь место ряд событий, таких как вход в систему, выход из системы, обновление, ожидание и т. Д. Я считаю, что именно здесь широковещательные рассылки имеют смысл, поскольку любая область может прослушивать событие без даже внедряя сервис, и ему не нужно оценивать какие-либо выражения или результаты кэширования для проверки изменений. Он просто срабатывает и забывает (поэтому убедитесь, что это уведомление о том, что нужно забыть, а не то, что требует действий)
Приведенный выше сервис будет транслировать сообщение во все области, когда функция save () завершится и данные будут действительными. В качестве альтернативы, если это $ ресурс или ajax-представление, переместите широковещательный вызов в обратный вызов, чтобы он срабатывал при ответе сервера. Широковещательные рассылки особенно подходят этому шаблону, потому что каждый слушатель просто ожидает события без необходимости проверять область действия каждого отдельного $ digest. Слушатель будет выглядеть так:
Одним из преимуществ этого является устранение нескольких часов. Если бы вы комбинировали поля или извлекали значения, как в примере выше, вам бы пришлось следить за свойствами как имени, так и фамилии. Наблюдение за функцией getUser () будет работать только в том случае, если пользовательский объект будет заменен при обновлении, и не будет срабатывать, если пользовательский объект просто обновит свои свойства. В этом случае вам придется сделать глубокие часы, и это более интенсивно.
$ broadcast отправляет сообщение из области, к которой оно обращено, в любые дочерние области. Поэтому вызов его из $ rootScope будет срабатывать во всех областях. Например, если вы будете транслировать $ из области видимости вашего контроллера, она будет срабатывать только в областях, которые наследуются от области видимости вашего контроллера. $ emit идет в противоположном направлении и ведет себя аналогично событию DOM в том смысле, что оно всплывает в цепочку областей видимости.
Имейте в виду, что есть сценарии, в которых $ broadcast имеет большой смысл, и есть сценарии, в которых $ watch является лучшим вариантом, особенно если он находится в изолированной области с очень конкретным выражением наблюдения.
источник
Я использую такой же подход, как @dtheodot, но использую угловое обещание вместо передачи обратных вызовов
Тогда везде, где просто использовать
myService.setFoo(foo)
метод для обновленияfoo
на службе. В вашем контроллере вы можете использовать его как:Первые два аргумента -
then
это обратные вызовы об успехе и ошибке, третий - обратный вызов уведомления.Ссылка для $ q.
источник
$scope.$watch
с сервисной переменной, казалось, не работала (область, на которую я смотрел, была модальной, унаследованной от$rootScope
) - это работало. Классный трюк, спасибо, что поделились!Без часов или обратного вызова наблюдателя ( http://jsfiddle.net/zymotik/853wvv7s/ ):
JavaScript:
HTML
Этот JavaScript работает, когда мы передаем объект обратно из сервиса, а не значение. Когда объект JavaScript возвращается из службы, Angular добавляет наблюдение ко всем его свойствам.
Также обратите внимание, что я использую 'var self = this', так как мне нужно сохранять ссылку на исходный объект при выполнении $ timeout, иначе 'this' будет ссылаться на объект окна.
источник
$scope.count = service.count
не работает.$scope.data = service.data
<p>Count: {{ data.count }}</p>
Я наткнулся на этот вопрос в поисках чего-то похожего, но думаю, что оно заслуживает подробного объяснения происходящего, а также некоторых дополнительных решений.
Когда угловое выражение, такое как то, которое вы использовали, присутствует в HTML, Angular автоматически устанавливает
$watch
для$scope.foo
и обновляет HTML всякий раз, когда$scope.foo
изменяется.Непроизносимая проблема заключается в том, что одна из двух вещей влияет на то
aService.foo
, что изменения не обнаруживаются. Эти две возможности:aService.foo
каждый раз устанавливается новый массив, что делает ссылку на него устаревшей.aService.foo
обновляется таким образом, чтобы$digest
цикл не запускался при обновлении.Проблема 1: устаревшие ссылки
Рассматривая первую возможность, предполагая,
$digest
что применяется a , если быaService.foo
всегда был один и тот же массив, автоматически заданный набор$watch
обнаружил бы изменения, как показано в фрагменте кода ниже.Решение 1-a: убедитесь, что массив или объект является одним и тем же объектом при каждом обновлении
Показать фрагмент кода
Как вы можете видеть, ng-repeat, предположительно прикрепленный к
aService.foo
, не обновляется приaService.foo
изменениях, но ng-repeat, прикрепленный к,aService2.foo
делает . Это потому, что наша ссылка наaService.foo
устарела, а наша ссылка наaService2.foo
нет. Мы создали ссылку на исходный массив with$scope.foo = aService.foo;
, который затем был отброшен службой при следующем обновлении, что означает, что мы$scope.foo
больше не ссылаемся на массив, который нам нужен.Однако, хотя есть несколько способов убедиться, что исходная ссылка сохраняется в такте, иногда может потребоваться изменить объект или массив. Или что, если свойство service ссылается на примитив, такой как
String
илиNumber
? В этих случаях мы не можем просто полагаться на ссылку. Так что можно делать?Несколько ответов, приведенных ранее, уже дают некоторые решения этой проблемы. Тем не менее, я лично за использование простого метода, предложенного Jin и thetallweeks в комментариях:
Решение 1-b: прикрепите сервис к области действия и укажите ссылку
{service}.{property}
в HTML.Смысл, просто сделай это:
HTML:
JS:
Показать фрагмент кода
Таким образом,
$watch
будет решеноaService.foo
для каждого$digest
, который получит правильно обновленное значение.Это своего рода то, что вы пытались сделать с помощью обходного пути, но в гораздо меньшей степени. Вы добавили ненужные
$watch
в контроллере , который явно ставитfoo
на$scope
всякий раз , когда она меняется. Вам не нужно это дополнительное,$watch
когда вы присоединяетеaService
вместоaService.foo
к$scope
и явно привязываетеaService.foo
в разметке.Теперь это все хорошо, если предположить, что применяется
$digest
цикл. В моих примерах выше я использовал$interval
сервис Angular для обновления массивов, который автоматически запускает$digest
цикл после каждого обновления. Но что, если служебные переменные (по какой-либо причине) не обновляются в «угловом мире». Другими словами, мы DonT есть$digest
цикл активируется автоматически , когда меняется свойство службы?Проблема 2: Отсутствует
$digest
Многие решения здесь решат эту проблему, но я согласен с Code Whisperer :
Поэтому я предпочел бы продолжать использовать
aService.foo
ссылку в разметке HTML, как показано во втором примере выше, и не должен регистрировать дополнительный обратный вызов в контроллере.Решение 2. Используйте сеттер и геттер с
$rootScope.$apply()
Я был удивлен, что никто еще не предложил использовать сеттер и геттер . Эта возможность была введена в ECMAScript5 и, таким образом, существует уже много лет. Конечно, это означает, что если по какой-либо причине вам необходимо поддерживать действительно старые браузеры, то этот метод не будет работать, но я чувствую, что методы получения и установки в JavaScript значительно недоиспользуются. В данном конкретном случае они могут быть весьма полезны:
Показать фрагмент кода
Здесь я добавил «частный» переменную в функции службы:
realFoo
. Получить обновляется и извлекаться с использованиемget foo()
иset foo()
функции соответственно наservice
объекте.Обратите внимание на использование
$rootScope.$apply()
в функции set. Это гарантирует, что Angular будет в курсе любых измененийservice.foo
. Если вы получаете ошибки 'inprog', посмотрите эту полезную справочную страницу , или если вы используете Angular> = 1.3, вы можете просто использовать$rootScope.$applyAsync()
.Также будьте осторожны с этим, если
aService.foo
обновляется очень часто, так как это может значительно повлиять на производительность. Если производительность будет проблемой, вы можете установить шаблон наблюдателя, подобный другим ответам здесь, используя установщик.источник
services
не за свойствами, которые принадлежат самой службе.Насколько я могу сказать, вам не нужно делать что-то настолько сложное, как это. Вы уже присвоили foo из службы своей области видимости, и поскольку foo является массивом (и, в свою очередь, объектом, он назначается по ссылке!). Итак, все, что вам нужно сделать, это что-то вроде этого:
Если какая-то другая переменная в этом же Ctrl зависит от изменения foo, тогда да, вам понадобятся часы для наблюдения за foo и внесения изменений в эту переменную. Но пока это просто просмотр ссылок не требуется. Надеюсь это поможет.
источник
$watch
работать с примитивом. Вместо этого, я определил метод на службу , которая будет возвращать элементарное значение:somePrimitive() = function() { return somePrimitive }
И я присвоил свойство $ возможности для этого метода:$scope.somePrimitive = aService.somePrimitive;
. Затем я использовал метод контекста в HTML:<span>{{somePrimitive()}}</span>
$scope.foo = aService.foo
не обновляет переменную области автоматически.Вы можете вставить сервис в $ rootScope и посмотреть:
В вашем контроллере:
Другой вариант - использовать внешнюю библиотеку, например: https://github.com/melanke/Watch.JS.
Работает с: IE 9+, FF 4+, SF 5+, WebKit, CH 7+, OP 12+, BESEN, Node.JS, Rhino 1.7+
Вы можете наблюдать изменения одного, многих или всех атрибутов объекта.
Пример:
источник
Вы можете наблюдать за изменениями внутри самой фабрики, а затем транслировать изменения
Тогда в вашем контроллере:
Таким образом, вы помещаете весь соответствующий заводской код в его описание, тогда вы можете полагаться только на трансляцию извне
источник
== == ОБНОВЛЕНО
Очень просто сейчас в $ watch.
Ручка здесь .
HTML:
JavaScript:
источник
Основываясь на ответе dtheodor, вы можете использовать что-то похожее на приведенное ниже, чтобы не забыть отменить регистрацию обратного вызова ... Некоторые могут возражать против передачи
$scope
службы.Array.remove - это метод расширения, который выглядит следующим образом:
источник
Вот мой общий подход.
В вашем контроллере
источник
Для тех, кто, как я, просто ищет простое решение, это делает почти то же, что вы ожидаете от использования обычных $ watch в контроллерах. Единственное отличие состоит в том, что он оценивает строку в контексте javascript, а не в определенной области видимости. Вам придется внедрить $ rootScope в ваш сервис, хотя он используется только для правильного подключения к циклам дайджеста.
источник
Столкнувшись с очень похожей проблемой, я посмотрел функцию в области видимости и заставил функцию возвращать переменную службы. Я создал JS Fiddle . Вы можете найти код ниже.
источник
Я пришел к этому вопросу, но оказалось, что моя проблема в том, что я использовал setInterval, когда я должен был использовать поставщика угловых $ интервалов. Это также относится и к setTimeout (вместо этого используйте $ timeout). Я знаю, что это не ответ на вопрос ОП, но это могло бы помочь некоторым, поскольку помогло мне.
источник
setTimeout
или любую другую неангулярную функцию, но только не забудьте обернуть код в обратный вызов$scope.$apply()
.В другом потоке я нашел действительно отличное решение с похожей проблемой, но совершенно другим подходом. Источник: AngularJS: $ watch в директиве не работает при изменении значения $ rootScope
В принципе решение там говорит НЕ использовать ,
$watch
поскольку это очень тяжелое решение. Вместо этого они предлагают использовать$emit
и$on
.Моя проблема состояла в том, чтобы наблюдать переменную в моем сервисе и реагировать в директиве . А с вышеуказанным способом это очень просто!
Пример моего модуля / сервиса:
Поэтому в основном я смотреть My
user
- всякий раз , когда он установлен на новое значение I статус.$emit
user:change
Теперь в моем случае в директиве я использовал:
Сейчас в директиве слушаю на свое
$rootScope
и на данное изменение - реагирую соответственно. Очень легко и элегантно!источник
// сервис: (здесь ничего особенного)
// ctrl:
источник
Немного некрасиво, но я добавил регистрацию переменных области видимости в свой сервис для переключения:
И тогда в контроллере:
источник
this
с этого сервиса вместоself
?return this;
ваших конструкторов ;-)Взгляните на этот поршень: это самый простой пример
http://jsfiddle.net/HEdJF/
источник
Я видел несколько ужасных шаблонов наблюдателей, которые вызывают утечки памяти в больших приложениях.
Я мог бы немного опоздать, но это так просто.
Функция наблюдения отслеживает изменения ссылок (примитивные типы), если вы хотите наблюдать что-то вроде массива push, просто используйте:
someArray.push(someObj); someArray = someArray.splice(0);
Это обновит ссылку и обновит часы из любой точки мира. Включая метод получения услуг. Все, что является примитивом, будет обновлено автоматически.
источник
Я опоздал на часть, но я нашел более хороший способ сделать это, чем ответ, опубликованный выше. Вместо того, чтобы назначать переменную для хранения значения служебной переменной, я создал функцию, прикрепленную к области, которая возвращает служебную переменную.
контроллер
Я думаю, что это будет делать то, что вы хотите. Мой контроллер продолжает проверять ценность моего сервиса с этой реализацией. Честно говоря, это намного проще, чем выбранный ответ.
источник
Я написал две простые утилиты, которые помогают мне отслеживать изменения свойств сервиса.
Если вы хотите пропустить длинное объяснение, вы можете перейти прямо к jsfiddle
Эта служба используется в контроллере следующим образом. Предположим, у вас есть служба «testService» со свойствами «prop1», «prop2», «prop3». Вы хотите посмотреть и назначить область «prop1» и «prop2». С сервисом часов это будет выглядеть так:
Я бы запустил его в конце моего асинхронного кода, чтобы запустить цикл $ digest. Как это:
Итак, все это вместе будет выглядеть так (вы можете запустить его или открыть скрипку ):
источник