* 하위 module을 갖고 있는 module에서 config와 run의 실행 시점
(1) 하위 의존성 module들의 config 콜백 실행
(2) 의존성 module들의 config 실행이 모두 완료된 후, 상위 module의 config 콜백 실행
(3) 하위 의존성  module의 run 콜백 실행
(4) 의존성 module들의 run 실행이 모두 완료된 후, 상위 module의 run콜백 실행
 
* constant와 value의 차이점
– 거의 유사한데 의견이 다른 부분이 있어서 정확히 모르겠다.
 
* constant 사용시 주의할 점 (실행 시점)
– 상위 module에 constant 를 정의하고, 하위 의존성 module의 config 에서 가져다 쓸 수는 없다 (실행 시점 문제)
(그러나, 하위 의존성 module의 run 에서는 가져다 쓸 수 있다)
 
* ng-repeat 예시 1

<tr ng-repeat="item in todos"><!-- 배열 foreach -->
    <td ng-repeat="prop in item">{{prop}}</td><!-- 객체 프로퍼티 foreach -->
</tr>

 
* ng-repeat 예시 2

<tr ng-repeat="item in todos"><!-- 배열 foreach -->
    <td ng-repeat="(key, value) in item">{{key}}={{value}}</td><!-- 객체 프로퍼티 foreach (key, value로 풀어서) -->
</tr>

 
* ng-repeat 디렉티브의 내장 변수
$index : 현재 객체나 속성의 위치를 반환한다.
$first : 현재 객체가 컬렉션 내 첫 번째 객체이면 true를 반환한다.
$middle : 현재 객체가 컬렉션 내 첫 번째나 마지막 객체가 아니면 true를 반환한다.
$last : 현재 객체가 컬렉션 내 마지막 객체이면 true를 반환한다.
$even : 현재 객체가 컬렉션 내 짝수 번째 객체이면 true를 반환한다.
$odd : 현재 객체가 컬렉션 내 홀수 번째 객체이면 true를 반환한다.
 
* 테이블의 짝수 행, 홀수 행에 각각 다른 스타일을 적용하는 예시 (Bootstrap에서는 그냥 선언적으로 되겠지만…^^)

<tr ng-repeat="item in todos" ng-class="$odd ? 'odd' : 'even'">
    <td>{{$index + 1}}</td>
    <td ng-repeat="prop in item">{{prop}}</td>
</tr>

 
* ng-repeat 사용시, 하나의 엘리먼트를 반복하는 것이 아니라 여러 개를 셋트로 반복해야 할 경우
(ex: 하나의 tr만 반복하는 것이 아니라 여러 개의 tr을 셋트로 하여 반복하는 경우)
=> 반복하려는 셋트의 첫 엘리먼트에 ng-repeat-start 를 사용하고, 셋트의 마지막 엘리먼트에 ng-repeat-end를 사용한다.
 
* ng-include 디렉티브 : html 파일을 읽어서 DOM에 삽입한다.
– 주의! : <ng-include> 사용시 <script> 처럼 닫는 태그가 반드시 있어야 한다.
– 주의! : <ng-include>의 src 속성을 지정할 때에는 쌍따옴표 내에 외따옴표가 있어야 한다.
– ng-include 로 로드한 파일에서는 부모문서의 데이터 및 모델에 접근할 수 있고, ng-repeat 안에서 로딩되었다면 $index 등의 변수도 사용할 수 있다.
– onload 속성에 $scope에 정의한 함수를 바인딩하여 호출할 수 있다. ng-include가 커스텀 엘리먼트가 아니라 커스텀 어트리뷰트일 경우에도 onload속성은 동일하게 동작한다.
 
* 동적으로 include 대상을 변경하는 예시
– js

$scope.viewFile = function(){
    return $scope.showList ? "list.html" : "table.html";
}

– html

<label>
    <input type="checkbox" ng-model="showList">
    Use the list view
</label>
<ng-include src="viewFile()"></ng-include>

 
* ng-switch 디렉티브 : 조건에 따라 html 묶음을 교체하여 노출할 때 사용 (아래 예시)

<div class="radio" ng-repeat="button in ['None', 'Table', 'List']">
    <label>
        <input type="radio" ng-model="data.mode" value="{{button}}" ng-checked="$first" />
        {{button}}
    </label>
</div>
<div ng-switch on="data.mode">
    <div ng-switch-when="Table">
        <table>
            테이블 내용 블라블라...
        </table>
    </div>
    <div ng-switch-when="List">
        <ol>
            리스트 내용 블라블라...
        </ol>
    </div>
    <div ng-switch-default>
        else 일 경우 보여줄 영역...
    </div>
</div>

 
* ng-include와 ng-switch 디렉티브의 선택
– 기본적으로는 ng-switch 를 사용하자
– 컨텐츠가 지나치게 관리하기 복잡해지거나, 같은 콘텐츠를 다른 곳에서도 사용해야 할 일이 생기면 ng-include를 사용하자.
 
* 값이 바인딩 되지 않은 인라인 템플릿 표현식 (ex: {{name}}) 이 화면에 표시되지 않도록 하는 방법
– 바인딩 완료시점까지 숨기고 싶은 엘리먼트에 ng-cloak 속성을 선언해 준다.
 
*  ng-show, ng-hide는 엘리먼트를 감추기만 하지만, ng-if는 해당 엘리먼트를 DOM에서 제거한다.
 
* 체크박스를 클릭하면 버튼을 disabled 시키는 예제 (ng-disabled)
– controller에 $scope.dataValue = false; 코드가 있다고 가정한다.

<label>
    <input type="checkbox" ng-model="dataValue">
    Set the Data Value
