Angualr Scope란?

Scope란 무엇인가? 

Scope는 어플리케이션 모델을 참조하고 있는 객체를 말한다.
이것은 expression을 위한 컨텍스트를 실행하게 된다.
Scope는 DOM 구조와 닮은 계층적 구조로 나열되어 있다.
Scopes는 expressions와 이벤트 전이를 watch할 수 있다.

Scope의 특징 : 

Scopes는 $watch API를 제공하며 이는 모델의 변경을 감시한다.
Scopes는 $apply API를 제공하며, Angular왕국 (controllers, services, Angular event handlers)와 같은 외부로 부터 발생된 변경사항을 부로 반영한다.

Scope는 공유된 모델 속성에 접근을 제공하는 반면 어플리케이션 컴포넌트의 속성에 접근하기 위한 중첩된 접근 제한도 될 수 있다. 중첩된 scope는 "child scopes" 혹은 "isolate scopes"들이며, "child scope" 부모 스콥으로부터 속성을 상속 받는다. "isolate scope"는 그렇지 못하다.

Scope는 evaluated된 expression에 대한 컨텍스트를 제공한다. 예를 들어 {{username}}표현식은 username 속성이 정의된 특정 스콥에 대한 evaluate가 없다면 무의미한 것이된다.

Data-Model로서의 Scope : 

Scope는 어플리케이션의 컨트롤러와 view를 서로 접착하는 접착제이다. template 링킹 단계동안 directives는 scope에서 $watch 표현식을 설정한다. $watch는 속성의 변경에 대한 알림이 되도록 지시할 수 있다. 이것은 DOM 값을 업데이트 하게 된다.

컨트롤러와 디렉티브는 scope에 대한 참조를 가진다. 그러나 서로는 참조할수 없다. 이러한 고립정책은 DOM으로 부터 controller을 분리 시킨다.
이것은 매우 중요한 포인트로 컨트롤러는 뷰가 필요 없게 하고, 뷰는 컨트롤러가 필요없게 만들어 주어 어플리케이션 스토리의 테스트를 매우 향상 시켜준다.

------------------------------------------------------------------
angular.module('scopeExample', [])
.controller('MyController', ['$scope', function($scope) {
  $scope.username = 'World';

  $scope.sayHello = function() {
    $scope.greeting = 'Hello ' + $scope.username + '!';
  };
}]);
------------------------------------------------------------------


------------------------------------------------------------------
<div ng-controller="MyController">
  Your name:
    <input type="text" ng-model="username">
    <button ng-click='sayHello()'>greet</button>
  <hr>
  {{greeting}}
</div>
------------------------------------------------------------------

이 예제는 MyController에 'World'를 할당하고 scope의 username에 할당한다. scope는 할당된 입력값을 알리고, "Hello"와 결합하여 렌더링 한다. 이 예제는 어떻게 컨트롤러가 데이터를 scope에 쓸수 있는지 보여주는 좋은 예이다.

유사하게 controller는 sayHello메소드에서 본것과 같이 행위를 할당할 수 도 있다. 이것은 사용자가 "greet"를 클릭하는 경우 수행될 수 있도록 한다. sayHello메소드는 username속성을 읽고 greeting 속성을 생성한다. 이 예제는 scope 의 속성값을 자동적으로 업데이트 하도록 HTML입력 위젯에 바인딩 하는 내용을 보여준다.

{{greeing}}에 포함된 로직은 다음과 같다.
- 템플릿의 DOM노드에서 {{greeting}}으로 정의된 scope를 탐색한다. 이 예제에서는 MyController에서 scope에 지정되어 있다. 이 scope의 게층구조에 대해서는 이후에 설명하도록 하자.
- greeting표현식을 Evaluate한다. 이는 상단 스콥을 탐색하여, 결과값을 DOM엘리먼트에 반영한다.

scope내에 속성값에 데이터는 view에 렌더링 된다고 생각할 수 있다. scope는 모든 관련된 뷰에서singlesource-of-truth이다.

뷰의 관점에서 테스트를 위해서 컨트롤러의 분리는 매우 필요한 것이다. 왜냐하면 뷰에 집중하도록 하여 컨트롤러가 함께 있는 산만함을 없애준다.

------------------------------------------------------------------
it('should say hello', function() {
  var scopeMock = {};
  var cntl = new MyController(scopeMock);

  // Assert that username is pre-filled
  expect(scopeMock.username).toEqual('World');

  // Assert that we read new username and greet
  scopeMock.username = 'angular';
  scopeMock.sayHello();
  expect(scopeMock.greeting).toEqual('Hello angular!');
});
------------------------------------------------------------------

Scope Hierarchies

