it-swarm.com.ru

Компоненты AngularJS 1.5+ не поддерживают Watchers, что обходится?

Я обновлял свои пользовательские директивы до новой компонентная архитектура . Я читал, что компоненты не поддерживают наблюдателей. Это правильно? Если да, то как вы обнаруживаете изменения на объекте? Для базового примера у меня есть пользовательский компонент myBox, который имеет дочерний компонент game с привязкой к игре. Если в игровом компоненте есть игра с изменениями, как я могу показать предупреждающее сообщение в myBox? Я так понимаю, есть метод rxJS, возможно ли сделать это чисто по-угловому? Мой JSFiddle

JavaScript

var app = angular.module('myApp', []);
app.controller('mainCtrl', function($scope) {

   $scope.name = "Tony Danza";

});

app.component("myBox",  {
      bindings: {},
      controller: function($element) {
        var myBox = this;
        myBox.game = 'World Of warcraft';
        //IF myBox.game changes, show alert message 'NAME CHANGE'
      },
      controllerAs: 'myBox',
      templateUrl: "/template",
      transclude: true
})
app.component("game",  {
      bindings: {game:'='},
      controller: function($element) {
        var game = this;


      },
      controllerAs: 'game',
      templateUrl: "/template2"
})

HTML

<div ng-app="myApp" ng-controller="mainCtrl">
  <script type="text/ng-template" id="/template">
    <div style='width:40%;border:2px solid black;background-color:yellow'>
      Your Favourite game is: {{myBox.game}}
      <game game='myBox.game'></game>
    </div>
  </script>

 <script type="text/ng-template" id="/template2">
    <div>
    </br>
        Change Game
      <textarea ng-model='game.game'></textarea>
    </div>
  </script>

  Hi {{name}}
  <my-box>

  </my-box>

</div><!--end app-->
76
Ka Tech

Написание компонентов без Наблюдатели

В этом ответе описаны пять методов, которые можно использовать для написания компонентов AngularJS 1.5 без использования наблюдателей.


Используйте директиву ng-change

какие альтернативные методы доступны для наблюдения за изменениями состояния объекта без использования watch при подготовке AngularJs2?

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

<textarea ng-model='game.game' 
          ng-change="game.textChange(game.game)">
</textarea>

А чтобы распространить событие на родительский компонент, обработчик событий необходимо добавить в качестве атрибута дочернего компонента.

<game game='myBox.game' game-change='myBox.gameChange($value)'></game>

JS

app.component("game",  {
      bindings: {game:'=',
                 gameChange: '&'},
      controller: function() {
        var game = this;
        game.textChange = function (value) {
            game.gameChange({$value: value});
        });

      },
      controllerAs: 'game',
      templateUrl: "/template2"
});

И в родительском компоненте:

myBox.gameChange = function(newValue) {
    console.log(newValue);
});

Это предпочтительный метод продвижения вперед. Стратегия AngularJS с использованием $watch не масштабируется, потому что это стратегия опроса. Когда число слушателей $watch достигает примерно 2000, пользовательский интерфейс становится вялым. Стратегия в Angular 2 состоит в том, чтобы сделать структуру более реактивной и избегать размещения $watch на $scope.


Используйте $onChanges крюк жизненного цикла

В версии 1.5.3 AngularJS добавил хук жизненного цикла $onChanges к сервису $compile.

Из документов:

Контроллер может предоставлять следующие методы, которые действуют как ловушки жизненного цикла:

  • $ onChanges (changesObj) - вызывается всякий раз, когда обновляются односторонние (<) или интерполяционные (@) привязки. changesObj - это хеш, ключи которого - это имена связанных свойств, которые изменились, а значения - это объект формы { currentValue: ..., previousValue: ... }. Используйте этот хук для запуска обновлений в компоненте, таких как клонирование связанного значения, чтобы предотвратить случайную мутацию внешнего значения.

- Справочник по API AngularJS - Хуки жизненного цикла

Хук $onChanges используется для реагирования на внешние изменения в компоненте с помощью односторонних привязок <. Директива ng-change используется для распространения изменений с контроллера ng-model вне компонента с привязками &


Используйте $doCheck крюк жизненного цикла

В версии 1.5.8 AngularJS добавил хук жизненного цикла $doCheck к сервису $compile.

Из документов:

