Backbone.js 서점 샘플 예제를 사이클링 샘플로 변경하면서 직접 코딩해 보고 구조를 파악하자.
1) index.html 파일 준비하기
- 이미지 파일 다운로드 하고 img 폴더에 넣는다 (100*152)
- backbone.js, underscore.js, jquery.js 파일 다운로드 받아서 js 폴더에 넣는다
- index.html 파일을 루트에 생성한다
- 브라우져 수행 점검
- css 폴더를 만들고 screen.css 파일을 생성한다
css body { background-color: #eee; }
.bookContainer { border: 1px solid #aaa; width: 350px; height: 170px; background-color: #fff; float: left; margin: 5px; } .bookContainer img { float: left; margin: 10px; } .bookContainer ul { list-style-type: none; }
2) index.html 확장하기
- script 태그를 넣는다
<script id="rideTemplate" type="text/template">
<img src="<%= coverImage %>"/>
<ul>
<li><%= title %></li>
<li><%= rider %></li>
<li><%= ridingDate %></li>
<li><%= keywords %></li>
</ul>
</script>
Model의 명칭과 template의 <%= 명칭 %> 을 일치시켰다
3) ride.js 통해 Backbone 코딩하기
- js 폴더안에 ride.js파일을 생성하고 Model, View 클래스를 만든다
(function ($) {
// model 만들기
var Ride = Backbone.Model.extend({
defaults:{
coverImage:"img/my_cycle.png",
title:"2011년 대관령 대회",
rider:"Yun YoungSik",
ridingDate:"2011",
keywords:"대관령 힐크라이밍 대회"
}
});
// view 만들기
var RideView = Backbone.View.extend({
// el 변수에 html에 있는 DOM 레퍼런스를 만든다. $el은 DOM에 대한 jQuery 객체를 의미 한다
// html에 없는 DOM을 만들때는 tagName/className/id 등을 설정한다. 설정하지 않으면 기본 div 태그임
// el 이란 무엇인가?
tagName:"div",
// screen.css 에서 사용할 css의 클래스이름 .rideContainer
className:"rideContainer",
template:$("#rideTemplate").html(),
render:function () {
//tmpl은 JSON객체를 받아서 html을 반환하는 함수이다.
var tmpl = _.template(this.template);
//jQuery html() 함수를 사용하기 위해 jQuery객체 $el을 쓴다.
this.$el.html(tmpl(this.model.toJSON()));
return this;
}
});
})(jQuery);
- View를 생성하고 Model를 할당한 후 render()를 호출하여 화면을 그린다
(function ($) {
// model 만들기
var Ride = Backbone.Model.extend({
defaults:{
coverImage:"img/my_cycle.png",
title:"2011년 대관령 대회",
rider:"Yun YoungSik",
ridingDate:"2011",
keywords:"대관령 힐크라이밍 대회"
}
});
// view 만들기
var RideView = Backbone.View.extend({
tagName:"div",
className:"rideContainer",
template:$("#rideTemplate").html(),
render:function () {
//tmpl은 JSON객체를 받아서 html을 반환하는 함수이다.
var tmpl = _.template(this.template);
//this.el은 tagName에 정의된 것이다. jQuery html() 함수를 사용하기 위해서는 $el을 쓴다.
this.$el.html(tmpl(this.model.toJSON()));
return this;
}
});
// model, view 생성하여 view에 model 할당하기
var ride = new Ride({
title:"Some title",
rider:"Yun DoWon",
ridingDate:"2011",
keywords:"대관령 힐크라이밍 대회"
});
rideView = new RideView({
model: ride
});
// 템플릿에 값을 맵핑한 DOM 을 최종적으로 넘김
$("#rides").html(rideView.render().el);
})(jQuery);
- 브라우져 호출 결과화면
4) Collection을 통해 목록 만들기
- Collection 클래스를 상속받아 Collection Model을 만들고 Collection을 제어할 수 있는 View도 만든다
(function ($) {
var Ride = Backbone.Model.extend({
defaults:{
coverImage:"img/my_cycle.png",
title:"2011년 대관령 대회",
rider:"Yun YoungSik",
ridingDate:"2011",
keywords:"대관령 힐크라이밍 대회"
}
});
var RideView = Backbone.View.extend({
tagName:"div",
className:"rideContainer",
template:$("#rideTemplate").html(),
render:function () {
//tmpl은 JSON객체를 받아서 html을 반환하는 함수이다.
var tmpl = _.template(this.template);
//this.el은 tagName에 정의된 것이다. jQuery html() 함수를 사용하기 위해서는 $el을 쓴다.
this.$el.html(tmpl(this.model.toJSON()));
return this;
}
});
var ride = new Ride({
title:"Some title",
rider:"Yun DoWon",
ridingDate:"2011",
keywords:"대관령 힐크라이밍 대회"
});
var rideView = new RideView({
model: ride
});
$("#rides").html(rideView.render().el);
//////////////////////
// rides 샘플 3개 생성
var rides = [{title:"대관령 힐크라이밍 대회", rider:"Yun YoungSik", ridingDate:"2010", keywords:"대관령"},
{title:"미시령 힐크라이밍 대회", rider:"Yun DoWon", ridingDate:"2011", keywords:"미시령"},
{title:"투어 드 코리아", rider:"Yun YoungSik", ridingDate:"2012", keywords:"코리아"}];
//////////////////////
// Ride Collection 생성
var Rides = Backbone.Collection.extend({
model : Ride
});
//////////////////////
// Collection View 생성
var RidesView = Backbone.View.extend({
el:$("#rides"),
initialize:function(){
this.collection = new Rides(rides);
this.render();
},
// 컬렉션의 내역을 루핑을 돌고,
// each 메소드내에서서 함수 그자체가 this가 된다. 따라서 this 참조 안하게 that 을 설정한다 (블로깅)
render: function(){
var that = this;
_.each(this.collection.models, function(item){
that.renderRide(item);
}, this);
},
// RideView 를 통해 개별적으로 렌더링한다
renderRide:function(item){
var rideView = new RideView({
model: item
});
this.$el.append(rideView.render().el);
}
});
// new 를 하면 initialize() 가 자동 호출된다
var ridesView = new RidesView();
})(jQuery);
- 호출한 결과 화면 : 기존 1개와 샘플 데이터 4개로 총 4개의 화면이 나옴
5) 컬렉션에 모델을 추가하기
- 옷 신제품 발표 컬렉션에 옷입은 모델들을 추가하는 느낌이랄까 어감이 비슷하네요. ^^
- 모델을 추가하기 위해서 index.html에 input tag 를 추가한다
.. 중략 ..
<div id="rides">
<!-- default 로 들어 있던 div 태그 제거 -->
<!--div class="rideContainer">
<img src="img/my_cycle.png"/>
<ul>
<li>Title</li>
<li>Rider</li>
<li>Riding date</li>
<li>Keywords</li>
</ul>
</div-->
<!-- 새로운 카드를 넣을 수 있는 input tags -->
<div id="addRide">
<label for="coverImage">CoverImage: </label><input id="coverImage" type="file" />
<label for="title">Title: </label><input id="title" type="text" />
<label for="rider">Rider: </label><input id="rider" type="text" />
<label for="ridingDate">Riding date: </label><input id="ridingDate" type="text" />
<label for="keywords">Keywords: </label><input id="keywords" type="text" />
<button id="add">Add</button>
</div>
<!-- 하단의 template script 만 놓는다 -->
<script id="rideTemplate" type="text/template">
.. 중략 ..
- screen.css 파일에 하기 내역을 추가한다
css body { background-color: #eee; }
.rideContainer { border: 1px solid #aaa; width: 350px; height: 170px; background-color: #fff; float: left; margin: 5px; } .rideContainer img { float: left; margin: 10px; } .rideContainer ul { list-style-type: none; }
#addRide label {
width:100px;
margin-right:10px;
text-align:right;
line-height:25px;
}
#addRide label, #addRide input {
display:block;
margin-bottom:10px;
float:left;
}
#addRide label[for="title"], #addRide label[for="ridingDate"] {
clear:both;
}
#addRide button {
display:block;
margin:5px 20px 10px 10px;
float: right;
clear: both;
}
#addRide div {
width: 550px;
}
#addRide div:after {
content:"";
display:block;
height:0;
visibility:hidden;
clear:both;
font-size:0;
line-height:0;
}
- ride.js 파일에서 RidesView 안의 renderRide 메소드 밑으로 addRide 메소드를 추가한다
// 테스트로 한개 넣었던 코드에 대해서 주석처리한다.
/* var ride = new Ride({
title:"No title",
rider:"Unknown",
ridingDate:"Unknown",
keywords:"empty"
});
var rideView = new RideView({
model: ride
});
$("#rides").html(rideView.render().el); */
.. 중략 ..
renderRide:function(item){
var rideView = new RideView({
model: item
});
this.$el.append(rideView.render().el);
},
addRide: function(e){
e.preventDefault();
var formData = {};
// jQuery의 each로 formData key=value 객체를 만듦
$("#addRide").children("input").each(function (i, el) {
if ($(el).val() !== "") {
formData[el.id] = $(el).val();
}
});
// rides 배열에 저장
//rides.push(formData);
// 컬렉션 객체에 저장
this.collection.add(new Ride(formData));
},
// add버튼 누를때 이벤트 발생하여 addRide 메소드 호출
events:{
"click #add": "addRide"
}
});
- 컬렉션에 모델이 추가되는 add 메소드 호출시 이벤트 발생토록 함
var RidesView = Backbone.View.extend({
el:$("#rides"),
initialize:function(){
this.collection = new Rides(rides);
this.render();
// initailize 초기화일 때 자동으로 trigger 될 수 있도록 설정을 합니다
this.collection.on("add", this.renderRide, this);
},
- 실행하기
$ static
serving "." at http://127.0.0.1:8080
11:16:58 [200]: /
11:16:58 [200]: /css/screen.css
11:16:58 [200]: /js/ride.js
11:16:58 [200]: /js/underscore.js
11:16:58 [200]: /js/backbone.js
11:16:58 [200]: /img/my_cycle.png
11:16:58 [200]: /js/jquery.js
11:16:58 [404]: /favicon.ico
+ http://localhost:8080 호출 한 경우
+ 값을 입력하고 "Add" 클릭하였을 때 (평택 대회)
6) 모델 삭제하기
- index.html안에 삭제 버튼을 넣는다
<script id="rideTemplate" type="text/template">
<img src="<%= coverImage %>"/>
<ul>
<li><%= title %></li>
<li><%= rider %></li>
<li><%= ridingDate %></li>
<li><%= keywords %></li>
</ul>
<button class="delete">Delete</button>
</script>
- button에 대한 screen.css 를 조정한다
.rideContainer ul {
list-style-type: none;
margin-bottom: 0;
}
.rideContainer button {
float:right;
margin: 10px;
}
- 개별 모델에 delete button이 놓인다
- 삭제 로직을 RideView 에 코딩한다 : 삭제 메소드 -> 이벤트 등록
var RideView = Backbone.View.extend({
tagName:"div",
className:"rideContainer",
template:$("#rideTemplate").html(),
render:function () {
//tmpl은 JSON객체를 받아서 html을 반환하는 함수이다.
var tmpl = _.template(this.template);
//this.el은 tagName에 정의된 것이다. jQuery html() 함수를 사용하기 위해서는 $el을 쓴다.
this.$el.html(tmpl(this.model.toJSON()));
return this;
},
events: {
"click .delete": "deleteRide"
},
deleteRide:function () {
//모델을 삭제한다.
this.model.destroy();
//뷰를 삭제한다.
this.remove();
}
});
7) 컬렉션 모델 삭제하기
- rides 배열에는 아직 그대로 데이터가 존재한다. 이를 삭제하기 위하여 먼저 remove 이벤트의 trigger를 RidesView에 등록한다
var RidesView = Backbone.View.extend({
el: $("#rides"),
initialize: function(){
this.collection = new Rides(rides);
this.render();
// 컬렉션 add가 호출되면 renderRide를 trigger 한다
this.collection.on("add", this.renderRide, this);
this.collection.on("remove", this.removeRide, this);
},
render: function(){
var that = this;
_.each(this.collection.models, function(item){
that.renderRide(item);
}, this);
},
renderRide: function(item){
var rideView = new RideView({
model: item
});
this.$el.append(rideView.render().el);
},
addRide: function(e){
e.preventDefault();
var formData = {};
// jQuery의 each로 formData key=value 객체를 만듦
$("#addRide").children("input").each(function (i, el) {
if ($(el).val() !== "") {
formData[el.id] = $(el).val();
}
});
// rides 배열에 저장
rides.push(formData);
// 컬렉션 객체에 저장
this.collection.add(new Ride(formData));
},
// 삭제된 모델을 인자로 자동 넣어준다
removeRide: function(removedRide){
// attributes는 Model의 hash key=value object 이다
var removedRideData = removedRide.attributes;
_.each(removedRideData, function(val, key){
if(removedRideData[key] === removedRide.defaults[key]){
console.log(">> 1 : " + removedRideData[key]);
delete removedRideData[key];
}
});
_.each(rides, function(ride){
if(_.isEqual(ride, removedRideData)){
console.log(">> 2 : " + ride);
rides.splice(_.indexOf(rides, ride), 1);
}
});
},
// add버튼 누를때 이벤트 발생하여 addRide 메소드 호출
events:{
"click #add": "addRide"
}
});
- 삭제버튼을 클릭하면 삭제가 잘 된다
* 지금까지의 소스
<참조>
- 원문 : Backbone.js Developing 번역글
'Backbone.js' 카테고리의 다른 글
[Handlebar.js] Client Side 템플릿 엔진 사용하기 (0) | 2013.03.16 |
---|---|
[Backbone.js] 서점 샘플 응용하기 - 02 (MongoDB 연결하기) (0) | 2013.03.15 |
[Backbone.js] 큰 규모의 SPA 개발을 위한 준비 (0) | 2013.03.12 |
[Backbone.js] Underscore.js 이해와 API 테스트 (0) | 2013.03.11 |
[Backbone.js] 배우는 방법 (2) | 2013.02.20 |