각 Angular 어플리케이션은 하나의  scope를 가진다. 그러나 몇개의 자식 scope들을 가질 것이다.
어플리케이션은 복수개의 스콥을 가질 수 있다. 왜냐하면 directives는 새로운 자식 scope를 생성하기 때문이다. (directive 는 새로운 스콥을 생성한다. ) 새로운 스콥이 생성되면, 그들의 부모 scope의 자식으로 추가된다. 이 생성트리는 DOM과 같이 병행하여 추가된다.

Angular가 {{name}}를 evaluate할때 name속성으로 주어진 엘리먼트와 연결된 scope를 우선 찾는다. 만약 properties가 존재하지 않는다면 부모 스콥을 찾게 되며, root에 다다를때까지 이러한 행위가 반복된다. JavaScript에서 이 행위는 prototypical inheritance로 알려져 있다. 자식의 스콥은 prototypically 상속을 자신의 부모로 부터 받는다.

다음 예제는 어플리케이션에서 scopes에 대한 일러스트를 보여준다. 그리고 프로퍼티의 prototypical inheritance 를 보여준다. 이 예제는 스콥의 영역을 보여주고 있다.


------------------------------------------------------------------
<div class="show-scope-demo">
  <div ng-controller="GreetController">
    Hello {{name}}!
  </div>
  <div ng-controller="ListController">
    <ol>
      <li ng-repeat="name in names">{{name}} from {{department}}</li>
    </ol>
  </div>
</div>
------------------------------------------------------------------


------------------------------------------------------------------
angular.module('scopeExample', [])
.controller('GreetController', ['$scope', '$rootScope', function($scope, $rootScope) {
  $scope.name = 'World';
  $rootScope.department = 'Angular';
}])
.controller('ListController', ['$scope', function($scope) {
  $scope.names = ['Igor', 'Misko', 'Vojta'];
}]);
------------------------------------------------------------------


------------------------------------------------------------------
.show-scope-demo.ng-scope,
.show-scope-demo .ng-scope  {
  border: 1px solid red;
  margin: 3px;
}
------------------------------------------------------------------

Hello World!
  1. Igor from Angular
  2. Misko from Angular
  3. Vojta from Angular

Angular는 자동적으로  ng-scope 클래스를 scope가 추가된 엘리먼트의 클래스에 위치시킨다. <style> 로 정의된 곳은 (붉은색 표시) 신규 scope가 위치된 영역이다. 이 자식 스콥들이 필요한 이유는 repeater가 {{name}}표현식을 검증하기 때문이다. 그러나 각 scope 표현은 서로다른 결과를 나타낸다. {{department}}의 검증은 root scope로 부터 prototypically inherit 되어진다. 이것은 오직 department 속성만 존재하기 때문이다.

DOM으로 부터 Scopes 순회하기 

Scopes는 DOM에 $scope 데이터 속성으로 추가된다. 그리고 디버깅 목적으로 순회하게 된다. rootScope는 DOM중에서 ng-app 디렉티브의 위치에 정의된다. 일반적으로 ng-app는 <html> 엘리먼트에 위치한다. 그러나 다른 엘리먼트에도 들어갈 수 있다. 예를 들면 Angular에 의해서한 부분만 제어되기를 원하는 경우가 그렇다.

디버거에서 scope 테스트 :
1. 브라우저에서 원하는 엘리먼트에서 오른쪽 클릭을 하여 'inspect element'를 선택한다. 그러면 브라우저 디버거가 열리고 원하는 위치가 하이라이트 된다.
2. 현재 선택된 엘리먼트에서 $0 값을 콘솔에 입력하여 디버거에 접근한다.
3. 콘솔에서 angular.element($0).scope()로 해당 $scope에 접근할 수 있다.

Scope Events Propagation

scope는 DOM 이벤트와 같은 방법으로 이벤트를 전파할 수 있다. 이벤트는 scope의 자식으로 broadcast 할수 있고, 자식에서 부모로 emitted 할 수 있다.
------------------------------------------------------------------
angular.module('eventExample', [])
.controller('EventController', ['$scope', function($scope) {
  $scope.count = 0;
  $scope.$on('MyEvent', function() {
    $scope.count++;
  });
}]);

------------------------------------------------------------------


------------------------------------------------------------------
<div ng-controller="EventController">
  Root scope <tt>MyEvent</tt> count: {{count}}
  <ul>
    <li ng-repeat="i in [1]" ng-controller="EventController">
      <button ng-click="$emit('MyEvent')">$emit('MyEvent')</button>
      <button ng-click="$broadcast('MyEvent')">$broadcast('MyEvent')</button>
      <br>
      Middle scope <tt>MyEvent</tt> count: {{count}}
      <ul>
        <li ng-repeat="item in [1, 2]" ng-controller="EventController">
          Leaf scope <tt>MyEvent</tt> count: {{count}}
        </li>
      </ul>
    </li>
  </ul>
</div>
------------------------------------------------------------------

Scope Life Cycle