</label>
<button class="btn btn-success" ng-disabled="dataValue">My Button</button>

 
* ng-disabled와 마찬가지로, angularjs에서 직접적으로 접근하여 제어할 수 없는 boolean 어트리뷰트 디렉티브
– ng-checked, ng-open, ng-readonly, ng-selected
 
* boolean 계열은 아니지만, angularjs에서 직접적으로 작업을 수행할 수 없는 어트리뷰트와 연동하는 디렉티브
– ng-href, ng-src, ng-srcset
 
* controller $scope에 정의되지 않은 model 객체를 암시적으로 생성하여 사용하는 예시
– 아래 html에서 newTodo는 $scope에 정의되지 않았지만, 클릭 이벤트 핸들러 동작시 매개변수로 들어간다.
이러한 경우에 angularjs는 알아서 model객체를 생성하여 적용해 준다.
– 그러나 이 경우에는, 해당 input에 아무런 입력/변경사항이 없을 경우 발생하는 undefined 에러를 주의해야 한다. (그래서 스크립트에 if문이 있음)
– js

$scope.addNewItem = function(newItem){
	if(angular.isDefined(newItem) && angular.isDefined(newItem.action) && angular.isDefined(newItem.location)){
		$scope.todos.push({
			action: newItem.action + " (" + newItem.location + ")",
			complete: false
		});
	}
}

– html

<div class="form-group row">
	<label for="actionText">Action:</label>
	<input id="actionText" class="form-control" ng-model="newTodo.action" />
</div>
<div class="form-group row">
	<label for="actionLocation">Location:</label>
	<select id="actionLocation" class="form-control" ng-model="newTodo.location">
		<option>Home</option>
		<option>Office</option>
		<option>Mall</option>
	</select>
</div>
<button class="btn btn-primary btn-block" ng-click="addNewItem(newTodo)">Add</button>

 
* 폼 유효성 검사 관련하여 의미를 갖는 input type 어트리뷰트 (ex: <input type=”text” />)
– checkbox, email(이메일 형식 검사), number, radio, text, url(url 형식 검사)
– type 어트리뷰트는 아니지만 required 어트리뷰트도 기본적으로 사용한다.
 
* 폼 디렉티브에서 정의한 유효성 검증 변수 (ex: {{myForm.$invalid}} )
$pristine : 사용자가 엘리먼트/폼과 상호작용하지 않은 경우 true를 반환한다.
$dirty : 사용자가 엘리먼트/폼과 상호작용한 경우 true를 반환한다.
$valid : 엘리먼트/폼의 내용이 유효한 경우 true를 반환한다.
$invalid : 엘리먼트/폼이 유효하지 않은 경우 true를 반환한다.
$error : 유효성 검증 오류에 대한 상세 정보를 제공
 
* angularjs 유효성 검증시 사용자 피드백을 위해 사용하는 클래스
ng-pristine : 사용자가 상호작용하지 않은 엘리먼트에는 이 클래스가 추가된다.
ng-dirty : 사용자가 상호작용한 엘리먼트에는 이 클래스가 추가된다.
ng-valid : 유효한 엘리먼트에는 이 클래스가 추가된다.
ng-invalid : 유효하지 않은 엘리먼트에는 이 클래스가 추가된다.
– 예시1 (css)

<style>
	form .ng-invalid.ng-dirty { background-color: lightpink; } /* (1) 폼엘리먼트에 클래스명이 없어도 예약어처럼 자동 적용된다. */
	form .ng-valid.ng-dirty { background-color: lightgreen; }
	span.summary.ng-invalid { color: red; font-weight: bold; } /* (2) summary라는 사용자정의클래스의 엘리먼트에 적용된다. */
	span.summary.ng-valid { color: green }
</style>

– 예시1 (html)  ((1)은 전반적으로 적용되는 것이기 때문에 (2)의 예시만 기재)

<span class="summary" ng-class="myForm.$valid ? 'ng-valid' : 'ng-invalid'">
	{{myForm.$valid}}
</span>

– 예시2 (css)

form .ng-invalid-required.ng-dirty { background-color: lightpink; }
form .ng-invalid-email.ng-dirty { background-color: lightgoldenrodyellow; }
form .ng-valid.ng-dirty { background-color: lightgreen; }

– 예시2 (js – controller 내부의 코드)

$scope.getError = function(error){
	if(angular.isDefined(error)){
		if(error.required){
			return 'Please enter a value';
		}else if(error.email){
			return 'Please enter a valid email address';
		}
	}
}

– 예시2 (html)

<div class="error" ng-show="myForm.userEmail.$invalid && myForm.userEmail.$dirty">
	{{getError(myForm.userEmail.$error)}}
</div>

 
* 유효성검증 피드백 지연
– 너무 빨리 validation 하지 않고 저장 시점에 하도록
– css

form.validate .ng-invalid-required.ng-dirty { background-color: lightpink; } /* validate 사용자정의클래스는 동적으로 추가된다. */
form.validate .ng-invalid-email.ng-dirty { background-color: lightgoldenrodyellow; } /* dirty일 경우에만 적용되는 것에 주의 */
div.error { color: red; font-weight: bold; }

– js

$scope.addUser = function(userDetails){
	if(myForm.$valid){
		$scope.message = userDetails.name + " (" + userDetails.email + ") (" + userDetails.agreed + ")";
	}else{
		$scope.showValidation = true; //사용자정의 속성
	}
}

– html

<form name="myForm" novalidate ng-submit="addUser(newUser)" ng-class="showValidation ? 'validate' : ''">
	<input name="userEmail" type="email" class="form-control" required ng-model="newUser.email" />
	<div class="error" ng-show="showValidation">
		{{getError(myForm.userEmail.$error)}}
	</div>
</form>