Контроллер может предоставлять следующие методы, которые действуют как ловушки жизненного цикла:

  • $doCheck() - вызывается при каждом повороте цикла дайджеста. Предоставляет возможность обнаруживать изменения и действовать на них. Любые действия, которые вы хотите предпринять в ответ на обнаруженные вами изменения, должны вызываться из этой ловушки; реализация этого не влияет на то, когда вызывается $onChanges. Например, этот хук может быть полезен, если вы хотите выполнить проверку глубокого равенства или проверить объект Date, изменения в котором не будут обнаружены детектором изменений Angular и, следовательно, не вызовут $onChanges. Этот хук вызывается без аргументов; при обнаружении изменений необходимо сохранить предыдущие значения для сравнения с текущими значениями.

- Справочник по API AngularJS - Хуки жизненного цикла


Межкомпонентное общение с require

Директивы могут требовать контроллеров других директив для обеспечения связи между собой. Это может быть достигнуто в компоненте путем предоставления отображения объекта для свойства require . Ключи объекта указывают имена свойств, под которыми требуемые контроллеры (значения объектов) будут связаны с контроллером требуемого компонента.

app.component('myPane', {
  transclude: true,
  require: {
    tabsCtrl: '^myTabs'
  },
  bindings: {
    title: '@'
  },
  controller: function() {
    this.$onInit = function() {
      this.tabsCtrl.addPane(this);
      console.log(this);
    };
  },
  templateUrl: 'my-pane.html'
});

Для получения дополнительной информации см. AngularJS Developer Guide - Intercomponent Communication


Push-значения из службы с RxJS

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

Создайте сервис с расширениями RxJS для Angular .

<script src="//unpkg.com/angular/angular.js"></script>
<script src="//unpkg.com/rx/dist/rx.all.js"></script>
<script src="//unpkg.com/rx-angular/dist/rx.angular.js"></script>
var app = angular.module('myApp', ['rx']);

app.factory("DataService", function(rx) {
  var subject = new rx.Subject(); 
  var data = "Initial";

  return {
      set: function set(d){
        data = d;
        subject.onNext(d);
      },
      get: function get() {
        return data;
      },
      subscribe: function (o) {
         return subject.subscribe(o);
      }
  };
});

Тогда просто подпишитесь на изменения.

app.controller('displayCtrl', function(DataService) {
  var $ctrl = this;

  $ctrl.data = DataService.get();
  var subscription = DataService.subscribe(function onNext(d) {
      $ctrl.data = d;
  });

  this.$onDestroy = function() {
      subscription.dispose();
  };
});

Клиенты могут подписываться на изменения с помощью DataService.subscribe, а производители могут отправлять изменения с помощью DataService.set

ДЕМО на ПЛНКР .

142
georgeawg

Объект $watch доступен внутри объекта $scope, поэтому вам нужно добавить $scope внутри функции фабрики контроллера, а затем поместить наблюдатель над переменной.

$scope.$watch(function(){
    return myBox.game;
}, function(newVal){
   alert('Value changed to '+ newVal)
});

Демо Здесь

Примечание: Я знаю, что вы преобразовали directive в component, чтобы удалить зависимость $scope, чтобы вы были на шаг ближе к Angular2. Но похоже, что это не было удалено для этого случая . </ Забастовка>

Обновление

В основном Angular 1.5 добавляет .component метод jus дифференцировать две разные функциональности. Подобно component. Означает выполнение определенного поведения, добавляя selector, где как directive означает добавление определенного поведения в DOM. Директива - это просто метод-обертка в .directive DDO (объект определения директивы). Единственное, что вы можете видеть, это то, что они удалили функцию link/compile при использовании метода .component, где у вас была возможность получить угловой скомпилированный DOM.

Используйте $onChanges$doCheck ловушку жизненного цикла ловушки жизненного цикла компонента Angular, они будут доступны после версии Angular 1.5.3+.

_/$ onChanges (changesObj) - Вызывается при обновлении привязок. ChangesObj - это хеш, ключами которого являются имена связанных свойств.

$ doCheck () - Вызывается при каждом повороте цикла дайджеста при изменении привязки. Предоставляет возможность обнаруживать изменения и действовать на них.

Использование той же функции внутри компонента обеспечит совместимость вашего кода для перехода на Angular 2.

8
Pankaj Parkar

Всем, кто интересуется моим решением, я в конечном итоге прибегаю к RXJS Observables, который вам придется использовать, когда вы доберетесь до Angular 2. Вот рабочая скрипка для связи между компонентами, она дает мне больше контроля над тем, что смотреть.

JS FIDDLE RXJS Наблюдаемые