일반적인 브라우저가 이벤트를 수신하는 플로우는 자바스크립트의 콜백에 대한 실행을 수행하는 것이다. 콜백이 완료되면 브라우저는 DOM을 다시 렌더링하고 추가적인 이벤트를 기다리기 위해 return한다. 

브라우저가 자바스크립트 코드를 호출할때 Angular 외부의 컨텍스트를 수행하면, 이것은 Angular가 모델의 변경사항을 감지하지 못한다는 의미이다. 모델의 변경을 수행하기 위해서는 $apply를 수행하여 Angular가 컨텍스트를 실행할 수 있도록 해주어야 한다. 오직 모델의 변경은 $apply 메소드 내부에서 수행된 경우에만 Angular에 의해서 처리될 수 있다. 예를 들어 DOM 이벤트를 리슨하는 디렉티브 ng-click와 같은 디렉티브는 $apply 메소드의 내부에서 evaluation이 수행되어야 한다. 

expression이 evaluating이 두행되고 난 뒤에는 $apply메소드는 $digest를 수행한다. $digest 단계에서scope는 $watch 익스프레션의 전체를 검증하고 이전 값과 비교를 수행한다. dirty checking은 비동기적으로 끝이난다. 이 의미는 $scope.username="angulat"과 같이 할당한 값이 $watch 에 의해서 즉시 노티되지 않는다는 것을 의미한다. 대신에 $watch 노티는 다음 $digest단계까지 딜레이 된다. 이 딜레이는 $watch가 수행되는 동안 다른 $watch가 수행되지 않는다는 것을 보장하는 것이다. 만약 $watch가 모델의 값을 변경하면 이것은 강제적으로 $digest사이클을 수행한다.

1. Creation : 
  root scope가 $injector에 의해서 어플리케이션 bootstrap 동안 생성된다. 템플릿 링킹동안 몇가지 디렉티브들은 새로운 child scope들을 생성한다. 

2. Watcher registration : 
  템플릿 링킹동안 디렉티브는 scope에 watches를 등록한다. 이 watches는 모델을 DOM으로 전파할때 이용된다. 

3. Model mutation : 
  모델 변경이 적합하게 관찰되기 위해서는 scope.$apply()에서 처리되어야한다. Angular API는 명시적으로 수행한다. 추가적으로 $apply는 controller에서는 동기적으로 수행되어야 하며, $http, $timeout혹은 $interval서비스에서는 비동기적으로 수행된다. 

4. Mutation ovservation : 
  $apply의 끝에서는 Angular는 $digest root scope에서 사이클을 수행한다. 이것은 모든 자식 scope에 전달된다. $digest 사이클동안 모든 $watch 표현 혹은 함수들은 모델의 변경에 대한 체크가 수행되며, 만약 변경이 발견되면 $watch listener는 호추된다. 

5. Scope destruction : 
  자식 scope가 더이상 필요하지 않은경우 자식 scope의 생성자는 삭제된다. 이것은 scope.$destroy() API를 통해 수행되며 이것은 $digest이 자식 scope를 호출하는 것을 멈추고 gabage collector에 의해서 재생되기를 기다린다. 

Scopes and Directives : 

compilation 단계동안 compiler는 DOM템플릿에 대한 디렉티브 매치를 수행한다. 디렉티브는 보통 2개의 카테고리에 속한다.
- Ovserving directives : {{ }} 과 같은 표현식으로, register listener는  $watch()메소드를 사용한다. 이 디렉티브 타입은 표현식이 변경되는 경우 노티가 수행되며 이것은 view를 업데이트 할 수 있게 된다.
- Listener directives : ng-click와 같은 것으로 등록된 리스너는 해당 이벤트가 fire된경우 연관된 표션식을 수행하고, $apply()메소드를 이용하여 뷰를 업데이트 한다.

외부 이벤트가 수신되면, (사용자 액션 혹은 XHR) 연관된 expression은 $apply메소드를 통해서 적용되어야 하며 이것은 모든 리스너가 정확히 업데이트 된다는 것을 의미한다.

Directives that Create Scopes 

대부분의 케이스에서 directive와 scope는 상호작용 한다. 그러나 scope의 새로운 인스턴스를 생성하지 않는다. 그러나 몇몇 directive는 ng-controller혹은 ng-repeat는 새로운 자식 scope를 생성하고, 이 자식 스코프를 DOM에 추가한다.
사용자는 angular.element(aDomElement).scope() 메소드를 이용하여 스콥을 순회할 수 있다.
directive guide를 참조하여 추가적인 정보를 획득하고, isolate scope에 대해서 확인하자.

Controllers and Scopes

Scopes와 Controllers 는 다음 상황에서 각각 상호작용 한다.
- Controller가 scope를 사용하여 컨트롤러 메소드를 템플릿에 노출하고자 하는경우
- Controller 정의 메소드가 Model을 변경하는 경우 (scope의 속성..)
- Controller가 모델에서 watches를 등록하는 경우, 이 watch는 컨트롤러가 특정행위를 수행하면 즉시 실행한다.

