AngularJS 1.5+ 구성 요소는 감시자를 지원하지 않습니다. 해결 방법은 무엇입니까?


78

사용자 지정 지시문을 새로운 구성 요소 아키텍처 로 업그레이드했습니다 . 구성 요소가 감시자를 지원하지 않는다는 것을 읽었습니다. 이 올바른지? 그렇다면 물체의 변화를 어떻게 감지합니까? 기본 예를 들어 myBox게임에 바인딩이있는 자식 구성 요소 게임이있는 사용자 지정 구성 요소 가 있습니다. 게임 구성 요소 내에 변경 게임이있는 경우 myBox에 경고 메시지를 표시하려면 어떻게해야합니까? rxJS 방법이 있다는 것을 이해합니다. 순전히 각도로 수행 할 수 있습니까? 내 JSFiddle

자바 스크립트

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-->

답변:


157

감시자 없이 구성 요소 작성

이 답변은 감시자 를 사용하지 않고 AngularJS 1.5 구성 요소를 작성하는 데 사용하는 5 가지 기술을 설명 합니다.


ng-change지시어 사용

AngularJs2를 준비 할 때 watch를 사용하지 않고 obj 상태 변경을 관찰 할 수있는 대체 메서드는 무엇입니까?

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 명에 이르면 UI가 느려집니다. 각도 2의 전략 프레임 워크 반응성과 피할 배치하는 것입니다 $watch에를 $scope.


사용 $onChanges수명주기 후크를

으로 버전 1.5.3 , AngularJS와 추가 된 $onChanges받는 라이프 사이클 후크 $compile서비스를.

문서에서 :

컨트롤러는 수명주기 후크 역할을하는 다음 메서드를 제공 할 수 있습니다.

  • $ onChanges (changesObj)-단방향 ( <) 또는 보간 ( @) 바인딩이 업데이트 될 때마다 호출 됩니다. 는 changesObj그 키 변경된 결합 속성의 이름은 해쉬이고, 값은 형태의 목적이다 { currentValue: ..., previousValue: ... }. 이 후크를 사용하여 바인딩 된 값 복제와 같은 구성 요소 내에서 업데이트를 트리거하여 외부 값의 우발적 변형을 방지합니다.

— AngularJS 포괄적 인 지시어 API 참조-수명주기 후크

$onChanges후크와 성분으로 외부 변화에 반응하는 데 사용되는 <일방향 바인딩. 이 ng-change지시문은 바인딩을 사용 ng-model하여 구성 요소 외부의 컨트롤러 에서 변경 사항을 전파하는 데 사용됩니다 &.


사용 $doCheck수명주기 후크를

으로 버전 1.5.8 , AngularJS와 추가 된 $doCheck받는 라이프 사이클 후크 $compile서비스를.

문서에서 :

컨트롤러는 수명주기 후크 역할을하는 다음 메서드를 제공 할 수 있습니다.

  • $doCheck()-다이제스트 사이클의 매 턴마다 호출됩니다. 변경 사항을 감지하고 조치를 취할 수있는 기회를 제공합니다. 감지 한 변경 사항에 대한 응답으로 수행하려는 조치는이 후크에서 호출되어야합니다. 이 구현은를 $onChanges호출 하는시기에 영향을주지 않습니다 . 예를 들어,이 후크는 깊은 동등성 검사를 수행하거나 Angular의 변경 탐지기에 의해 감지되지 않아 트리거되지 않는 Date 객체를 확인하려는 경우 유용 할 수 있습니다 $onChanges. 이 후크는 인수없이 호출됩니다. 변경 사항을 감지하는 경우 현재 값과 비교하기 위해 이전 값을 저장해야합니다.

— AngularJS 포괄적 인 지시어 API 참조-수명주기 후크


구성 요소 간 통신 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 개발자 가이드-Intercomponent Communicatation을 참조하십시오.


RxJS 를 사용하여 서비스에서 값 푸시

예를 들어 상태를 유지하는 서비스가있는 상황에서는 어떻습니까? 변경 사항을 해당 서비스에 푸시하고 페이지의 다른 임의의 구성 요소가 이러한 변경 사항을 인식하는 방법은 무엇입니까? 최근이 문제를 해결하는 데 어려움을 겪고 있습니다.

Angular 용 RxJS Extensions 로 서비스를 구축합니다 .

<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.

PLNKR데모 .


감사합니다. game.gameChange가 아닌 myBox.game의 변경에 반응하려고합니다. 이것은 입력이 아니라 레이블이므로 위의 내용이 작동하지 않을 수 있습니다. 결국 rxjs에 의지해야 할 것 같습니다 ...
Ka Tech

이벤트를 상위 구성 요소에 전파하기위한 정보를 추가했습니다.
georgeawg

이것이 myBox.game변수 에 대한 프로그래밍 방식 값 변경을 어떻게 처리 할까요?
Pankaj Parkar