class BoxCtrl {
    constructor(msgService) {
    this.msgService = msgService
    this.msg = ''

    this.subscription = msgService.subscribe((obj) => {
      console.log('Subscribed')
      this.msg = obj
    })
    }

  unsubscribe() {
    console.log('Unsubscribed')
    msgService.usubscribe(this.subscription)
  }
}

var app = angular
  .module('app', ['ngMaterial'])
  .controller('MainCtrl', ($scope, msgService) => {
    $scope.name = "Observer App Example";
    $scope.msg = 'Message';
    $scope.broadcast = function() {
      msgService.broadcast($scope.msg);
    }
  })
  .component("box", {
    bindings: {},
    controller: 'BoxCtrl',
    template: `Listener: </br>
    <strong>{{$ctrl.msg}}</strong></br>
    <md-button ng-click='$ctrl.unsubscribe()' class='md-warn'>Unsubscribe A</md-button>`
  })
  .factory('msgService', ['$http', function($http) {
    var subject$ = new Rx.ReplaySubject();
    return {
      subscribe: function(subscription) {
        return subject$.subscribe(subscription);
      },
      usubscribe: function(subscription) {
        subscription.dispose();
      },
      broadcast: function(msg) {
        console.log('success');
        subject$.onNext(msg);
      }
    }
  }])
4
Ka Tech

Небольшой заголовок относительно использования ng-change, как рекомендуется с принятым ответом, вместе с угловым компонентом 1,5.

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

Разметка, в которой используется компонент:

<my-component on-change="$ctrl.doSth()"
              field-value="$ctrl.valueToWatch">
</my-component>

Компонент JS:

angular
  .module('myComponent')
  .component('myComponent', {
    bindings: {
      onChange: '&',
      fieldValue: '='
    }
  });

Компонент разметки:

<select ng-model="$ctrl.fieldValue"
        ng-change="$ctrl.onChange()">
</select>
2
Wtower

Я опоздал. Но это может помочь другим людям.

app.component("headerComponent", {
    templateUrl: "templates/header/view.html",
    controller: ["$rootScope", function ($rootScope) {
        let $ctrl = this;
        $rootScope.$watch(() => {
            return $ctrl.val;
        }, function (newVal, oldVal) {
            // do something
        });
    }]
});
0
tetra master

Доступно в IE11, MutationObserver https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver . Вам нужно внедрить службу $ element в контроллер, который разделяет разделение DOM/контроллер, но я чувствую, что это фундаментальное исключение (то есть недостаток) в angularjs. Так как hide/show является асинхронным, нам нужен обратный вызов on-show, который angularjs & angular-bootstrap-tab не предоставляют. Это также требует, чтобы вы знали, какой конкретный элемент DOM вы хотите наблюдать. Я использовал следующий код для контроллера angularjs, чтобы запустить отображение перекомпоновки диаграммы Highcharts.

const myObserver = new MutationObserver(function (mutations) {
    const isVisible = $element.is(':visible') // Requires jquery
    if (!_.isEqual(isVisible, $element._prevIsVisible)) { // Lodash
        if (isVisible) {
            $scope.$broadcast('onReflowChart')
        }
        $element._prevIsVisible = isVisible
    }
})
myObserver.observe($element[0], {
    attributes: true,
    attributeFilter: ['class']
})
0
user982671

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

Событие транслируется: $rootScope.$broadcast("clickRow", rowId)by любым родительским (или даже дочерним контроллером) . Затем в вашем контроллере вы можете обработать событие следующим образом: 

$scope.$on("clickRow", function(event, data){
    // do a refresh of the view with data == rowId
});

Вы также можете добавить некоторые записи на эту тему (взято отсюда: https://stackoverflow.com/a/34903433/3147071 )

var withLogEvent = true; // set to false to avoid events logs
app.config(function($provide) {
    if (withLogEvent)
    {
      $provide.decorator("$rootScope", function($delegate) {
        var Scope = $delegate.constructor;
        var origBroadcast = Scope.prototype.$broadcast;
        var origEmit = Scope.prototype.$emit;

        Scope.prototype.$broadcast = function() {
          console.log("$broadcast was called on $scope " + this.$id + " with arguments:",
                     arguments);
          return origBroadcast.apply(this, arguments);
        };
        Scope.prototype.$emit = function() {
          console.log("$emit was called on $scope " + this.$id + " with arguments:",
                     arguments);
          return origEmit.apply(this, arguments);
        };
        return $delegate;
      });
    }
});
0
sebius