ng-controller파트를 추가로 확인하자.

Scope $watch Performance Considerations

특정 속성의 소콥에 대한 변경에 대해서 Dirty checking 은 Angular에서 공통적인 오퍼레이션이다. 이것은 dirty checking은 효과적으로 수행되어야 함을 의미한다. dirty checking함수는 어떠한 DOM접근도 하지 않도록 해야한다. DOM접근은 자바스크립트 객체에서 속성 접근보더 더 느린 순으로 접근이 된다.

Scope $watch Depths

Dirty checking는 3가지 전략으로 수행될 수 있다.
By reference
By collection contents
By Value

이 3가지 전략은 그들이 검출하고 성능적인 특징이 따라 종류가 달라진다.

- Watch by reference (scope.$watch (watchExpression, listener)) 은 다음과 같이 변경을 검출한다. 전체 값이 watch expression 이 새로운 값으로 변경되어 반환된 경우에 검출이 된다. 만약 값이 배열 혹은 객체이면, 내부 변경은 검출되지 않는다. 이것은 가장 효율적인 전략이다.

- Watch collection contents (scope.$watchCollection(watchExpression, listener))은 변경을 다음과 같이 검출한다. 배열 혹은 객체 내부의 변경이 발생하는 경우 : 아이템이 추가, 삭제, 재배열되어지는 경우에 처리된다. 검출은 shallow하게 처리된다. 이것은 중쳑된 컬렉션에서는 많지 않다. 컬렉션 컨텐츠의 watching은 watch by reference보다는 더 비용이 든다. 왜냐하면 컬렉션 컨텐츠가 관리되기 위해서 복사 되기 때문이다. 그라나 이 전략은 복사를 최소한으로 수행한다.

- Watch by value (scope.$watch( watchExpression, listener, true)) 는 임의의 중첩된 데이터 구조의 변경을 검출한다. 이것은 가장 강력한 변경 감지 전략이다. 그러나 가장 비용이 비싸다. 전체 충접 구조의 데이터 구조를 탐색하며, 각 다이제스트에서 수행되어 져야한다. 그리고 전체 복사가 메모리 내에서 이루어져야 한다.

Integration with the browser event loop

아래 나열된 다이어그램과 예제는 어떻게 Angular가 브라우저의 event loop와 상호작용하는지 보여준다.

1. 브라우저 이벤트 루프는 event가 도착하기를 대기한다. event는 사용자 인터렉션, 타이머 이벤트, 혹은 네트워크 이벤트이다. (네트워크 이벤트 = 서버로 부터 결과 전송)

2. 이벤트의 콜백은 수행된다. 이것은 자바스크립트 컨텍스트에 들어간다. 콜백은 DOM구조를 변경할 수 있다.

3. 콜백이 수행되면 브라우저는 JavaScript 컨텍스트를 버리고, 다시 DOM의 변경을 기반으로 뷰를 다시 그린다.

Angular는 일반적인 JavaScript를 변경하며 이는 주어진 이벤트 프로세싱 루프에 의해서 수행된다. 이것은 자바스크립트를 전통적인 방법에서 Angular 수행방식을 분리한다. 오직 Angular 실행 컨텍스트에서만 오퍼레이션이 적용되며, 이것은 Angular data-binding, exeception handling, property watching등으로 부터 수행된다. 또한 $apply를 이용할 수 있으며 이는 JavaScript로 부터 Angular excution으로 들어갈 수 있도록 해준다. 염두해야할 것은 대부분의 장소(controller, service)에서 $apply는 이미 디렉티브에 의해서 호출되어지며 이는 이벤트 핸들링을 수행한다. $apply를 명시적으로 호출하는 것은 custom이벤트 콜백을 구현할때, 혹은 서드파티 라이브러리 콜백과 함께 수행되어야 할때 적용된다.


1. Angular가 scope.$apply(stimulusFn) 호출에 의해서 컨텍스트 수행에 들어갈때 stimulusFn은 Angular에서 수행되고자 하는 작업을 의미한다.

2. Angular는 stimulusFn()을 실행하고 이는 보통 어플리케이션의 상태를 변경한다.

3. Angular는 $digest루프에 들어간다. loop는 $evalAsync큐와 $watch리스트 처리를 수행하도록 만들어 졌다. $digest루프는 모델이 안정화 될때까지 반복한다. 이의미는 $evalAsync 큐가 비고 $watch리스트가 더이상 변경을 감지할 수 없을때 까지이다.

4. $evalAsync큐는 현재 스택 프레임의 외부에서 발생이 필요한 스케쥴 작업에 이용된다. 그러나 브라우저 뷰가 렌더링 하기전에 수행된다. 이것은 보통 setTimeout(0)으로 완료된다. 그러나 setTimeout(0) 접근은 느리고 많은 view 플리킹을 수반한다.