좋은 대답 @georgeawg. Service예를 들어 보류 상태 인 상황에서는 어떻습니까? 변경 사항을 해당 서비스에 푸시하고 페이지의 다른 임의의 구성 요소가 이러한 변경 사항을 인식하는 방법은 무엇입니까? 최근에이 문제를 해결하는 데 어려움을 겪고 있습니다 ...
Mark Pieszak-Trilon.io

1
좋은 대답은 rx.Subject () 대신 rx.BehaviorSubject ()를 사용하여 RxJS 솔루션을 개선 할 수 있다고 말하면됩니다. rx.Subject ()는 그 자체로 마지막 값을 저장하는 작업을 수행하고 사용자가 한 것처럼 기본값으로 초기화 할 수 있습니다. 당신의 서비스에서
Manuel

8

$watch개체는 개체 내부에서 사용할 수 $scope있으므로 $scope컨트롤러 팩토리 함수 내부 에 추가 한 다음 변수 위에 감시자를 배치해야합니다.

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

여기 데모

참고 : 당신이 변환 한 알 directivecomponent의 의존성을 제거하기 위해, $scope당신은 한 걸음 더 가까이 Angular2을 얻을 수 있도록합니다. 그러나이 경우에는 제거되지 않은 것 같습니다.

최신 정보

기본적으로 각도 1.5는 .component두 가지 다른 기능을 차별화하는 방법을 추가했습니다 . 마찬가지로 component.stands는 추가 특정 behaviby을 수행하기 selector로하는 경우, directive스탠드 DOM에 특정 동작을 추가 할 수 있습니다. Directive는 .directiveDDO (Directive Definition Object)의 래퍼 방법 일뿐 입니다. 당신이 볼 수있는 것은 각도 컴파일 된 DOM을 얻을 수있는 방법 link/compile을 사용 .component하는 동안 제거 기능 이 있다는 것입니다.

Angular 컴포넌트 라이프 사이클 후크의 $onChanges/ $doCheck라이프 사이클 후크를 사용하십시오 . Angular 1.5.3+ 버전 이후에 사용할 수 있습니다.

$ onChanges (changesObj) -바인딩이 업데이트 될 때마다 호출됩니다. changesObj는 키가 바인딩 된 속성의 이름 인 해시입니다.

$ doCheck () -바인딩이 변경 될 때 다이제스트주기의 각 턴에 호출됩니다. 변경 사항을 감지하고 조치를 취할 수있는 기회를 제공합니다.

구성 요소 내부에서 동일한 기능을 사용하면 코드가 Angular 2로 이동할 수 있도록 호환됩니다.


고마워요, 이제 이걸 고수 할 것 같아요. AngularJs2를 준비 할 때 watch를 사용하지 않고 obj 상태 변경을 관찰하는 데 사용할 수있는 alt 메서드에 대해 설명하는 링크를 알려줄 수 있습니까?
Ka Tech

1
지금은 사용 말할 수 @KaTech observable의를 RxJS호환 좋은 것 즉,Angular2
카지 Parkar

4

내 솔루션에 관심이있는 사람이라면 누구나 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);
      }
    }
  }])

2

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>

0

IE11, MutationObserver https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver 에서 사용 가능합니다 . DOM / 컨트롤러 분리를 반 중단하는 $ element 서비스를 컨트롤러에 삽입해야하지만, 이것이 angularjs의 근본적인 예외 (즉, 결함)라고 생각합니다. hide / show는 비동기 적이므로 angularjs 및 angular-bootstrap-tab이 제공하지 않는 on-show 콜백이 필요합니다. 또한 관찰하려는 특정 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']
})

쉽게 피할 사용 Angular2 +로 마이그레이션을 만들려면 $scope$rootScope. 이벤트 이미 터 및 구독자 대신 RxJS 사용을 고려하십시오 .
georgeawg

0

정말 좋은 대답을 받아 들였지만 이벤트의 힘을 사용할 수도 있다고 덧붙일 수 있습니다 (Qt 신호 / 슬롯에서와 비슷합니다).

이벤트는 $rootScope.$broadcast("clickRow", rowId) 부모 (또는 자식 컨트롤러)에 의해 브로드 캐스팅 됩니다. 그런 다음 컨트롤러에서 다음과 같이 이벤트를 처리 할 수 ​​있습니다.

$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;
      });
    }
});

2
각도와 2 + 이벤트 버스는 + 쉽게 Angular2에 피할 사용하여 마이그레이션하려면 (. 이벤트 버스 성능 문제가) 멀리하려고 $scope하고 $rootScope. 이벤트 이미 터 및 구독자 대신 RxJS 사용을 고려하십시오 .
georgeawg

0

나는 늦었다. 그러나 그것은 다른 사람들에게 도움이 될 수 있습니다.

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
        });
    }]
});

이것이 왜 작동하는지 설명 할 수 있습니까? rootscope를 남용하는 것은 나쁜 생각이 아닙니까?
mix3d
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.