본문 바로가기

Javascript/Angular.js

CodeSchool Angular.js Level3

레벨 3에서는 form과 model에 대해서 공부한다.


app.js

(function() {

  var app = angular.module('gemStore', []);


  app.controller('StoreController', function(){

    this.products = gems;

  });


  app.controller('TabController', function(){

    this.tab = 1;


    this.setTab = function(newValue){

      this.tab = newValue;

    };


    this.isSet = function(tabName){

      return this.tab === tabName;

    };

  });


  app.controller('GalleryController', function(){

    this.current = 0;

    this.setCurrent = function(newGallery){

      this.current = newGallery || 0;

    };

  });


  var gems = [{

      name: 'Azurite',

      description: "Some gems have hidden qualities beyond their luster, beyond their shine... Azurite is one of those gems.",

      shine: 8,

      price: 110.50,

      rarity: 7,

      color: '#CCC',

      faces: 14,

      images: [

        "images/gem-02.gif",

        "images/gem-05.gif",

        "images/gem-09.gif"

      ],

      reviews: [{

        stars: 5,

        body: "I love this gem!",

        author: "joe@example.org",

        createdOn: 1397490980837

      }, {

        stars: 1,

        body: "This gem sucks.",

        author: "tim@example.org",

        createdOn: 1397490980837

      }]

    }, {

      name: 'Bloodstone',

      description: "Origin of the Bloodstone is unknown, hence its low value. It has a very high shine and 12 sides, however.",

      shine: 9,

      price: 22.90,

      rarity: 6,

      color: '#EEE',

      faces: 12,

      images: [

        "images/gem-01.gif",

        "images/gem-03.gif",

        "images/gem-04.gif",

      ],

      reviews: [{

        stars: 3,

        body: "I think this gem was just OK, could honestly use more shine, IMO.",

        author: "JimmyDean@example.org",

        createdOn: 1397490980837

      }, {

        stars: 4,

        body: "Any gem with 12 faces is for me!",

        author: "gemsRock@example.org",

        createdOn: 1397490980837

      }]

    }, {

      name: 'Zircon',

      description: "Zircon is our most coveted and sought after gem. You will pay much to be the proud owner of this gorgeous and high shine gem.",

      shine: 70,

      price: 1100,

      rarity: 2,

      color: '#000',

      faces: 6,

      images: [

        "images/gem-06.gif",

        "images/gem-07.gif",

        "images/gem-08.gif"

      ],

      reviews: [{

        stars: 1,

        body: "This gem is WAY too expensive for its rarity value.",

        author: "turtleguyy@example.org",

        createdOn: 1397490980837

      }, {

        stars: 1,

        body: "BBW: High Shine != High Quality.",

        author: "LouisW407@example.org",

        createdOn: 1397490980837

      }, {

        stars: 1,

        body: "Don't waste your rubles!",

        author: "nat@example.org",

        createdOn: 1397490980837

      }]

    }];

})();


reviews 배열이 추가되었다.


index.html

<!--  Review Form -->

<form name="reviewForm">

  <!--  Live Preview -->

  <blockquote>

    <strong>{{review.stars}} Stars</strong>

    {{review.body}}

    <cite class="clearfix">-{{review.author}}</cite>

  </blockquote>


  <!--  Review Form -->

  <h4>Submit a Review</h4>

  <fieldset class="form-group">

    <select ng-model="review.stars" class="form-control" ng-options="stars for stars in [5,4,3,2,1]"  title="Stars">

      <option value="">Rate the Product</option>

    </select>

  </fieldset>

  <fieldset class="form-group">

    <textarea ng-model="review.body" class="form-control" placeholder="Write a short review of the product..." title="Review"></textarea>

  </fieldset>

  <fieldset class="form-group">

    <input ng-model="review.author" type="email" class="form-control" placeholder="jimmyDean@example.org" title="Email" />

  </fieldset>

  <fieldset class="form-group">

    <input type="submit" class="btn btn-primary pull-right" value="Submit Review" />

  </fieldset>

</form>

form은 그냥 평범한 form 태그를 써주면 된다. 그리고 select 태그나 input 태그에 ng-model 속성을 주고 여기에 입력을 하면, Live Preview에서 해당 변수명이 실시간으로 변하는 것을 볼 수 있다.


이제, form에 입력한 리뷰를 등록해보자.

컨트롤러를 사용할 때이다.


app.js에 ReviewController를 만든다.

app.controller('ReviewController', function(){

  this.review = {};

  

  this.addReview = function(product){

    product.reviews.push(this.review);

    this.review = {};

  };

});

addReview 펑션에서 push 후 review 오브젝트를 다시 비워주는 이유는, 리뷰 입력후 submit 날린 후 다시 form을 클리어해서 보여주기 위해서다. review 오브젝트를 비워주지 않으면 submit 후에도 여전히 내가 남긴 코멘트가 남아있을 것이다. angular.js의 2way binding이 이런 건가보다.. 신기하네.


html