5. $watch리스트는 지난 반복의 변경된 표현식의 셋이다. 만약 변경이 검출되면 $watch함수는 새로운 값으로 DOM의 값을 변경한다.

6. $digest루프가 끝이나면 실행은 Angular와 JavaScript 컨텍스트로 벗어난다. 이것은 다른 변경으로 인해서 DOM이 다시 렌더링 된 다음에 이어진다.

다음은 어떻게 Hello world 예제가 사용자의 입력으로 인해서 데이터 바인딩을 수행하는지 설명한다.
1. 컴파일 단계동안 :
    1. ng-model과 input directive는 keydown을 셋업하고 <input>컨트롤에 리슪ㄴ다.
    2. interpolation은 $watch를 셋업하여 name의 변경을 알림받는다.

2. runtime단계 동안 :
    1. 'X'키가 브라우저에서 눌려졌고 키다운 이벤트가 입력 컨트롤러에서 발생하였다.
    2. input디렉티브는 입력값의 변경을 캡쳐하고, $apply("name = 'X';")를 호출하여 Angular 실행 컨텍스트 내부의 어플케이션 모델을 변경한다.
    3. Angular는 name = 'X'; 를 모델에 적용한다.
    4. $digest루프가 시작된다.
    5. $watch 리스트가 name속성의 변경을 검출하고 interpolation에 알린다. 이것은 DOM업데이트를 수행하게 된다.
    6. Angular는 존재하는 컨텍스가 있으며, interpolation을 검증한다. 이것은 DOM을 수행한다.
    7. 브라우저는 변경된 텍스트로 부라우저를 다시 그린다.










from : https://docs.angularjs.org/guide/scope





AngularJS Concept

AngularJS 사이트에서 AngularJS에 대한 개념 설명을 정리하고자 한다.

용어 :


ConceptDescription
Template추가적인 마크업을 포함한 HTML
Directives커스텀 속성(attributes)와 엘리먼트(elements)를 말하며 HTML을 확장한다. 
Modelview에서 사용자에게 보여주고자 하는 데이터이며, 사용자와 인터렉션을 하는 대상 객체이다. 
Scope컨트롤러, 디렉티브, 표현식 등이 접근할 수 있는 컨텍스트를 말하며, 이는 모델이 저장되는 곳이다.
Expressionsscope로 부터 변수 혹은 함수(functions)에 접근한다. 
Compiler템플릿을 파싱하고, 초기화 하는 명령어와 표현식을 초기화 한다. 
Filter사용자에게 노출되기 위해서 표현식의 값을 포매팅한다. 
View사용자가 보게될 내용 
Data Binding모델과 뷰사이에 데이터를 싱크한다. 
Controller뷰 뒷단에 있는 비즈니스 로직이다. 
Dependency Injection객체와 함수(functions)를 생성하고 엮어준다. 
Injector의존성을 인젝션하는 컨테이너이다. 
Module컨트롤러, 서비스, 필더, 디렉티브를 포함하는 app의 서로다른 부분에 해당하는 컨테이너이다. 이것은 Injector에 의해서 설정된다. 
Service재사용가능한 비즈니스 로직으로 views에 독립적인 객체이다. 

데이터 바인딩 : 

다음 예제는 수량과 가격을 계산하는 예제로 각 통화에 따라서 값을 노출하는 예이다.

--------------------------------------------------------------------------------------------
<div ng-app ng-init="qty=1;cost=2">
  <b>Invoice:</b>
  <div>
    Quantity: <input type="number" min="0" ng-model="qty">
  </div>
  <div>
    Costs: <input type="number" min="0" ng-model="cost">
  </div>
  <div>
    <b>Total:</b> {{qty * cost | currency}}
  </div>
</div>
--------------------------------------------------------------------------------------------

Invoice:
Quantity: 
Costs: 
Total: $6.00

샘플 예제 : https://docs.angularjs.org/guide/concepts#template



Template : 상단 코드의 HTML영역을 Template라고 부른다. 
Compiler : Angular가 시작되면, 상기 마크업을 파싱하고 처리하는 역할을 한다. 
View : 로드되고 변환되고 DOM으로 렌더링 된 것을 view라고 부른다. 
Directives : 상기 코드에서 ng-app등과 같은 것을 directive라고 부른다. 이는 특정 행위를 수행하며 이는 attributes나 elements에 적용이 된다. 

상기 예제는 ng-app 속성에 해당하는 directive를 이용하고 있으며 어플리케이션의 초기화를 자동으로 수행하는 역할을 한다. 

input 엘리먼트에 있는 ng-model 디렉티브는 입력된 값을 저장하고 업데이트 하는 역할을 한다. 

