angularjs - как перерисовать компонент?

A1x

Новичок
Всем привет,

сделал компонент на Angular 1.5 - что-то вроде всплывающего flash сообщения, например на отправку формы - "форма сохранена" или "ошибка валидации" на основе бутстраповских стилей соответственно alert-success, alert-danger и т.д.

тег компонента выглядит так
Код:
<active-alert status="ctrl.alert.status" text="ctrl.alert.message"></active-alert>
если присваиваем например
Код:
ctrl.alert.status = 'success'; 
ctrl.alert.message = 'форма сохранена';
- появляется зеленое всплывающее сообщение и через какое-то время исчезает по таймауту

проблема в том что нельзя два раза подряд показать одно и то же сообщение например 'success' - когда второй раз присваиваем ctrl.alert.status = 'success'; - компонент не срабатывает потому что значение по факту не изменилось и умный Angular не меняет состояние. (не вызывается обработчик $onChanges компонента)

Собственно вопрос - как сделать чтобы компонент перерисовывался? Есть вариант перерисовывать по событию сгенерированному $rootScope.$broadcast(), но это похоже на стрельбу из пушки по воробьям, потому что в данном случае есть совершенно определенный адресат для этого события

поиграться можно тут - https://jsfiddle.net/x11a/qjtzwgka/18/

как можно заметить если нажимать кнопки success/error по очереди то все работает, если одну подряд то нет. Буду благодарен за любые замечания потому что у меня на Angular опыта мало
 

fixxxer

К.О.
Партнер клуба
Ручками какать в DOM - это вообще не angular way. И передавать параметрами директивы тут тоже ни к чему, вообще тут в компоненте нет смысла.
Сделай сервис, который будет хранить текущее сообщение.
Сделай отдельную директиву active-alert (в принципе можно и компонентом, но смысла не вижу), ее темплейтом сделай то, что ты там программно добавляешь в body, и всунь ее один раз где-нибудь на уровне ниже <body>.
В директиву инжектишь сервис и кладешь его в скоуп, в темлейте директивы банальный ng-show/ng-if.
В контроллеры инжектишь тот же сервис и дергаешь там его метод setMessage().
 
Последнее редактирование:

A1x

Новичок
если директивам можно какать в DOM то почему нельзя компонентам? Насколько я понял, компоненты - это те же директивы, только с более понятным (но наверное менее гибким) API, тем более если $element можно заинжектить в контроллер. Насчет того что передавать сообщение лучше с помощью сервиса согласен
 

fixxxer

К.О.
Партнер клуба
компоненты - это те же директивы, только с более понятным (но наверное менее гибким) API
Технически да, а концептуально это то же самое, что компонент в реакте.

если директивам можно какать в DOM
Только по очень большой нужде. :) В component-like директивах (angular <1.5) всегда стараются обходиться темплейтами со стандартными core-директивами.
Понятно, что есть масса случаев, когда без прямой работы с DOM не обойтись, но тут явно не этот случай.
$element можно заинжектить в контроллер
Можно и $window/$document заинжектить, и document.getElementById()... написать в контроллере.
Нужно ли? Вряд ли.

Контроллеры вообще стоит делать тонкими, как и в похапе.
 

A1x

Новичок
в результате сделал такой сервис общего назначения, который можно использовать если не хочется использовать события
Код:
angular.module('core').factory('Signal', function() {
  var registry = {};
  return {
      emit: function(name, data) {
          if (!registry[name]) {
              return;
          }

          for (var i = 0; i < registry[name].length; i++) {
              registry[name][i](data);
          }
      },
      on: function(name, handler) {
          if (!registry[name]) {
              registry[name] = [];
          }
          registry[name].push(handler);
      }
  }
})
окончательный вариант - https://jsfiddle.net/x11a/h7n5nz1r/26/
 

fixxxer

К.О.
Партнер клуба
registry[name].push(handler);
С таким подходом накушаешься невкусного, когда там будет свалка хендлеров из тех мест, где уже давно произошел scope.destroy.
Конкретно в случае с твоей директивой, которая на уровне ng-app обычно будет, этого не случится, но в качестве универсального подхода - не подойдет.
 

fixxxer

К.О.
Партнер клуба
Ща починим :)
PHP:
      on: function(name, handler, scope) {
          if (!registry[name]) {
              registry[name] = [];
          }
          registry[name].push(handler);
          scope && scope.$on('$destroy', function() {
              var idx = registry[name].indexOf(handler);
              ~idx && registry[name].splice(idx, 1);
          });
      }
 
  • Like
Реакции: A1x
Сверху