<form name="reviewForm" ng-controller="ReviewController as reviewCtrl"

                  ng-submit="reviewCtrl.addReview(product)">


  <!--  Live Preview -->

  <blockquote>

    <strong>{{reviewCtrl.review.stars}} Stars</strong>

    {{reviewCtrl.review.body}}

    <cite class="clearfix">—{{reviewCtrl.review.author}}</cite>

  </blockquote>


  <!--  Review Form -->

  <h4>Submit a Review</h4>

  <fieldset class="form-group">

    <select ng-model="reviewCtrl.review.stars" class="form-control" ng-options="stars for stars in [5,4,3,2,1]" title="Stars">

      <option value="">Rate the Product</option>

    </select>

  </fieldset>

  <fieldset class="form-group">

    <textarea ng-model="reviewCtrl.review.body" class="form-control" placeholder="Write a short review of the product..." title="Review"></textarea>

  </fieldset>

  <fieldset class="form-group">

    <input ng-model="reviewCtrl.review.author" type="email" class="form-control" placeholder="jimmyDean@example.org" title="Email" />

  </fieldset>

  <fieldset class="form-group">

    <input type="submit" class="btn btn-primary pull-right" value="Submit Review" />

  </fieldset>

</form>

form에 리뷰 컨트롤러를 씌워(?)주고, ng-submit으로 form이 submit됐을때 해야할 액션을 지정해준다. 그리고 Live Preview에도 reviewCtrl의 review를 가져오도록 해야 제대로 동작한다. ng-model도 마찬가지로 reviewCtrl을 참조하도록 수정해준다. 이렇게 하면 submit 날리는 경우 reviews 배열에 차곡차곡 쌓인다. 



이제 validation 체크를 해보자.

<!--  Review Form -->

<form name="reviewForm" ng-controller="ReviewController as reviewCtrl" 

      ng-submit="reviewForm.$valid && reviewCtrl.addReview(product)" novalidate>


  <!--  Live Preview -->

  <blockquote >

    <strong>{{reviewCtrl.review.stars}} Stars</strong>

    {{reviewCtrl.review.body}}

    <cite class="clearfix">—{{reviewCtrl.review.author}}</cite>

  </blockquote>


  <!--  Review Form -->

  <h4>Submit a Review</h4>

  <fieldset class="form-group">

    <select ng-model="reviewCtrl.review.stars" class="form-control" ng-options="stars for stars in [5,4,3,2,1]" title="Stars" required>

      <option value="">Rate the Product</option>

    </select>

  </fieldset>

  <fieldset class="form-group">

    <textarea ng-model="reviewCtrl.review.body" class="form-control" placeholder="Write a short review of the product..." title="Review"></textarea>

  </fieldset>

  <fieldset class="form-group">

    <input ng-model="reviewCtrl.review.author" type="email" class="form-control" placeholder="jimmyDean@example.org" title="Email" required/>

  </fieldset>

  <fieldset class="form-group">

    <input type="submit" class="btn btn-primary pull-right" value="Submit Review" />

  </fieldset>

</form>

일단 form에 novalidate 속성을 추가해준다. 이는 html에서 기본으로 제공하는 validation 검사를 쓰지 않겠다는 의미이다. 그리고 required는 필수값이고, type='email', 'number' 등으로 지정해주면 angular에서 알아서 validation 검사를 해준다. validation 결과가 false라도 submit은 실행된다. 이를 막기 위해 ng-submit에 reviewForm.$valid 를 추가해준다. $valide는 angular가 내장하고 있는 변수이다. validation 통과 유무를 true/false 값으로 가지고 있다.


angular에서는 input 태그에 validation 과 관련된 class를 자동으로 추가해준다.

예를 들면, 타이핑 전에는 ng-pristine ng-invalid 클래스가 아래와 같이 자동으로 들어가 있는 것이다.

<input ng-model="reviewCtrl.review.author" type="email" class="form-control ng-pristine ng-invalid " placeholder="jimmyDean@example.org" title="Email" required/>

타이핑 후에는 ng-pristine이 ng-dirty 클래스로 변경되며, validation 통과한 경우 ng-invalid 가 ng-valid로 바뀌게 된다.

이를 이용해서 css로 valid 여부를 보여줄 수 있다.


css

.ng-invalid.ng-dirty {

  border-color : red;

}

.ng-valid.ng-dirty {

  border-color : green;

}


마지막으로, review를 추가할때 등록시간을 넣어주자.

this.addReview = function(product){

  this.review.createdOn = Date.now();

  product.reviews.push(this.review);

  this.review = {};

};

보여줄때는 date filter를 쓰면 간단하게 변환할 수 있다.

<cite class="clearfix">—{{review.author}} on {{review.createdOn | date}}</cite>






* 소스는 모두 http://campus.codeschool.com 에서 가져온 겁니다.

'Javascript > Angular.js' 카테고리의 다른 글

CodeSchool Angular.js Level5  (0) 2017.03.10
CodeSchool Angular.js Level4  (0) 2017.03.09
CodeSchool Angular.js Level2  (0) 2017.03.06
CodeSchool Angular.js Level1  (0) 2017.02.27