Custom directives to access the DOM:
DOM에 접근하는 Custom directives : Angular에서는 오직 directive를 통해서만 DOM에 접근하도록 하고 있다. 이것은 매우 중요한 포인트로 DOM에 직접 접근하는 것은 매우 테스트하기가 어렵게 만든다. 만약 DOM에 직접 접근하고자 한다면 이것을 위한 Custom directive를 작성하는 것을 권고한다. 
{{ expression | filter }} 로 두겹의 {혹은 } 를 사용하여 데이터를 표시하고 있다. 이것은 마크업의 값을 평가할때(evaluate) 해당 값을 교체하게 된다.
Expression 은 템플릿에서 JavaScript와 같은 코드를 작성할 수 있는 것으로, Angular가 값을 읽고 쓸 수 있도록 해준다. 

이 변수값은 global값이 아니다. JavaScript에서와 같이 scope내에서 유효한 값이된다. 
Scope는 Angular에서 값의 범위를 제공하며, 이는 expression을 위한 접근가능한 범위이다. 

값은 scope에 저장되며, 이것은 model에서 접근된다. 
상단 예제는 입력 필드에서 들어온 값을 서로 곱한 값을 저장하고 보여준다. 

Filter는 사용자에게 보여줄 표현식의 데이터를 포매팅하는 것이다. 상기 예제에서는  currency 포맷을 이용하여 금액을 표시하고 있다. 

상기 예제에서 중요한 것은 Angular가 live binding을 제공한다는 것이다. 입력값이 변경되면 표현식의 값이 자동적으로 재 계산되며, 이 값은 계산된 값으로 DOM에 반영된다. 

이 개념은 two-way data binding이라고 한다. 
One-way data binding : 
  기존의 템플릿을 이용한 방식 JSTL, FreeMarker 등과 같이 서버에서 모델과 템플릿을 조합하여 최종 결과를 클라이언트에 전송하여 브라우저가 렌더링 하는 방식
Two-way data binding : 
  템플릿이 라이브 View로 컴파일되고, 뷰의 변경사항이 모델에 반영되고, 모델의 변경사항이 View에 즉각 변경되는 형태를 말한다. 

Controller : 

상기 예제에서 추가적인 로직을 넣어보자. 이는 서로다른 통화에서 가격을 계산하고, 주문을 결제하는 내용을 담고 있다.

------------------------------------------------------------------
angular.module('invoice1', [])
.controller('InvoiceController', function() {
  this.qty = 1;
  this.cost = 2;
  this.inCurr = 'EUR';
  this.currencies = ['USD', 'EUR', 'CNY'];
  this.usdToForeignRates = {
    USD: 1,
    EUR: 0.74,
    CNY: 6.09
  };

  this.total = function total(outCurr) {
    return this.convertCurrency(this.qty * this.cost, this.inCurr, outCurr);
  };
  this.convertCurrency = function convertCurrency(amount, inCurr, outCurr) {
    return amount * this.usdToForeignRates[outCurr] / this.usdToForeignRates[inCurr];
  };
  this.pay = function pay() {
    window.alert("Thanks!");
  };

});

------------------------------------------------------------------


------------------------------------------------------------------
<div ng-app="invoice1" ng-controller="InvoiceController as invoice">
  <b>Invoice:</b>
  <div>
    Quantity: <input type="number" min="0" ng-model="invoice.qty" required >
  </div>
  <div>
    Costs: <input type="number" min="0" ng-model="invoice.cost" required >
    <select ng-model="invoice.inCurr">
      <option ng-repeat="c in invoice.currencies">{{c}}</option>
    </select>
  </div>
  <div>
    <b>Total:</b>
    <span ng-repeat="c in invoice.currencies">
      {{invoice.total(c) | currency:c}}
    </span>
    <button class="btn" ng-click="invoice.pay()">Pay</button>
  </div>

</div>

------------------------------------------------------------------


Invoice:
Quantity: 
Costs:  
Total: USD2.70 EUR2.00 CNY16.46 



Controller : 상기 코드를 보면 controller이라는 새로운 자바 스크립트 코드가 들어 있다. 
이는 실제 controller인스턴스를 생성하는 코드이며, 컨트롤러의 목적은 값을 노출하고, expression과 directive에 기능을 추가하는 역할을 한다. 

컨트롤러를 생성하고나면 이를 사용하기 위해서는 HTML에 ng-controller 디렉티브를 추가하여 사용할 수 있다. 이 디렉티브는 Angular에 InvoiceController가 디렉티브의 엘리먼트와 그 하위의 엘리먼트 전체에대한 처리를 책임진다는 의미이다. 

문법은 InvoiceController as invoice 로 Angular에게 컨트롤러의 인스턴스와 이 인스턴를 저장하고 있는 invoice 변수를 현재 scope에 지정하였다는 것을 알려준다. 

이후에는 invoice. 이라는 프리픽스를 붙여서 컨트롤러 인스턴스에 접근할 수 있다. 

