AngularJS 기초학습2
팩토리
팩토리로 서비스를 만든다.
객체 리터럴을 팩토리안에 생성하거나 다음 몇가지 메서드를 추가한다.
myApp.factory('Server', ['$http', function ($http) {
return {
get: function(url) {
return $http.get(url);
},
post: function(url) {
return $http.post(url);
},
};
}]);
하단은 Angular의 XHR을 래핑한 코드이며, 컨트롤러에 의존성을 주입한 후 간단히 사용하면 된다.
myApp.controller('MainCtrl', ['$scope', 'Server', function ($scope, Server) {
var jsonGet = 'http://myserver/getURL';
var jsonPost = 'http://myserver/postURL';
Server.get(jsonGet);
Server.post(jsonPost);
}]);
혹시 서버 변경사항을 폴링하고 싶으면 Server.poll(jsonPoll)을 설정하거나 Server.socket(jsonSoket)을 사용할수도 있다.
코드를 최소화로 유지함으로써, 나만의 코드를 모듈화 할수가 있다.
필터
필터는 배열의 데이터와 함께 사용하며 루프 밖에서도 사용 할 수 있다. 데이터를 순회하면서 특정 조건에 만족하는 데이터만 추리고 싶을 떄 필터를 사용 하면 된다.
myApp.filter('reverse', function () {
return function (input, uppercase) {
var out = '';
for (var i = 0; i < input.length; i++) {
out = input.charAt(i) + out;
}
if (uppercase) {
out = out.toUpperCase();
}
return out;
}
});
// 데이터를 제공하는 컨트롤러
myApp.controller('MainCtrl', ['$scope', function ($scope) {
$scope.greeting = 'Todd Motto';
}]);
다음은 DOM에서 사용하는 방법이다:
<div ng-app="myApp">
<div ng-controller="MainCtrl">
<p>No filter: {{ greeting }}</p>
<p>Reverse: {{ greeting | reverse }}</p>
</div>
</div>
그리고 ng-repeat 안에서 다음과 같이 필터를 사용한다:
<ul>
<li ng-repeat="number in myNumbers |filter:oddNumbers">{{ number }}</li>
</ul>
다음은 컨트롤러 안에서 필터를 선언하는 예제다:
myApp.controller('MainCtrl', ['$scope', function ($scope) {
$scope.numbers = [10, 25, 35, 45, 60, 80, 100];
$scope.lowerBound = 42;
$scope.greaterThanNum = function (item) {
return item > $scope.lowerBound;
};
}]);
그리고 이 필터를 ng-repeat 에서 다음과 같이 사용한다:
<li ng-repeat="number in numbers | filter:greaterThanNum">
{{ number }}
</li>
양방향 데이터 바인딩(완전히 동기화된 데이터)
모델을 갱신하면 뷰에 반영되고 뷰를 갱신하면, 모델에 반영되는 형태를 말한다.
이는 별다른 작업 없이도 데이터가 동기화 된다는 뜻이다.
<div ng-app="myApp">
<div ng-controller="MainCtrl">
<input type="text" ng-model="myModel" placeholder="Start typing..." />
<p>My model data: {{ myModel }}</p>
</div>
</div>
myApp.controller('MainCtrl', ['$scope', function ($scope) {
// 빈 문자열로 초기화하고 모델 데이터를 읽어온다.
$scope.myModel = '';
}]);
XHR/AJAX / $http 호출과 JSON 바인딩
$scope에 기본적인 데이터를 넣는 방법과 모델이 어떻게 양방향 데이터 바인딩으로 동작하는지를 알아봤고, 실제 XHR 호출을 시도해 보자
$http 메서드는 Angular가 서버 데이터에 접근하는 기능을 래핑한 메서드 이다.
myApp.controller('MainCtrl', ['$scope', '$http', function ($scope, $http) {
$http({
method: 'GET',
url: '//localhost:9000/someUrl'
});
}]);
이렇게 하면 Angular는 콜백을 좀 더 효율적이고 읽기 쉬운 형태로 작성 할수있는 promise라는 걸 반환한다.
myApp.controller('MainCtrl', ['$scope', function ($scope) {
$http({
method: 'GET',
url: '//localhost:9000/someUrl'
})
.success(function (data, status, headers, config) {
// 성공! 데이터를 가져왔어
})
.error(function (data, status, headers, config) {
// 이런. 뭔가 잘못되었음! :(
});
}]);
{
"user": {
"name": "Todd Motto",
"id": "80138731"
}
}
즉 서버가 객체 하나를 반환해주고
myApp.controller('UserCtrl', ['$scope', '$http', function ($scope, $http) {
// 사용자 객체를 생성
$scope.user = {};
// 빈 문자열로 초기화
$scope.user.username = '';
// 서버에 사용자 이름을 요청
$http({
method: 'GET',
url: '//localhost:9000/someUrlForGettingUsername'
})
.success(function (data, status, headers, config) {
// 서버로부터 받아온 사용자 이름을 모델에 할당!
$scope.user.username = data.user.name;
})
.error(function (data, status, headers, config) {
// 이런. 뭔가 잘못되었음! :(
});
}]);
DOM에서는 다음과 같이 설정하면 된다:
<div ng-controller="UserCtrl">
<p>{{ user.username }}</p>
</div>
선언적 데이터 바인딩
Angular의 철학은 기능이 풍부한 동적 HTML을 생성해서 웹 클라이언트 측에서 상상 할수 없을만큼 많은 일을 보이지 않게 처리해주는 것이다.
myApp.controller('EmailsCtrl', ['$scope', function ($scope) {
// 이메일 객체를 생성
$scope.emails = {};
// 서버에서 데이터를 받아온 것처럼 꾸며보자.
// 그냥 객체의 배열이다.
$scope.emails.messages = [{
"from": "Steve Jobs",
"subject": "I think I'm holding my phone wrong :/",
"sent": "2013-10-01T08:05:59Z"
},{
"from": "Ellie Goulding",
"subject": "I've got Starry Eyes, lulz",
"sent": "2013-09-21T19:45:00Z"
},{
"from": "Michael Stipe",
"subject": "Everybody hurts, sometimes.",
"sent": "2013-09-12T11:38:30Z"
},{
"from": "Jeremy Clarkson",
"subject": "Think I've found the best car... In the world",
"sent": "2013-09-03T13:15:11Z"
}];
}]);
HTML에 이걸 넣을 필요 없다. 동적인 HTML 조각을 만들기 위해 애플리케이션이 무엇을 해야 하는지 선언하는 선언적 바인딩을 사용할 시간이다. Angular가 기본으로 제공하고 어떤 콜백이나 상태 변경 없이도 데이터를 순회하며 결과를 렌더링하는 ng-repeat 디렉티브를 사용해보자:
<ul>
<li ng-repeat="message in emails.messages">
<p>From: {{ message.from }}</p>
<p>Subject: {{ message.subject }}</p>
<p>{{ message.sent | date:'MMM d, y h:mm:ss a' }}</p>
</li>
</ul>
선언적 바인딩의 강력함을 예로 ng-* 디렉티브를 공부해보자
서버와 모델, 뷰, 데이터를 그려주는 부분이 어떻게 잘 조화되는지 알 수 있다.
Scope 함수
myApp.controller('MainCtrl', ['$scope', function ($scope) {
$scope.deleteEmail = function (index) {
$scope.emails.messages.splice(index, 1)
};
}]);
Tip. 모델에서 데이터를 지우는 동작. 실제 DOM과 연관된 요소를 지우는 게 아니기 때문이다.
ng-* 디렉티브를 통해 Scope에 함수를 바인딩 해보자. 여기서는 ng-click 디렉티브를 사용한다.
<a ng-click="deleteEmail($index)">Delete email</a>
$index를 매개변수로 넘기면, Angular가 어떤메일을 지울지 알리기 위함이다.
선언적 DOM 메서드
이제 DOM 메서드로 넘어가보자.
ng-show와 ng-click을 사용해서 깔끔한 토클네비게이션을 만들어보자
<a href="" ng-click="toggle = !toggle">Toggle nav</a>
<ul ng-show="toggle">
<li>Link 1</li>
<li>Link 2</li>
<li>Link 3</li>
</ul>
표현식
elem.onclick = function (data) {
if (data.length === 0) {
otherElem.innerHTML = 'No data';
} else {
otherElem.innerHTML = 'My data';
}
};
이 코드는 데이터의 상태에 따라 DOM을 수정하는 코드로 GET요청에 대한 콜백으로 사용한다
<p>{{ data.length > 0 && 'My data' || 'No data' }}</p>
동적 뷰와 라우팅
단일 페이지 웹 애플리케이션(웹사이트) 에는 헤더, 푸터, 사이드바, 본문이 있고 URL에 따라 내용이 표시되는게 보통이다
Angular를 사용하면 동적 뷰를 통해 이를 쉽게 설정 할수 있다.
동적 뷰를 사용하는 방법은 URL을 기준으로 $routerProvider를 통해 특정 뷰를 얻어온 다음 적용하면 된다.
myApp.config(['$routeProvider', function ($routeProvider) {
/**
* $routeProvider
*/
$routeProvider
.when('/', {
templateUrl: 'views/main.html'
})
.otherwise({
redirectTo: '/'
});
}]);
Ajax 호출이 진행 중일때 이벤트를 보내는 $http 인터셉터 같은 것도 있다. 새로운 데이터를 받아오는 동안에 로딩 표시를 보여주는 용도로 사용 할 수 있다.
전역 Static 데이터
자바 태그를 DOM안에 넣었고 렌더링될 떄 서버로부터 데이터를 받아온다.
<!-- index.html 내용 (물론 페이지 맨 아래) -->
<script>
window.globalData = {};
globalData.emails = <javaTagHereToGenerateMessages>;
</script>
페이지가 해석되는 동안 자바 태그가 데이터를 렌더링 하고, Angular는 이메일 목록을 즉시 렌더링 할 것이다.
myApp.controller('EmailsCtrl', ['$scope', function ($scope) {
$scope.emails = {};
// 초기 데이터를 설정!
$scope.emails.messages = globalData.emails;
}]);
압축
Angular 코드 압축
myApp.controller('MainCtrl',
['$scope', 'Dependency', 'Service', 'Factory',
function ($scope, Dependency, Service, Factory) {
// 코드
}]);
하단은 압축한 결과 이다.
myApp.controller('MainCtrl',
['$scope', 'Dependency', 'Service', 'Factory',
function (a,b,c,d) {
// a = $scope
// b = Dependency
// c = Service
// d = Factory
// $scope 별칭이 사용됨
a.someFunction = function () {...};
}]);
주입하는 의존 객체의 순서에 주의 한다.