ng-repeat라는 디렉티브를 이용하여 Currency 리스트를 view에 노출할 수 있다. 

상기에 저정된 total 함수는 {{ invoice.total(...) }} 의 형태로 처리결과를 화면에 노출할 수 있다. 

버튼의 경우 ngClick를 이용하여 해당 이벤트를 기동할 수 있도록 한다. 이것은 버튼이 클릭된경우 해당하는 표현식을 검증하는 방식으로 수행된다. 

새로운 자바 스크립트 파일은 module를 생성하고 있다. 이것은 컨트롤러를 등록하는 역할을 수행하고 있다. 

Service :

InvoiceController 는 모든 예제를 포함하고 있다. 그러나 어플리케이션이 확장되고 커지면 view에 독립적인 모델로 Controller에서 부터 로직 부분을 Service로 떼어내는 것이 좋다. 
이를 통해서 다른 파트에서 동일한 처리를 하는 Service를 재사용 가능하게 된다. 
이후 우리가 가격계산하는 로직이 변경되는 경우 Controller의 변경없이 Service의 변경만으로 원하는 작업을 수행할 수 있다.


------------------------------------------------------------------
angular.module('finance2', [])
.factory('currencyConverter', function() {
  var currencies = ['USD', 'EUR', 'CNY'];
  var usdToForeignRates = {
    USD: 1,
    EUR: 0.74,
    CNY: 6.09
  };
  var convert = function (amount, inCurr, outCurr) {
    return amount * usdToForeignRates[outCurr] / usdToForeignRates[inCurr];
  };

  return {
    currencies: currencies,
    convert: convert
  };
});
------------------------------------------------------------------


------------------------------------------------------------------
angular.module('invoice2', ['finance2'])
.controller('InvoiceController', ['currencyConverter', function(currencyConverter) {
  this.qty = 1;
  this.cost = 2;
  this.inCurr = 'EUR';
  this.currencies = currencyConverter.currencies;

  this.total = function total(outCurr) {
    return currencyConverter.convert(this.qty * this.cost, this.inCurr, outCurr);
  };
  this.pay = function pay() {
    window.alert("Thanks!");
  };
}]);
------------------------------------------------------------------


------------------------------------------------------------------
<div ng-app="invoice2" ng-controller="InvoiceController as invoice">
  <b>Invoice:</b>
  <div>
    Quantity: <input type="number" min="0" ng-model="invoice.qty" required >
  </div>
  <div>
    Costs: <input type="number" min="0" ng-model="invoice.cost" required >
    <select ng-model="invoice.inCurr">
      <option ng-repeat="c in invoice.currencies">{{c}}</option>
    </select>
  </div>
  <div>
    <b>Total:</b>
    <span ng-repeat="c in invoice.currencies">
      {{invoice.total(c) | currency:c}}
    </span>
    <button class="btn" ng-click="invoice.pay()">Pay</button>
  </div>
</div>

------------------------------------------------------------------

우리는 이전예제에서 convertCurrency 함수를 새로운 파일에 이동시켰다. 
그렇다면 어떻게 controller이 새로 때어낸 함수를 이용할 수 있을까? 


Dependency Injection : 여기서 사용하는 것이 Depencency Injection이다. 이는 소프트웨어 디자인 패턴으로 어떻게 객체나 함수가 생성되는지 그리고 어떻게 이러한 의존성을 획득하고 사용할 수 있는지를 알려준다. 
Angular에서 directives, filters, controllers, services 등등은 생성되고, depencency를 추가할 수 있도록 되어 있다. 

의존성을 이용하기 위해서는 의존성 추가 파트에 해당 의존성을 추가해 주어야 한다. Angular에서 이러한 일을 해주는 것이 module이다. Angular가 시작되면 ng-app 디렉티브의 설정을 이용하게 되며, 모든 의존성 있는 모듈들을 추가한다.

상기 예제에서는 ng-app="invoice2" 디렉티브를 템플릿에 추가하였으며 이것은 Angular에게 invoice2를 어플리케이션의 메인 모듈이며 이를 이용하겠다고 알리는 것이다. 
이후 코드 부분에서 angular.module('invoice2', ['finance2']) 를 이용하여 invoice2가 finance2 모듈에 의존성을 갖는다는 것을 알려준다. Angualr는 InvoiceController를 이용하며 게다가 currencyConverter 서비스도 함께 이용하게 된다. 

이제 Angular는 어플리케이션의 모든 파트를 알고 있게 되었다. 이제는 그것들을 생성하는 일이 남아 있다. 이전 섹션에서는 controllers는 factory 함수를 이용하여 컨트롤러가 생성된 것을 확인했다. 서비스도 다양한 방법으로 그들의 factory를 정의할 수 있다. 상단의 예제에서 우리는 currencyConverter함수를 서비스 팩토리를 이용하여 반환하였다. 

어떻게 InvoiceController가 currencyConverter 함수의 레퍼런스를 획득할 수 있을까? 
Angular에서는 단순하게 정의한 생성자 아규먼트로 이 행위를 수행할 수 있다. injector는 객체를 올바른 순서로 생성하고, 이전 생성된 객체를 의존성이 있는 객체의 팩토리에 전달할 수 있는 방법을 제공한다. 
InvoiceController는 currencyConverter로 이름된 아규먼트를 가진다. 이를 통해서 Angular는 컨트롤러와 서비스의 의존성에 대해 알게 되며, 컨트롤러에서 서비스의 인스턴스를 아규먼트를 통해서 접근할 수 있게 된다. 

이전 섹션과 이 섹션의 차이는 이제는 module.controller함수에서 배열을 전달하고 있다는 것이다. 
배열의 첫번째는 의존성이 있는 서비스의 이름을 의미하며, 이것은 컨트롤러에서 필요한 값이다. 배열의 마지막 엔트리는 생성자 함수이다. Angualr에서는 array를 이용하여 DI를 수행하여 코드를 최소화 하고 있다. 

Accessing the backend

------------------------------------------------------------------
angular.module('invoice3', ['finance3'])
.controller('InvoiceController', ['currencyConverter', function(currencyConverter) {
  this.qty = 1;
  this.cost = 2;
  this.inCurr = 'EUR';
  this.currencies = currencyConverter.currencies;

  this.total = function total(outCurr) {
    return currencyConverter.convert(this.qty * this.cost, this.inCurr, outCurr);
  };
  this.pay = function pay() {
    window.alert("Thanks!");
  };
}]);
------------------------------------------------------------------


------------------------------------------------------------------
angular.module('finance3', [])
.factory('currencyConverter', ['$http', function($http) {
  var YAHOO_FINANCE_URL_PATTERN =
        '//query.yahooapis.com/v1/public/yql?q=select * from '+
        'yahoo.finance.xchange where pair in ("PAIRS")&format=json&'+
        'env=store://datatables.org/alltableswithkeys&callback=JSON_CALLBACK';
  var currencies = ['USD', 'EUR', 'CNY'];
  var usdToForeignRates = {};

  var convert = function (amount, inCurr, outCurr) {
    return amount * usdToForeignRates[outCurr] / usdToForeignRates[inCurr];
  };

  var refresh = function() {
    var url = YAHOO_FINANCE_URL_PATTERN.
               replace('PAIRS', 'USD' + currencies.join('","USD'));
    return $http.jsonp(url).then(function(response) {
      var newUsdToForeignRates = {};
      angular.forEach(response.data.query.results.rate, function(rate) {
        var currency = rate.id.substring(3,6);
        newUsdToForeignRates[currency] = window.parseFloat(rate.Rate);
      });
      usdToForeignRates = newUsdToForeignRates;
    });
  };

  refresh();

  return {
    currencies: currencies,
    convert: convert
  };
}]);

------------------------------------------------------------------


------------------------------------------------------------------
<div ng-app="invoice3" ng-controller="InvoiceController as invoice">
  <b>Invoice:</b>
  <div>
    Quantity: <input type="number" min="0" ng-model="invoice.qty" required >
  </div>
  <div>
    Costs: <input type="number" min="0" ng-model="invoice.cost" required >
    <select ng-model="invoice.inCurr">
      <option ng-repeat="c in invoice.currencies">{{c}}</option>
    </select>
  </div>
  <div>
    <b>Total:</b>
    <span ng-repeat="c in invoice.currencies">
      {{invoice.total(c) | currency:c}}
    </span>
    <button class="btn" ng-click="invoice.pay()">Pay</button>
  </div>
</div>
------------------------------------------------------------------

이 finance모듈에서 우리는 $http를 이용하고 있다. 이것은 내장 서비스로 Angular가 서버의 backend로 접근할 수 있도록 제공해준다. $http는 XMLHttpRequest와 JSONP 전송을 wrapping하고 있다. 

from : https://docs.angularjs.org/guide/concepts





[Java] Eclipse static import 등록하기

Hamcrest나 Assert 등은 자동으로 ctrl + space를 해도 import되지 않는다.
이때 설정을 통해 한번에 필요한 패키지를 등록해두고 사용하면 편리하다.

1. Eclipse 등록하기

Preferences > Java > Editor > Content Assist > Favorites

다음과 같은 내용을 등록한다.
org.hamcrest.Matchers.*
org.hamcrest.CoreMatchers.*
org.junit.*
org.junit.Assert.*
org.junit.Assume.*
org.junit.matchers.JUnitMatchers.*


참고로 등록할때에는 마지막 클래스 까지만 등록한다. 



2. 사용하기 
등록후 assertThat에 커서를 옮기고 ctrl + space를 클릭하면 자동으로 임포트 된다.