블로그 이미지
윤영식
Full Stacker, Application Architecter, KnowHow Dispenser and Bike Rider

Publication

Category

Recent Post

2013. 11. 20. 10:58 AngularJS/Start MEAN Stack

갑작이 떠오른 아이디어를 모바일 서비스로 빠르게 만들고 싶다. 클라이언트/서버/스토어 이런 것을 해줄 사람은 없다. 나 홀로 만들어 보고 프로토타입핑해서 스타팅해보고 싶을 때 MEAN Stack을 사용하자 




1. 스프링같은 "WebApp Framework" 에 대한 고민

  클라이언트단의 Android 또는 iOS 네이티브 코드는 익히는데 시간은 없고, 다양한 스마트 기기에 대응을 했으면 한다. 그리고 JavaScript를 어느 정도 할 수 있다. 이때 생각할 수 있는 대안이 "웹앱 사이트" 또는 "모바일 웹앱"을 만드는 것이다. 그러나 예전 jQuery 코드를 생각해 보자. 조금만 복잡해 지면 쉽게 스파게티가 되고, 테스트 코드는 엄두도 못내며 결국 유지보수의 골칫덩어리로 남게 된다. 

그렇다면 자바에서 Spring Framework과 같이 DI가 되면서 MVC처럼 모듈화 개발을 가능하게 해주는 클라이언트단 프레임워크는 없을까? 처음엔 Backbone.js를 살펴 보았고, 예로 Trello와 같은 서비스에서 사용을 하고 있다. 하지만 코드의 길이가 길어지고 불필요한 중복코드의 남발이 야기되어 좀 더 간결하면서 예전 Flex Framework과 같은 것은 없을지 고민해 보았다. HTML을 확장해서 사용해도 자동 해석을 하여 브라우져가 이해할 수 있는 HTML로 바꾸어 주면 되는 그런 프레임워크 말이다. 역시 구글 형님들도 똑같은 고민을 하고 프로젝트에 적용하고 놀라운 효과를 본 새로운 프레임워크를 공식 런칭하게 되니 이름하여 AngularJS





2. 새로운 "I/O Machine" 에 대한 고민

  서비스에서 요하는 기능중 가장 중요한 부분은 Push 기능이 아닐까 싶다. 실시간으로 변화하는 정보를 수많은 유저에게 바로 Push 서비스해줄 수 있는 서버가 필요하다. 하지만 Push를 위하여 별도의 Port를 사용하긴 싫고 웹앱이므로 요청하였던 HTTP Port를 사용하고 싶다면 어떤 것을 선택해야 할까?

서버단이 Java라면 vert.x도 고려해 볼 만하다. 하지만 아직 레퍼런스와 문서가 부족하다. 하지만 여기에 우리에게 구세주와 같은 세로운 I/O 머신이 나왔으니 V8엔진을 기반으로 돌아가는 Node.js이다. Node.js를 사용하는 가장 큰 목적중 하나가 Socket.io와 같은 이벤트 기반의 Push 모듈들이 아닐까 생각이든다. Node.js는 모듈기반으로 필요한 것들을 추가하여 사용할 수 있다. 이를 위하여 NPM(Node Package Manager)이 존재하고 방대한 모듈과 레퍼런스들이 존재한다. 또한 Chrome에 적용된 V8 자바스크립트 해석기를 달고 있으므로 모든 코딩은 JavaScript 만으로 이루어진다





3. 스키마에 자유로운 "Store"에 대한 고민 

  서비스를 빠르게 만들고 싶은데 개발을 진행하다 보면 자주 테이블이 변경되고 이에 맞추어 서버코드들도 수정을 해주거나 새롭게 테이블을 생성해 주는 번거로운 작업들을 거친다. 따라서 스키마가 자유로운 데이터베이스를 원한다. 그리고 서비스이므로 사용자의 데이터를 많이 쌓아 두고 싶다. 이를 통해 향후 마케팅 용으로 사용하려 한다. 이왕이면 클라이언트/서버가 모두 자바스크립트이므로 데이터베이스 핸들링도 자바스크립트 이면 좋겠다. 이를 위해 새롭게 나온 NoSQL 이 있으니 이름하여 MongoDB. JSON형태로 데이터를 저장하고 정규화 없이 도큐먼트 형태로 저장을 할 수 있어서 스키마의 제약이 없다. 또한 테라급의 데이터 정도는 MongoDB를 Sharding구조로 만들어 MapReduce를 이용하여 처리할 수 있다. 그리고 Node.js 처럼 V8 해석기를 탑재하여 데이터 핸들링을 JavaScript로 할 수 있다. 마치 Oracle의 SqlPlus에서 Sql을 수행하듯 MongoDB가 정의한 형식의 쿼리를 수행할 수 있다. 





4. Node.js를 기업용 WAS(Web Application Server)로 만들기  

  초창기 Java를 떠올려 보자. 처음엔 Applet에 열광하여 업무를 애플릿으로 만든적이 있다. 그때 서버단은 Apache + PHP정도 였을 것이다. 그리고 SUN에서 J2EE 스팩을 발표하면서 Tomcat, JBoss, WebSphere, WebLogic 같은 WAS가 나왔으며, 이 또한 C계열의 TP-Monitor처럼 Java기반 I/O Machine 이 나오게 된 것이다. Java진영의 WAS는 나름의 공통된 스팩을 기반으로 웹을 이끌어 갈 수 있는 새로운 I/O 머신으로 떠올랐고, 기업에서 요구하는 Transaction 처리에 대한 스팩도 제안하여 많이 사용하게 되었다. 최근에는 Tomcat같은 Servlet 엔진만 갖춘 WAS에 Spring Framework를 사용하면 충분히 기업용 Transaction을 처리하는 업무를 개발할 수 있는 환경까지 오게 되었다. 그러나 Java의 WAS가 web1.0의 중심 머신이었다면 Node.js기반의 WAS가 web2.0의 중심 머신으로 되기 위한 조건은 무엇일까?

  결국 Node.js위에 올라가는 Java의 J2EE 스팩과 같은 견고한 스팩이 필요하다. 현재까지는 아직 그러한 스팩이 나오진 않으나 Java의 Servlet 스팩에 비견할 만한 프레임워크가 나왔으니 그것이 Express.js 이다. 이제 web2.0의 I/O 머신은 Node.js가 될 것이고, 여기에 웹에 대한 표준 스팩구현체는-사실 Node.js를 위한 공통된 스팩은 없다- 아니지만 가장 많이 사용하는 Express.js를 통하여 Node.js가 Tomcat과 같은 기능을 보유하게 되었다.




Angular.js -> Node.js + Express.js -> MongoDB 를 사용하게 되면 

  - JavaScript로 모두 개발을 한다. 클라이언트/서버 분리하여 개발할 필요가 없다. 그냥 FullStack JavaScript개발을 하면 된다

  - 클라이언트 개발이 MV* 개발이 되어 서버 개발에 익숙한 개발자가 쉽게 접근할 수 있다 

  - 서버에서 Push 기능을 쉽게 적용할 수 있으므로 다양한 서비스의 응용이 가능하다 

  - 자바의 서블릿 엔진 구현체인 Tomcat과 같이 충실한 RESTful I/O 머신을 쉽게 구현할 수 있다

  - 서버의 멀티 쓰레드 문제에서 해방될 수 있다. Asynch의 세상에서 놀자 

  - 데이터 스키마를 미리 정의하지 않고 에자일하게 개발을 진행할 수 있다

  - 큰 규모의 데이터를 저장하고 처리 할 수 있다  



그러나 우리에게 남은 과제는 이것들을 어떻게 배우고 사용하는지 잘 모른다는 것이다. 각자를 심도 있게 배우는 것은 별도의 가이드북을 따로 보길 바란다. 대신 여기서는 실전 서비스를 어떻게 하면 빠르게 만들 수 있을지 노하우를 적어보려한다. 낮에는 회사에서 업무를 보면서 향후 스타트업을 준비하는 개발자 또는 사람들에게 좀 더 이로운 가치를 전달해 주고 싶은 개발자가 3개월안에 개발할 수 있는 서비스를 이 책을 통해 런칭할 수 있는 것이 가능해 질 수 있길 바란다. 그래서 책을 쓰면서 동시에 작은 서비스를 만들어 보려 한다. 책은 가이드이고 그물일 뿐이다. 실제 돌아가는 서비스가 물고기이다. 즉, 실제 서비스를 낚지 못하면 이 책의 가치도 없다고 생각한다. 실제로 이것을 증명해 보이고 싶다. 



Getting Start MEAN

          


posted by 윤영식
2013. 9. 28. 16:18 AngularJS/Concept


posted by 윤영식
2013. 9. 4. 14:05 AngularJS/Start MEAN Stack

Mobile 서비스 개발을 위하여 JavaScript 기반의 기술 스택을 선택하여 사용하다 보니 1년사이에 MongoDB, Express.js, Angular.js, Node.js를 공부하고 솔루션에 적용하게 되었다. 그간 해당 기술들을 추상화한 프레임워크들을 사용하여 개발을 진행해 왔다. 그러나 가끔 풀리지 않는 문제에 대한 근원적인 해결책을 찾는데는 다시 처음부터 추상화 내역에 대한 이해가 바탕이 되지 않으면 풀리지 않았다. 


즉, 기본 스택을 추상화한 스택사용시에는 추상화 영역에 대한 사용경험과 전반적인 이해가 없이는 오히려 시간공수만 더 늘어나는 격이 된다. 예로 Express와 MongoDB를 좀 더 수월하게 사용키위하여 Trains, Sails Framework들을 사용해 보았고 나름의 장점은 있으나 왠지 남의 옷을 입은듯한 느낌이랄까? 문제 발생시 빠른 대응이 어려웠다 (개인적으론 Trains Framework의 Node.js단의 IoC 방식을 좋아한다)


따라서 소규모 팀으로 개발하면서 하나의 가치를 빠른 시간안에 전달하고자 한다면 기본 Stack기반으로 진행하면서 필요한 시점에 스스로 추상화 모듈을 만들어 사용하는 방법이 좋지 않을까 생각한다. (Don't Invent Wheel 이니 초기엔 쓸만한 것을 GitHub에서 찾아 응용하고 없으면 개발하자) 그런 의미에서 mean.io 에서 기본 Stack에 충실하면서 현재 사용되는 최신 개발 기술들 - Jade, Bootstrap, Mongoose, Bower, Grunt 같은 - 도 함께 접목되어 적당히 어려운 상태이므로 개발시 집중(Flow)을 가능케 하는 구조를 가지고 있다. (너무 어려우면 흥미를 읽고, 너무 쉬우면 긴장감이 떨어진다. 적당히 어려우면 흥미와 긴장감을 주어 개발시 집중을 가능케 한다) 



1. 설치

  - 홈페이지에서 zip 파일 다운로드 

  - 프로젝트로 이동해서 

$ cd mean


// 사전에 node.js 설치 

// Node, npm 설치 shell

$ npm install .

 

// 사전에 mongodb 설치하고 start

$ grunt



2. 접속 

  - http://localhost:3000/

    + Sign Up 할 수 있다

    + 테스트 글을 CRUD 할 수 있다 

    


  - MongoDB 

> show collections

articles

sessions

system.indexes

users


해당 스택을 통해 SPA(Single Page Application) 개발에 대한 Lean(신속한) 스타트업을 시도하자.

 


<참조>

  - mean.io

posted by 윤영식
2013. 8. 22. 15:36 AngularJS/Concept

AngularJS의 서비스 종류는 여러가지가 있습니다. Factory만 알았다면 Costant, Value, Provider 그리고 서비스의 확장 방법등에 대해 알아보자. "AngularJS Service is an Application Brain"



1. 개념

  - 서비스는 여러 종류가 있다

  - 항시 Singleton 이다. 즉, 하나의 Instance만 존재한다 



2. Constant

  - 공통으로 사용하는 환경값을 지정할 때 상수를 지정한다 

  - 사용자 정의 Directive에서 환경값을 가져다 쓰고 싶을 때 유용하다

// script 

app = angular.module("app", []);


app.controller('MainCtrl', function($scope, fooConfig) {

  $scope.fooConfig = fooConfig;

});


app.constant('fooConfig', {

  config1: true,

  config2: "Default config2"

});


// html

<!DOCTYPE html>

<html ng-app="app">

<head>

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>

<meta charset=utf-8 />

<title>JS Bin</title>

</head>

  <body ng-controller="MainCtrl">

    config1: {{fooConfig.config1}}

    <br />

    config2: {{fooConfig.config2}}

  </body>

</html>



3. Value

  - Value Service는 Constant Service와 유사하지만 최초 한번은 변경을 할 수 있다. Factory 서비스의 작은 동생뻘이다 

  - Directive에서 사용하기 유용하다 

  - Value를 가지고 계산할 수는 없다 

// script 

app = angular.module("app", []);


app.controller('MainCtrl', function($scope, fooConfig) {

  $scope.fooConfig = fooConfig;

  // 최초 한번의 변경이 가능

  angular.extend(fooConfig, {config3: "I have been extended"}); 

});


app.value('fooConfig', {

  config1: true,

  config2: "Default config2 but it can changes"

});


// html 

<!DOCTYPE html>

<html ng-app="app">

<head>

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>

<meta charset=utf-8 />

<title>JS Bin</title>

</head>

  <body ng-controller="MainCtrl">

    config1: {{fooConfig.config1}}

    <br />

    config2: {{fooConfig.config2}}

    <br />

    config3: {{fooConfig.config3}}

  </body>

</html>



4. Factory

  - 가장 일반적으로 사용하는 서비스 종류이다 

  - 팩토리는 어떤 형태의 데이터타입이라도 리턴할 수 있다. 리턴된 오브젝트를 가지고 작업을 한다 

  - 단, 리턴된 오브젝트의 값을 변경하면 해당 팩토리의 인스턴스를 사용하는 모든 곳에 변경값이 반영된다 

  - Revealing Module Pattern 식으로 작성을 한다 

// script 

app = angular.module("app", []);

app.controller('MainCtrl', function($scope, foo) {

  $scope.foo = foo;

});


// revealing module pattern 방식의 팩토리 서비스

app.factory('foo', function() {

  var thisIsPrivate = "Private";

  function getPrivate() {

    return thisIsPrivate;

  }

  return {

    variable: "This is public",

    getPrivate: getPrivate

  };

});


// html

<!DOCTYPE html>

<html ng-app="app">

<head>

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>

<meta charset=utf-8 />

<title>JS Bin</title>

</head>

  <body ng-controller="MainCtrl">

    public variable: {{foo.variable}}

    <br />

    private variable (through getter): {{foo.getPrivate()}}

  </body>

</html>



5. Service 

  - "service" service는 Factory service와 유사사지만 생성자명칭(== 서비스명칭)을 넘기면 자동으로 new를 통하여 생성된다 

    즉, Factory 처럼 오브젝트의 리턴이 필요없다 

  - 하기 예는 완전히 동일하다. 주의 할 것은 factory안에서 또는 service사용시 new을 여러번 하여도 반드시 한번만 인스턴스화 한다

    즉, Singleton 패턴으로 객체 하나만이 생성된다 

// script 

app = angular.module("app", []);


// Service를 통해서 생성 

app.controller('MainCtrl', function($scope, foo) {

  $scope.foo = foo;

});


app.service('foo', function() {

  var thisIsPrivate = "Private";

  this.variable = "This is public";

  this.getPrivate = function() {

    return thisIsPrivate;

  };

});


// Factory를 통해서 생성 : MainCtrl 에서 foo 파라미터 값과 하위 로직을 foo2로 바꿔도 동일 결과 

app.controller('MainCtrl', function($scope, foo2) {

  $scope.foo = foo2;

});


app.factory('foo2', function() {

  return new Foobar();

});


function Foobar() {

  var thisIsPrivate = "Private";

  this.variable = "This is public";

  this.getPrivate = function() {

    return thisIsPrivate;

  };

}


// 또는 펑션을 넘길 수도 있다 : : MainCtrl 에서 foo 파라미터 값과 하위 로직을 foo3로 바꿔도 동일 결과 

app.controller('MainCtrl', function($scope, foo3) {

  $scope.foo = foo3;

});


app.service('foo3', Foobar);


// html 

<!DOCTYPE html>

<html ng-app="app">

<head>

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>

<meta charset=utf-8 />

<title>JS Bin</title>

</head>

  <body ng-controller="MainCtrl">

    public variable: {{foo.variable}}

    <br />

    private variable (through getter): {{foo.getPrivate()}}

  </body>

</html>

 


6. Provider

  - Provider는 factory의 큰형님 뻘이다 

  - $get fuction을 가지고 있어야 한다. 만일 Provider 명칭이 foo이고 controller에 inject될 때 실제는 $get function의 결과값이 inject되는 것이다. 왜 factory로 사용하면 간편한데 굳이 이렇게 할까?

  - 이유인즉슨, config function을 통해서 provide에 대한 환경설정을 할 수 있기 때문이다 

  - 예를 보자 

    1) thisIsPrivate 변수가 $get 펑션 밖에 위치한다 

    2) 따라서 setPrivate 메소드를 통하여 thisIsPrivate 변수값을 변경할 수 있다 

    3) 이런게 하는 이유는 우리가 필요로 하는 다양한 환경값을 바꾸어서 환경설정을 하고 싶기 때문이다

    4) Provider명칭이 'foo' 이므로 app.config 에서 'fooProvider' 명을 준다 

// script 

app = angular.module("app", []);

app.controller('MainCtrl', function($scope, foo) {

  $scope.foo = foo;

});


app.provider('foo', function() {

  var thisIsPrivate = "Private";

  return {

    setPrivate: function(newVal) {

      thisIsPrivate = newVal; 

    },

    

    $get: function() {

      function getPrivate() {

        return thisIsPrivate;

      }


      return {

        variable: "This is public",

        getPrivate: getPrivate

      };

    } 

  };

});


app.config(function(fooProvider) {

  fooProvider.setPrivate('New value from config');

});


// html 

<!DOCTYPE html>

<html ng-app="app">

<head>

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>

<meta charset=utf-8 />

<title>JS Bin</title>

</head>

  <body ng-controller="MainCtrl">

    public variable: {{foo.variable}}

    <br />

    private variable (through getter): {{foo.getPrivate()}}

  </body>

</html>



7. Decorator (보너스1)

  - 기존 서비스에 새로운 펑션을 넣고 싶을 경우 사용한다

  - 예를 보자 

    1) $provide는 모든 서비스들을 내부적으로 생성할 때 사용한다 

    2) $provide.decorator 를 통해서 서비스를 확장할 수 있다. 

        첫번째 인자는 서비스 명칭, 두번째는 콜백으로 서비스 인스턴스인 $delegate을 받는다 

    3) $delegate에 greet 라는 메소드를 확장 정의 한다 

    4) 만일 3rd party 라이브러리 사용시 확장하고 싶을 경우 decorator를 사용한다 

// script 

app = angular.module("app", []);


app.controller('MainCtrl', function($scope, foo) {

  $scope.foo = foo;

});


app.factory('foo', function() {

  var thisIsPrivate = "Private";

  function getPrivate() {

    return thisIsPrivate;

  }

  return {

    variable: "This is public",

    getPrivate: getPrivate

  };

});


app.config(function($provide) {

  $provide.decorator('foo', function($delegate) {

    $delegate.greet = function() {

      return "Hello, I am a new function of 'foo'";

    };

    

    return $delegate;

  });

});


// html 

<!DOCTYPE html>

<html ng-app="app">

<head>

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>

<meta charset=utf-8 />

<title>JS Bin</title>

</head>

  <body ng-controller="MainCtrl">

    public variable: {{foo.variable}}

    <br />

    private variable (through getter): {{foo.getPrivate()}}

    <br />

    greet: {{foo.greet()}}

  </body>

</html>



8. 새로운 인스턴스 생성하기 (보너스2)

  - 팩토리 메소드를 두어서 호출시 마다 무언가 새로 생성하고 싶을 경우 사용한다 

  - 예를 보자 

    1) Function 1급 class인 Person을 만든다 

    2) MainCtrl, SecondCtrl 각각 getById(1) 을 호출하지만 서로 다른 인스턴스가 전달되었다

    3) "Update It" 버튼을 클릭하면 두번째 열만 "Dave - Canada" 로 바뀜을 확인 할 수 있다 

// script 

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

app.controller('MainCtrl', function($scope, personService) {

  $scope.aPerson = Person.getById(1);

});

app.controller('SecondCtrl', function($scope, personService) {

  $scope.aPerson = Person.getById(1);

  $scope.updateIt = function() {

    $scope.aPerson.update();

  };

});


// Our class

function Person( json ) {

  angular.extend(this, json);

}


Person.prototype = {

  update: function() {

    // Update it (With real code :P)

    this.name = "Dave";

    this.country = "Canada";

  }

};


Person.getById = function( id ) {

  // Do something to fetch a Person by the id

  return new Person({

    name: "Jesus",

    country: "Spain"

  });

};


// Our factory

app.factory('personService', function() {

  return {

    getById: Person.getById

  };

});


// html

<!DOCTYPE html>

<html ng-app="app">

<head>

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>

<meta charset=utf-8 />

<title>JS Bin</title>

</head>

  <body>

    <p ng-controller="MainCtrl">{{aPerson.name}} - {{aPerson.country}}</p>

    <div ng-controller="SecondCtrl">

      {{aPerson.name}} - {{aPerson.country}}

      <button ng-click="updateIt()">Update it</button>

    </div>

  </body>

</html>

  - 위의 script 코드를 CoffeeScript로 하면 class 표현을 쉽게 할 수 있다 

app.controller 'MainCtrl', ($scope, personService) -> $scope.aPerson = personService.getById(1) app.controller 'SecondCtrl', ($scope, personService) -> $scope.aPerson = personService.getById(2) $scope.updateIt = () -> $scope.aPerson.update() class Person constructor: (json) -> angular.extend @, json update: () -> @name = "Dave" @country = "Canada" @getById: (id) -> new Person name: "Jesus" country: "Spain" app.factory 'personService', () -> { getById: Person.getById 

}



<참조>

  - 원문 : Understanding Service Types

  - 가장 잘 설명된 글 : http://stackoverflow.com/questions/15666048/angular-js-service-vs-provider-vs-factory (강추)

  - 관련글 : GDG Korea WebTech 고재도님 링크

  - AngularJS Creating Service 매뉴얼

  - 이미지 : AngularJS Large Scale Architecture

  - JavaScript에서 객체를 만드는 3가지 방법

  - $provider를 통한 생성

posted by 윤영식
2013. 8. 21. 18:14 AngularJS/Concept

AngularJS를 사용할 때 가장 햇갈리는 부분이면서 중급으로 가기위하여 반드시 알고 넘어가야 하는 부분이 Scope 오브젝트에서 제공하는 $watch, $apply, $digest 메소드이다. 이들에 대해서 알아보자 



1. Event-Loop와 Angular Way

  - 브라우져는 사용자의 동작에 반응(이벤트)하기 위하여 이벤트 루프를 돌며 이벤트에 반응한다

  - Angular는 "angular context"라는 것을 만들어서 이벤트 루프를 확장한다 



2. $watch 목록

  - UI와 무언가를 바인딩하게 되면 반드시 $watch list(목록)에 $watch를 넣는다 

  - $watch는 모델(model)에 변경이 있는지를 항시 감시하는 역할을 한다

  - user와 pass에 대한 model 변경을 감시하기 위하여 두개의 $watch가 만들어져서 $watch list에 첨부된다 

User: <input type="text" ng-model="user" /> 

Password: <input type="password" ng-model="pass" />

  - $scope에 모델을 두개 만들고, html에서 한개의 model만 사용할 경우는 $watch가 한개만 만들어져서 $watch list에 첨부된다 

// script 

app.controller('MainCtrl', function($scope) { $scope.foo = "Foo"; $scope.world = "World"; });


// html

Hello, {{world}}

  - 배열일 경우 객체의 멤버를 각각 {{}}로 사용했으므로, name 1개, age 1개의 $watch가 만들어 진다. 예로 people 배열이 10개면 

    10 (배열) * 2 (name, age) + 1 (ng-repeat자체) = 총 21개의 $watch가 만들어져서 $watch list에 첨부된다 

// script

app.controller('MainCtrl', function($scope) { $scope.people = [...]; });


// html

<ul>

<li ng-repeat="person in people"> {{person.name}} - {{person.age}} </li> </ul>

  - Directives 만들 때도 바인딩이 있으면 당연히 $watch가 생성된다. 그럼 언제일까? template이 로딩될때 즉, linking 절차일때 필요한 $watcher가 생성된다 



3. $digest loop 

  - 브라우져가 "angular context"에 의하여 관리되어 질 수 있는 이벤트를 받게 될 때, $digest loop 가 작동되어 진다 

  - $digest loop은 두개의 작은 루프로 만들어진다. 하나는 $evalAsync queue를 처리하고, 다른 하나는 $watch list를 처리한다 

  - 처리되어지는 것은 무엇일까? 

    $digest는 $watch list를 루핑돌면서 model 변경을 체크하고, $watch에 등록된 listener handler를 수행한다

  - 이때 dirty-checking이 이루어지는데, 하나가 변경되면 모든 $watch를 루핑돌고 다시 체크해 보고 변화가 없을 때가지 루핑을 돈다

    그러나 무한 루프를 방지하기 위하여 기본적으로 최대 10번의 루핑을 돈다.  

    그리고 나서 $digest loop가 끝났을 때 DOM을 업데이트 한다. (즉, 변경감지시 즉시 DOM 반영이 아닌 Loop끝났을 때 반영함) 

  - 예를 보자. 

    1) $watch를 하나 가진다. ng-click의 경우는 클릭하면 펑션이 수행되므로 변화가 발생하지 않기 때문에 {{ name}}에 대해 1개만 생성

    2) 버튼을 클릭한다

    3) 브라우져는 "angular context"로 들어가서 처리될 이벤트를 받는다 

    4) $digest loop 가 수행되고, 변경을 체크하기 위하여 $watch에게 문의를 한다 

    5) $watch는 $scope.name에 변경이 있으면 변경을 보고한다. 그러면 다시 한번 $digest loop가 강제 수행된다

    6) $digest loop 돌면서 더 이상 변경된 것이 없다

    7) 브라우져는 통제권을 돌려받고 $scope.name 변경값을 반영하기 위하여 DOM을 업데이트 한다 

    

    여기서 중요한 것은 "angular context" 안으로 들어간 모든 이벤트는 $digest loop를 수행한다 는 것이다. 

    즉, 예로 input에 write하는 매번, 모든 $watch를 돌면서 변경을 체크하는 루프가 수행되는 것이다 

// script

app.controller('MainCtrl', function() { $scope.name = "Foo"; $scope.changeFoo = function() { $scope.name = "Bar"; } });


// html

{{ name }} <button ng-click="changeFoo()">Change the name</button>



4. $apply를 통하여 angular context로 들어가기 

  - 이벤트가 발생할 때 $apply를 호출하게 되면, 이벤트는 "angular context"로 들어가게 된다 

  - 그런데 $apply를 호출하지 않으면 "angular context" 밖에서 이벤트는 수행하게 된다 

  - 기존 ng-click 같은 이미 만들어져 있는 Directive들은 이벤트를 $apply 안에 랩핑한다. 

     또는 ng-model="foo"가 있다면 'f'를 입력하면 $apply("foo='f';")식으로 랩핑하여 호출한다  



5. Angular는 우리를 위해 자동으로 $apply를 호출해 주지 않는다 

  - jQuery의 예를 보면 jQuery 플러그인에서 이벤트에 대해 $apply가 호출되지 않기 때문에 발생한 이벤트는 "angular context"로 못들어가게 되므로 "$digest loop"도 수행되지 않게 된다. 결국 DOM의 변경이 발생하지 않는다 

  - 예를 보자 : <clickable> 앨리먼트를 클릭할 때마다 foo, bar 값이 1씩 증가하는 Directive이다 

     1) 클릭을 해보면 1씩 증가를 할까?  증가 하지 않는다 

     2) click 이벤트는 공통 이벤트이고 $apply로 감싸(랩핑)지지 않았기 때문이다 

     3) 결국 "angular context"에 못들어가니 "$digest loop"가 수행되지 않으니 $watch도 수행되지 않기 때문에 DOM 변경은 없다. 

         단, Click을 하게 되면 값은 1씩 증가한다. 즉 $scope값 변경이 DOM에 반영되지 않는다

app = angular.module('app', []); app.controller('MainCtrl', function($scope) { $scope.foo = 0; $scope.bar = 0; $scope.hello = "Hello"; $scope.setHello = function() { $scope.hello = "World"; }; });

app.directive('clickable', function() { return { restrict: "E", scope: { foo: '=', bar: '=' }, template: '<ul style="background-color: lightblue"><li>{{foo}}</li><li>{{bar}}</li></ul>', link: function(scope, element, attrs) { element.bind('click', function() { scope.foo++; scope.bar++; }); } } });

  - 예제 : http://jsbin.com/opimat/2/edit 를 보자 

    

    1) <clickable> 파란색 영역을 2번 클릭하면 숫자값의 변경에 대해서 DOM에 반영되지 않는다. 즉, 화면은 그대로 0 이다

    2) <button>의 ng-click을 하게되면 setHello()가 호출되고 자동으로 $apply에 감싸서 수행된다

    3) 이벤트가 "angular context"로 들어가면 "$digest loop"가 수행되면서 모든 "$watch list"를 돌면서 변경을 체크한다

    4) 변경된 값이 있다면 DOM을 업데이트 하므로 <clickable>의 foo, bar값이 현재 2번 클릭하여 2이므로 DOM으로 2로 변경한다 

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>

<meta charset=utf-8 />

<title>Directive example</title>

</head>

<body ng-controller="MainCtrl">

  <clickable foo="foo" bar="bar"></clickable>

  <hr />

  

  {{ hello }} <button ng-click="setHello()">Change hello</button>

</body>

</html>


  - 이벤트를 "angular context"로 들어가서 처리하게 해주는 방법-1

    이벤트 안에서 $apply()를 호출한다 

    $apply는 $scope(또는 Directive link의 scope)의 메소드이다. 따라서 $apply를 호출하여 "$digest loop"를 강제로 수행시킨다. 

element.bind('click', function() { scope.foo++; scope.bar++; scope.$apply(); 

});


  - 이벤트를 "angular context"로 들어가서 처리하게 해주는 방법-2

    여기서는 단순 값의 증가이지만 내부적으로 서버요청하다 에러가 발생하면 "방법-1"에서는 "angular context"가 전혀 에러를 알 수 없다

    방법-2처럼 감싸서(Wrapped) 사용해야 에러가 발생하면 "angular context"가 알 수 있도록 대응을 할 수 있다 

element.bind('click', function() { scope.$apply(function() { scope.foo++; scope.bar++; }); 

})


  - jQuery 플로그인을 사용하면 $apply 호출을 통해 "$digest loop"가 수행시켜 DOM 업데이트를 할 수 있다 



6. 우리 것은 $watch를 사용하자 

  - 우리의 모든 바인딩은 DOM 업데이트를 위하여 각각 $watch를 생성한다는 것을 알고 있다. 

     우리가 직접 $watch를 만들고 싶다면 어떨까?

  - 예를 보자

    1) $watch 첫번째 인자는 String, function이 가능하다. 여러개 모델 감시는 ; 로 구분한다 

    2) 두번째 인자는 첫번째 인자의 값이 변경되면 수행될 Handler 이다 

    3) 여기서는 <input>의 값을 변경할 때마다 updated 값이 1씩증가하여 {{updated}}에 값이 반영된다. 즉, DOM이 업데이트 된다

    4) View에서 값이 변경되어 Controller가 수행될 때 $watch를 발견하게 되고 바로 $watch가 수행된다.

// script 

app.controller('MainCtrl', function($scope) { $scope.name = "Angular"; $scope.updated = -1; $scope.$watch('name', function() { $scope.updated++; }); });


// html

<body ng-controller="MainCtrl">

<input ng-model="name" /> Name updated: {{updated}} times. </body>

    5) Controller가 수행되면서 $watch가 무조건 수행되는 것을 막기 위한 방법으로 두번째 인자 펑션의 파라미터 값으로 new, old 값을 받아 비교한다. 즉 최초에는 수행되지 않게 된다

$scope.$watch('name', function(newValue, oldValue) { if (newValue === oldValue) { return; } // AKA first run $scope.updated++

});

   6) 만일 값변경을 체크하는 것이 Object일 경우는 $watch의 3번째 인자값으로 true를 준다. 즉, Object의 비교를 수행도록 한다

app.controller('MainCtrl', function($scope) {

$scope.user = { name: "Fox" }; $scope.updated = 0; $scope.$watch('user', function(newValue, oldValue) { if (newValue === oldValue) { return; } $scope.updated++; }, true); });


주의할 점은 한 페이지에 $digest loop를 돌면서 체크하는 $watch가 2000~3000개 정도 생성되는 경우라면 성능상에 이슈가 있겠지만, 일반적인 경우 dirty-checking 하는 $digest loop은 상당히 빠르다



<참조>

  - 원문 : Watch how the apply runs a digest

  - 불필요한 $watch 제거하여 성능향상 시키기

  - AngularJS Concept 중에서

# Runtime 

The diagram and the example below describe how Angular interacts with the browser's event loop. 


일반적인 세상)

1. The browser's event-loop waits for an event to arrive. An event is a user interaction, timer event, or network event (response from a server). 

2. The event's callback gets executed. This enters the JavaScript context. The callback can modify the DOM structure. 

3. Once the callback executes, the browser leaves the JavaScript context and re-renders the view based on DOM changes. 


앵귤러 세상)

Angular modifies the normal JavaScript flow by providing its own event processing loop. This splits the JavaScript into classical and Angular execution context. Only operations which are applied in Angular execution context will benefit from Angular data-binding, exception handling, property watching, etc... You can also use $apply() to enter Angular execution context from JavaScript. Keep in mind that in most places (controllers, services) $apply has already been called for you by the directive which is handling the event. An explicit call to $apply is needed only when implementing custom event callbacks, or when working with third-party library callbacks. 


1. Enter Angular execution context by calling scope.$apply(stimulusFn). Where stimulusFn is the work you wish to do in Angular execution context. 

2. Angular executes the stimulusFn(), which typically modifies application state. 

3. Angular enters the $digest loop. The loop is made up of two smaller loops which process $evalAsync queue and the $watch list. The $digest loop keeps iterating until the model stabilizes, which means that the $evalAsync queue is empty and the $watch list does not detect any changes. 

4. The $evalAsync queue is used to schedule work which needs to occur outside of current stack frame, but before the browser's view render. This is usually done with setTimeout(0), but the setTimeout(0) approach suffers from slowness and may cause view flickering since the browser renders the view after each event. 

5. The $watch list is a set of expressions which may have changed since last iteration. If a change is detected then the $watch function is called which typically updates the DOM with the new value. 

6. Once the Angular $digest loop finishes the execution leaves the Angular and JavaScript context. This is followed by the browser re-rendering the DOM to reflect any changes. H


예)

ere is the explanation of how the Hello world example achieves the data-binding effect when the user enters text into the text field. 

1. During the compilation phase: 

    1. the ng-model and input directive set up a keydown listener on the <input> control. 

    2. the {{name}}interpolation sets up a $watch to be notified of name changes. 

2. During the runtime phase: 

    1. Pressing an 'X' key causes the browser to emit a keydown event on the input control. 

    2. The input directive captures the change to the input's value and calls $apply("name = 'X';") to update the application model inside the Angular execution context. 

    3. Angular applies the name = 'X'; to the model. 

    4. The $digest loop begins 

    5. The $watch list detects a change on the name property and notifies the {{name}} interpolation, which in turn updates the DOM. 

    6. Angular exits the execution context, which in turn exits the keydown event and with it the JavaScript execution context. 

    7. The browser re-renders the view with update text.


posted by 윤영식
2013. 8. 21. 11:41 AngularJS/Concept

AngularJS에 대해 어떻게 생각하시나요? 그냥 webpage를 만드는 Front-end 단의 JavaScript Framework 정도로 생각하시나요? AngularJS는 클라이언트의 브라우져에서 SPA(Single Page Application)를 구현하기 위한 프레임워크입니다. Webpage를 만드는 것이 아니라 SPA의 A처럼 애플리케이션을 만드는 프레임워크라는 것이지죠. Adobe의 Flex 프레임워크와 대비하여 생각해도 되지 않을까 합니다. AngularJS로 개발한다면 새로운 관점에서 바라보아야 합니다. Angular Way의 길에 들어오세요



1. 페이지를 디자인 하지 말고 DOM(Document Object Model) 조작을 통해 페이지를 변경하라 

  - "나는 몇개의 DOM 조각을 가지고 있다 이것을 가지고 뭔가 동작하는 것을 만들고 싶다"라는 생각에서 AngularJS는 출발한다 

  - 완성하고 싶은 것이 있으면 애플리케이션 아키텍쳐 디자인을 한 다음 마지막에 화면 디자인을 한다 



2. AngularJS 와 함께 jQuery 에 대해서 말하지 마라 

  - AngularJS를 사용하면 jQuery 를 바탕으로 개발하는 것은 배제하길 바란다. Angular Way를 가라~~

  - jQuery 플러그인을 AngularJS에서 사용하기 위해 $apply를 남발하여 코드를 꼬아 놓지 말자. jQuery 플러그인 대부분은 약간의 코드조각으로 AngularJS에서 재작성되어 질 수 있다 (Directive로 만들어서 사용하기)



3. 아키텍쳐라는 단어를 항상 생각하라

  - SPA (Sinlge Page Application)는 application 이다. webpage가 아니다. 서버사이드 개발과 똑같이 생각하라

  - 따라서 모듈화 하고 확장성 있고 테스트 가능하게 개발해야 한다 


3-1. 화면은 공식적으로 표현되는 것 

  - jQuery 경우를 보자 

<ul class="main-menu">
    <li class="active">
        <a href="#/home">Home</a>
    </li>
    <li>
        <a href="#/menu1">Menu 1</a>
        <ul>
            <li><a href="#/sm1">Submenu 1</a></li>
            <li><a href="#/sm2">Submenu 2</a></li>
            <li><a href="#/sm3">Submenu 3</a></li>
        </ul>
    </li>
    <li>
        <a href="#/home">Menu 2</a>
    </li> 

</ul>


// JavaScript 

$('.main-menu').dropdownMenu();

  - 위의 경우를 단순한 AngularJS의 Directive로 표현한다. 이렇게 하면 jQuery처럼 HTML과 Javascript를 분리할 필요가 없고 해당 <ul> HTML 태그가 dropdown-menu임을 바로 알 수 있다.  

<ul class="main-menu" dropdown-menu>
    ... 

</ul>

  - 애플리케이션 개발시 반은 jQuery 반은 AngularJS 스타일로 - half jQuery, half AngularJS - 절대로 절대로 개발하지 말라

  - 디자인을 하지 말고, Markup을 해라. 즉, 아키텍쳐링을 하고 그 다음에 디자인을 하라. 


3-2. Data Binding

  - DOM을 조작(첨부,삭제)하는데 있어서 Two Way Binding을 적극적으로 사용하라 

  - jQuery 의 경우를 보자 : append하고 delete 하는 조작들을 넣어야 한다. 

$.ajax({
  url: '/myEndpoint.json',
  success: function ( data, status ) {
    $('ul#log').append('<li>Data Received!</li>');
  

});

<ul class="messages" id="log"</ul>

  - AngularJS 를 보자 : $scope를 통하여 controller와 view 간의 데이터가 삭제 되거나 첨부되는 것을 보다 쉽고 깔끔하게 처리해 준다

// controller 코드
$http( '/myEndpoint.json' ).then( function ( response ) {
    $scope.log.push( { msg: 'Data Received!' } ); 

});


// html 코드 

<div class="messages">
    <div class="alert" ng-repeat="entry in log">
        {{ entry.msg }}
    </div> 

</div>


3-3. 모델객체와 화면과 분리되어 있다

  - $scope를 통하여 view와 controller간 모델역할을 수행한다 

  - 이 방법은 view와 완전 독립적이어서 separation of concern을 유지하고 테스트 가능하게 만들준다


3-4. 관심의 분리 (SoC, separation of concern)

  - 아키텍쳐링을 하여 개발하면 관심 영역별로 분리하여 유지할 수 있다

  - 화면(view)은 공식적인 표현되는 것이고, 모델(model)은 데이터를 대표하며, 업무를 수행하는 서비스를 갖게된다 

  - 이것은 3-5에서 이야기하는 DI와도 연관이 있다 


3-5. 의존성 주입 (DI, Dependency Injection)

  - 코드안의 하드코딩된 부분을 제거하고 의존관계에 있는 모듈을 다양한 방법으로 전달해 준다

  - AngularJS에서는 펑션의 파라미터로 넘겨준다. 이를 통해 테스트시에 서버를 직접 호출할 필요없이 필요한 목서비스(mock service)를 전달하여 테스트를 할 수도 있다 



4. 항시 TDD 하라 

  - Test Driven Development 를 수행하라

  - jQuery로 테스트 할 때는 항시 테스트 화면을 만들어 해야 제대로 된 테스트 수행이 가능해진다. 그러나 AngularJS는 그렇지 않다

  - 내부에 사용되는 기능을 모듈로 분리해 개발하고 DOM조작에 대해서 테스트하고 애플리케이션과 통합할 수가 있기 때문이다 

  - 그것은 관심의 분리(SoC) 방식으로 개발하기 때문에 TDD가 AngularJS에서 가능한 것이다 

  - 예를 보자 

// HTML : when-active라는 AngularJS Directive(지시자)

<a href="/hello" when-active>Hello</a>

// Test Script : Karma를 보자. DOM을 조작하여 화면을 이동시킨 후 true, false인지 체크한다 

it( 'should add "active" when the route changes', inject(function() {
    var elm = $compile( '<a href="/hello" when-active>Hello</a>' )( $scope );

    $location.path('/not-matching');
    expect( elm.hasClass('active') ).toBeFalsey();

    $location.path( '/hello' );
    expect( elm.hasClass('active') ).toBeTruthy();
}));


// Script : 정상적으로 라우팅이 되면 active class 첨부, 아니면 삭제 

.directive( 'whenActive', function ( $location ) {
    return {
        scope: true,
        link: function ( scope, element, attrs ) {
            scope.$on( '$routeChangeSuccess', function () {
                if ( $location.path() == element.attr( 'href' ) ) {
                    element.addClass( 'active' );
                }
                else {
                    element.removeClass( 'active' );
                }
            });
        }
    };
});



5. Directive(지시자)는 패키징된 jQuery가 아니다 

  - Directive안에서 DOM 조작을 하라. 하지만 jQuery 스타일은 배제하라 

  - Directive는 template을 가지고 있는 widget과 유사하다. 이를 통해 SoC를 보여주고 있고, 테스트를 가능하게 한다 

  - jQuery 스타일의 버튼토글 코딩을 보자 : AngularJS Directive안에 아래와 같이 jQuery 스타일의 DOM 조작을 수행하지 말라

.directive( 'myDirective', function () {
    return {
        template: '<a class="btn">Toggle me!</a>',
        link: function ( scope, element, attrs ) {
            var on = false;

            $(element).click( function () {
                if ( on ) {
                    $(element).removeClass( 'active' );
                }
                else {
                    $(element).addClass( 'active' );
                }

                on = !on;
            });
        }
    }; 

});

  - Angular Way 방식의 버튼코글 : ng-show, ng-class, ng-binding 과 같은 Directive를 통해 DOM 조작을 수행하라  

.directive( 'myDirective', function () {
    return {
        scope: true,
        template: '<a class="btn" ng-class="{active: on}" ng-click="toggle()">Toggle me!</a>',
        link: function ( scope, element, attrs ) {
            scope.on = false;
            scope.toggle = function () {
                scope.on = !$scope.on;
            };
        }
    }; });

  - Directive는 jQuery같은 펑션의 집합이 아니다. Directive는 HTML의 확장이다. 이는 HTML안에 우리가 수행하고 싶은 무언가를 넣고 싶은데 못 할 경우에 Directive를 만들어야 하는 것이다. 그래서 HTML의 일부로서 사용하는 것이다. 



Angular Way에서 jQuery 방식을 사용하지 말고 포함조차 시키지 말아라. 


- View  === AngularJS의 Directive가 포함된 HTML이다 

- Model === $scope를 통하여 Controller에서 Two way binding을 자동 수행한다 

- View와 Controller를 통하여 표현과 행위를 분리한다 (SoC)

- AngularJS 애플리케이션 접근법

   Step 1) Model을 먼저 생각하고 Service를 만든다

   Step 2) Model이 표현될 View에 대해 생각해 보고 template을 만들고, 필요하면 자동 모델 바인딩되는 Directive도 만든다.

   Step 3) 각 View 를 Controller에 연결한다 (ng-view and routing, ng-controller)



<참조>

  - 원문 : Think in AngularJS

  - AngularJS의 상속에 대한 이해 (그림포함)

posted by 윤영식
2013. 8. 20. 17:32 AngularJS/Prototyping

iCheck 플러그인은 HTML input의 type이 Checkbox나 Radio 의 모양을 UX 입장에서 직관적으로 만들어 준다. AngularJS하에서 이런 JQuery Plugin을 사용하려면 Directive(지시자) 화하여 사용해야 하는데 어떻게 만드는지 알아보자 




1. Directive 만드는 형식 

  - 사용하려는 HTML 안에서 

<div directiveName ></div>

<script type="text/javascript" src="pluginName.js"></script>


  - AngularJS Directive(지시자) 만들기

    + pluginActivationFunction은 플러그인의 호출하는 펑션이다. 

    + scope.$eval 은 플러그인의 환경값을 넣어주면 된다 

App.directive('directiveName', function() {

    return {

        restrict: 'A',

        link: function(scope, element, attrs) {

            $(element).'pluginActivationFunction'(scope.$eval(attrs.directiveName));

        }

    };

}); 



2. iCheck Directive 만들기 

  - ng-app 모듈에 Directive를 만든다 

    + Radio 박스 선택에 따른 부가화면을 show하거나 hidden 하는 처리를 한다  

    + Attribute로 ngIcheck 지시자를 만들었다. (HTML에서는 ng-icheck 로 사용된다)

    + ifChecked의 iCheck 이벤트를 받았다. iCheck의 Radio를 사용하면서 AngularJS의 Radio 이벤트를 받을 수 없게 되었기 때문이다 

    + Radio의 attribute로 value="file" or "direct" 라는 두개의 Radio 선택 옵션을 주었다. 

    + AngularJS Context안에서 $scope의 값 변경을 인식하기 위하여 $scope.$apply()를 iCheck.on listener의 핸들러 펑션에 호출함.

    + $(element).iCheck(<환경값>); 의 plugin 초기화 펑션을 호출해 주었다 

.directive('ngIcheck', function() {

   return {

       restrict: 'A',

       controller: function($scope, $element) { 

        $element.on('ifChecked', function(event){

 if(event.target.value === 'file') {

  $scope.isFile = true;

  $scope.isDirect = false;

  $scope.$apply();

 } else if(event.target.value === 'direct') {

  $scope.isDirect = true;

  $scope.isFile = false;

  $scope.$apply();

 } else {

  $scope.isFile = false;

  $scope.isDirect = false;

  $scope.$apply();

 }

});

       },

       link: function(scope, element, attrs) {

           $(element).iCheck({

        checkboxClass: 'icheckbox_square-blue', 

        radioClass: 'iradio_square-blue', 

        increaseArea: '20%' 

       });

       }

   };

});


// 중요)

// iCheck를 AngularJS의 Directive로 하지 않고 사용하려면 HTML하단에 해당 script를 넣어준다

// 이렇게 하면 Radio 버튼의 이벤트가 AngularJS Context안에서 작동하지 않기 때문에 AngularJS의 기능을 사용할 수 없다 

// 결국 이렇게 사용하지 않기 위해선 JQuery Plugin을 AngularJS Directive로 만들어 AngularJS Context에서 돌아가게 해주는 것이다

<script>

$(document).ready(function(){

  $('input').iCheck({

    checkboxClass: 'icheckbox_square-blue',

    radioClass: 'iradio_square-blue',

    increaseArea: '20%'

  });

});

</script>


  - HTML에서 사용하기 

    + <input type="radio" name="iCheck" .../> : 여기까지는 iCheck에서 사용함 

    + <input ... value="file" ng-icheck> : AngularJS에서 사용. ng-icheck Attribute를 만다면 AngularJS가 컴파일하고 link하고 이벤트 반응에 대하여 controller에서 처리한다 

    + Radio의 선택에 따라서 ng-show="isFile", ng-show="isDirect"에 따라서 해당 div가 show, hidden 되어 진다 

  <table id="maForm" class="table table-striped table-bordered">

      <thead>

        <tr>

          <th width="25%"><i class="icon-check-sign icon-small"></i> Options</th>

          <th width="75%" class="not-for-mobile"><i class="icon-edit icon-small"></i> Insert Data</th>

        </tr>

      </thead>

      <tbody>

        <tr>

          <td>

            <label>Select Input Method</label> <br>

            <table class="table table-condensed">

              <tr class="active">

                  <td><input type="radio" name="iCheck" value="file" ng-icheck /></td>

                  <td>Upload File</td>

              </tr>

              <tr class="active">

                  <td><input type="radio" name="iCheck" value="direct" ng-icheck /></td>

                  <td>Input Direct</td>

              </tr>

            </table>

          </td>

          <td>

          <!-- input direct data -->

          <div ng-show="isDirect">

  <textarea ng-model="ma.direct" class="form-control" rows="12"></textarea>

  </div>

  <!-- upload file such as .csv -->

  <div ng-show="isFile">

                  <input id="fileupload" ng-model="ma.file" type="file" name="files[]" multiple>

  </div>

          </td>

        </tr>

        <tr>

      <td colspan="2">

  <i class="icon-bar-chart icon-small"></i> Moving Average Chart<br>

  <img src="http://www.placehold.it/880x300/EFEFEF/AAAAAA&amp;text=no+image">

      </td>

        </tr>

      </tbody>

    </table>


  - 결과 : 선택하는 옵션 박스가 iCheck 플러그인을 통하여 좀 더 직관적으로 나오게 되었다

    + 단순한 Radio 옵션

    

    + 변경된 iCheck Radio 옵션

    



<참조> 

  - Correct way to integrate JQuery Plugins in AngularJS 

  - How to convert a jQuery plugin to AngularJS Directive : 유튜브 동영상

  - Directive의 $watch 와 $apply 완벽 이해 : $apply, $digest, $watch 에 대한 상세 설명

  - Thinking in AngularJS : Angular Way 

  - SlideJS Plugins을 Directive로 만드는 유사한 예제

'AngularJS > Prototyping' 카테고리의 다른 글

[AngularJS] Twitter Search 만들기  (0) 2013.04.05
[AngularJS] Todo 목록 만들기  (0) 2013.04.05
[AngularJS] Hello World 만들기  (0) 2013.04.04
posted by 윤영식
2013. 4. 27. 16:36 AngularJS/Concept

AngularJS에 구성 요소에 대한 Snippet을 살펴본다. 또한 jQuery와 통합하여 사용하는 방법을 알아보자 



1. 들어가기

  - Angular를 통해서 Client-Side에서 데이터와 애플리케이션 로직을 분리한다

  - 최초에 넣어야 할 라이브러리들 

<script type="text/javascript" src="/path/to/angular/angular.js"></script>

<script type="text/javascript" src="/path/to/angular/angular-resource.js"></script>



2. Module

  - 정의

    + Modules are used to fully encapsulate all the angular code of your application into one entry point

    + 컴포넌트와 같으면 Self living 할 수 있다

    + 큰 프로젝트를 할 때의 모듈 나누는 방법

  - 메인 레이아웃 파일에 다음과 코드를 넣는다

    + <html 태그에 넣거나 <body 에 넣거나 한다 

    + SPA를 한다면 아마도 <body> 태그 밑의 특정 부분이 Dynamic DOM 이 될 것이다 

<html data-ng-app="YOUR_APP_NAME">

또는

<html ng-app="YOUR_APP_NAME">


  - JavaScript에서 사용하기 

    + angular는 글로벌 변수이고, directive/controller/filter/service등을 만들기 위한 App 변수를 만들었다

    + ['ngResource']와 같이 의존하는 라이브러리를 설정한다  

var App = angular.module('YOUR_APP_NAME', ['ngResource']);



3. Binding, Expression

  - 양방향 data binding, 조건문, 반복문

  - {{ expression }} 데이터 표현

// html

<div class="records" data-ng-repeat="record in records | orderBy:orderProp">

  <h5> {{ record.title }} </h5>

</div>


// js 

$scope.records = [{ title : 'one' }, { title : 'two' }, { title : 'three' }]; 



4. Dependency Injection

  - 코드를 조직화 하고 테스트 가능하게 만들어준다

  - controllers, module configuraitons, directives, filters, resources, routes 들을 DI 할 수 있다 

// 메소드 파라미터로 DI를 해준다

var Ctrl = function($scope, $http, $location) {

  //now you can use any of the injected variables


  //to change the URL after something has happened then you can use $location

  $location.path('/path/to/new/page');

}

//and now the injection of the variables

Ctrl.$inject = ['$scope','$http','$location'];


  - DI에 대한 개념을 제대로 잡고 싶다면 보지타님의 동영상 보라



5. Routes

  - 요청 uri path와 controller를 맵핑하는 것이다 

// 모듈의 config 에서 정의한다 

// $routeProvider 서비스를 통하여 정의한다 

// uri path에 대하여 templateUrl 과 controller를 맵핑한다 

App.config(['$routeProvider', function($routes) {


  $route.when('/',{

    templateUrl : '/templates/home.html',

    controller : HomeCtrl

  });


  $route.when('/register',{

    templateUrl : '/templates/register.html',

    controller : RegisterCtrl

  });


  $routes.otherwise({

    redirectTo : '/'

  });


}]);



6. Controller & Scope

  - 컨트롤러는 $scope와 함께하여 데이터를 연동한다 

  - controller 만들기 (ng-app의 명칭 즉, 모듈명이 없을 경우)

    + $scope.title를 변경하면 angular는 데이터 변경을 모른다 

    + 하기와 같은 예제에서는 $scope.$apply() 가 필요하다 

    + $apply() 는 일반 JavaScript Context에서 수행되는 것을 AngularJS Context로 수행 되게 해준다 (필히 참조)

    + $apply, $digest, $$phase 란 무엇인가?

// angularjs 모듈에서 파생되지 않은 컨트롤러 펑션

var SomeCtrl = function($scope, $http, $location) {

  $scope.value = 'some value';

};

// controller에게 서비스를 주입하여 준다 (DI)

SomeCtrl.$inject = ['$scope','$http','$location'];


// html에서 {{ }} 를 설정

<div class="header">{{ title }}</div>


// 하기와 같이 설정을 하면 {{ title }} 이 자동으로 바뀌어야 한다  

$scope.title = 'this is awesome';


//if a scope digestion is already going on then it will get picked up and you won't

//have to call the $scope.$apply() method

// $apply를 호출하면 $digest cycle로 들어가고 model이 변화를 감지하여 $watch 에 등록된 expression의 변경을 수행한다

// 즉, view가 re-rendering 되는 것이다 

if(!$scope.$$phase) { //this is used to prevent an overlap of scope digestion

  $scope.$apply(); //this will kickstart angular to recognize the change

}


  - $scope는 $rootScope 상속한 것이다

    + 만일 전체 애플리케이션에서 $scope를 통하여 어떤 펑션을 사용하고 싶다면 하기와 같이 한다

    + 모듈이 로딩 될 때 config 와 run 이 자동 호출된다. run은 main메소드와 유사하다 (참조

App.run(['$rootScope', function($rootScope) {

  $rootScope.sharedFunction = function() { ... };

}]);


  - controller 사용하기 정식 방법 두가지 

    + html 태그안에서 ng-controller 통하여 설정

    + $routes의 controller 설정

// html

<div data-ng-controller="SomeCtrl">...</div>


// js

$routes.when('/some/path',{

  controller : Ctrl,

  templateUrl : '/templates/controller.html'

});



7. Services

  - 전체 애플리케이션에서 공유하는 것들

  - DI를 통하여 객체를 전달함 

//define the service

// App 모듈에 myService가 정의한 서비스의 명칭

App.factory('myService', ['myOtherService', '$location', function(myOtherService, $location) {

  return function(input) {

    //do something with the input using the myOtherService or the $location objects.

    return input;

  };

}]);


//use the service within the controller

// Controller에서 $scope와 myService DI하여 공유한다 

var HomeCtrl = function($scope, myService) {

  var input = '123';

  input = myService(input);

};

HomeCtrl.$inject = ['$scope','myService'];


//use the service a directive

// App 모듈에 directive를 정의한다. myService 서비스를 DI하여 공유한다 

App.directive('myDirective', ['myService',function(myService) {

  return {

    link: function($scope, element, attrs) {

      var input = '123';

      input = myService(input);

    }

  }

}]);



8. Models

  - RESTful Web Service를 호출을 위하여 7번의 서비스 개념으로 만들어 놓고 사용할 수 있다 

// ModelName이라고 서비스를 만든다

// $resource를 통하여 서버사이드로 호출한다 

App.factory('ModelName', ['$resource', function($resource) {

  $resource.url('/path/to/model/controller/:id',{

    id : '@id', //this binds the ID of the model to the URL param

  },{

    query : { method : 'GET', isArray : true }, //this can also be called index or all

    save : { method : 'PUT' }, //this is the update method

    create : { method : 'POST' },

    destroy : { method : 'DELETE' }

  }

}]);


// Controller 에서 ModelName 서비스를 DI 받아서 호출한다

var SomeCtrl = function($scope, $http, $location, ModelName) {

  //now you can use ModelName to do your business

};

SomeCtrl.$inject = ['$scope','$http','$location','ModelName'];


  - ModelName을 통하여 다음과 같이 사용할 수 있다 (연구필요)

    + 서비스를 CRUD query 형태로 만들어서 사용한다

    + Breeze.js 로 확장할 수도 있겠다 

//list all the records on the page

var results = ModelName.query({ search : 'all' }, onSuccessFn, onFailureFn);


//get a specific record

var record = ModelName.get({ id : 123 }, onSuccessFn, onFailureFn); //onSuccessFn and onFailureFn are optional callback functions where you can further customize the response


//create a new ModelName record

var record = new ModelName();


//update that record

record.someAttr = 'someValue';

record.$save();


//or if you prefer to submit your data in a different way

ModelName.save({

    id : record.id

  },{

  somePostObject : {

    attr1 : 'value',

    attr2 : 'value2'

  }

});


//destroy the record (and include a token)

record.destroy({ token : record.token });



9. Directives

  - HTML 코드를 통하여 만들어 놓은 플러그인과 링크를 설정해 주는 것이다. 애플리케이션내에서 블럭을 격리시켜준다. (그냥 컴포넌트 개념을 생각하며 되겠다. 화면 구성을 위하여 UI 컴포넌트 추가하는 것 처럼)

  - 웹앱을 구조화하는데 도움을 준다 

  - Plugin, Validation(유효성검사), Dynamic text properties 또는 i18n, l10n 에도 활용한다 

// link 는 Controller와 Directive가 $scope 를 통하여 데이터를 공유하는 중요 통로이다 

angular.directive('myDirective',function($compile) {

  return {

    templateUrl : '/path/to/some/template.html', //(optional) the contents of this template can be downloaded and constructed into the element

    replace : true, //whether or not to replace the inner data within the element

    link : function($scope, $element, attributes) { //this is where your magic happens

      $scope.title = '...';

    }

  };

});



10. Filters

  - 필터는 재사용 가능한 오퍼레이션이다 

  - binding operation에 내장되어 함께 수행된다 예) 정렬, 문자변형, Pagination 등

// App 모듈에서 myUppercase 명칭의 filter 정의  

App.filter('myUppercase', function(data) {

  for(var i=0; i < data.length; i++) {

    data[i].title = data[i].title.toUpperCase();

  }

  return data;

});


// html에서 사용하기 

<div data-ng-repeat="for record in records | filter:myUppercase">...</div>


// 또는 javascript 코드에서 호출가능

//be sure to inject the $filter object

var values = ['one','two','three'];

values = $filter('myUppercase')(values);



11. HTML5 Mode

  - angular app이 angular의 routing system 안에서 HTML5 history방식으로 관리되게 해준다 

  - 해당 코드만 넣으면 된다 

App.config(['$locationProvider', function($location) {

  $location.html5Mode(true); //now there won't be a hashbang within URLs for browers that support HTML5 history

}]);



12. jQuery 사용

  - 가장 간단하게 angular.js 스크립트 태그 전에 jQuery 태그를 먼저 위치시키면 된다

  - Angular.js는 jqLite를 기본 사용하나 jQuery가 먼저 설정되면 jQuery로 대체되어 사용한다 



<참조>

  - 원문 : use angularjs to power your web application

  - AngularJS 소개 ppt


posted by 윤영식
2013. 4. 11. 17:20 AngularJS/Concept

앵귤러의 모듈에 대해서 알아보자 



1) 개요

  - 모든 애플리케이션에는 main이 있지만 Angular에는 main 이 없다. 애플리케이션이 시작할 때 정의한 모듈을 읽는다

  - 모듈로 쪼개면 애플리케이션 이해가 쉽고 unit-testing이 수월하며 end-to-end testing도 가능하다

  - 필요한 모듈은 언제든지 로딩해서 사용할 수 있다

  - 3rd-party 라이브러리를 모듈로 만들어 사용할 수 있다 

  - Module API

  - 모듈 종류

    + Service Module

    + Directive Module

    + Filter Module 

    + module 들에 의존하는 Application Module : <ng-app="ModuleName"> 으로 시작


  - 모듈을 나누는 가장 큰 이유는 테스팅! 테스팅!

  - 아름다운 코드를 보자 

index.html

  1. <!doctype html>
  2. <html ng-app="xmpl">
  3. <head>
  4. <script src="http://code.angularjs.org/1.0.5/angular.min.js"></script>
  5. <script src="script.js"></script>
  6. </head>
  7. <body>
  8. <div ng-controller="XmplController">
  9. {{ greeting }}!
  10. </div>
  11. </body>
  12. </html>


script.js : service, directive, filter module => use xmpl application module

  1. angular.module('xmpl.service', []).
  2. value('greeter', {
  3. salutation: 'Hello',
  4. localize: function(localization) {
  5. this.salutation = localization.salutation;
  6. },
  7. greet: function(name) {
  8. return this.salutation + ' ' + name + '!';
  9. }
  10. }).
  11. value('user', {
  12. load: function(name) {
  13. this.name = name;
  14. }
  15. });
  16.  
  17. angular.module('xmpl.directive', []);
  18.  
  19. angular.module('xmpl.filter', []);
  20.  
  21. <!-- ng-app="xmpl" 모듈이 다른 부가적인 모듈에 의존관계를 가지고 있다 -->
  22. angular.module('xmpl', ['xmpl.service', 'xmpl.directive', 'xmpl.filter']).
  23. run(function(greeter, user) {
  24. // This is effectively part of the main method initialization code
  25. greeter.localize({
  26. salutation: 'Bonjour'
  27. });
  28. user.load('World');
  29. })
  30.  
  31. // A Controller for your app
  32. var XmplController = function($scope, greeter, user) {
  33. $scope.greeting = greeter.greet(user.name);
  34. }

  


2) 모듈 로딩과 의존관계

  - 모듈은 초기화를 시작하여 애플리케이션에 적용되는 동안 Configuration & Run Block의 집합체이다 

  - Configuration Block

    + providers, constants 만 inject 된다

    + 의존관계있는 모듈을 inject 받는다 

  1. angular.module('myModule', []).
  2. value('a', 123).
  3. factory('a', function() { return 123; }).
  4. directive('directiveName', ...).
  5. filter('filterName', ...);
  6.  
  7. // is same as
  8. // config 펑션에 provider를 통하여 설정한다  
  9. angular.module('myModule', []).
  10. config(function($provide, $compileProvider, $filterProvider) {
  11. $provide.value('a', 123);
  12. $provide.factory('a', function() { return 123; });
  13. $compileProvider.directive('directiveName', ...);
  14. $filterProvider.register('filterName', ...);
  15. });


  - Run Block 

    + instances, constants 만 inject 된다

    + 모듈의 main 메소드 격으로 보면된다  


* Unit Test 다음에 추가



<참조>

  - 원문 : Developer Guide - Module

posted by 윤영식
2013. 4. 11. 11:50 AngularJS/Concept

Angular는 지시자(Directive)를 통하여 DOM을 확장한다. 지시자에 대해 자세히 알아보자



특징

  - Camel cased name 을 갖는다. 예) ngBind

  - 변환될 때는 Snake case - 으로 변경된다 예) ng-bind

  - 지시자는 element name, attribute, class name, comment 에 올 수 있다

  1. <span my-dir="exp"></span>
  2. <span class="my-dir: exp;"></span>
  3. <my-dir></my-dir>
  4. <!-- directive:my-dir exp -->


  - compiler는 $interpolate 서비스를 이용해서 text와 attribute를 매칭시킨다. 표현이 watch에 등록되어 있고, digest 싸이클의 일부로서 업데이트 되는 것이다 

<a href="img/{{username}}.jpg">Hello {{username}}!</a>


  - HTML에서 이루어지는 3 스텝

    + Step 1: 최초에 Angular의 template은 HTML이므로 표준 브라우져 API를 사용하여 DOM 으로 파싱된다. 다른 템플릿 시스템과 차별점이기도 하다 (참조)

    + Step 2: $compile 메소드를 통해서 DOM을 해석하면서 지시자(directive)를 찾는다. 지시자는 $compile() 펑션이 수행되면서 DOM 구조를 변경하고 응답을 위하여 link() 펑션으로 return 한다

    + Step 3: 각 지시자별로 link() 를 호출하여 지시자별 listener등록과 scope에 대한 watch를 설정한다. 이에따라 scope와 DOM 사이에 Live Binding이 되는 것이다

  1. var $compile = ...; // injected into your code
  2. var scope = ...;
  3.  
  4. var html = '<div ng-bind="exp"></div>';
  5.  
  6. // Step 1: parse HTML into DOM element
  7. var template = angular.element(html);
  8.  
  9. // Step 2: compile the template
  10. var linkFn = $compile(template);
  11.  
  12. // Step 3: link the compiled template with the scope.
  13. linkFn(scope);


  - 왜 compile과 link를 나눈거지? 

    + model이 변경되면 DOM이 변경되어야 한다. 예) ng-repeat의 경우 model 목록이 늘면 DOM도 변경됨

    + {{user}}는 interpolation(값을 채우는) 지시자 이지만 ng-repeat은 ngRepeat 지시자이다. 여기서의 딜레마는 모델 데이터가 늘어나면 li 앨리먼트를 복제하여 compile과정을 또 거쳐야 하는데 그렇게 하면 성능이 느려진다. 이에 대한 해결책은 과정을 두개로 나누는 것이다. 그래서 ngRepeat 지시자를 해석하고 linking function 을 얻는다. ngRepeat 은 표현이 변경되면 li 엘리먼트를 복제하여 배열에 추가하고 새로운 scope 생성하여 복제한 li 엘러먼트에 할당하고 복제한 li 의 linking function 을 호출한다

  1. Hello {{user}}, you have these actions:
  2. <ul>
  3. <li ng-repeat="action in user.actions">
  4. {{action.description}}
  5. </li>
  6. </ul>


  - 정리

    + compile function : 지시자에 따라 template DOM 엘리먼트를 변환하여 특별한 DOM 엘리먼트를 만들어 준다 

    + link function : 지시자가 복제된 DOM 엘리먼트에 listener 등록하도록 해준다 



지시자 만들기

  - 전체 내역 

  1. var myModule = angular.module(...);
  2.  
  3. myModule.directive('directiveName', function factory(injectables) {
  4. var directiveDefinitionObject = {
  5. priority: 0,
  6. template: '<div></div>',
  7. templateUrl: 'directive.html',
  8. replace: false,
  9. transclude: false,
  10. restrict: 'A',
  11. scope: false,
  12. compile: function compile(tElement, tAttrs, transclude) {
  13. return {
  14. pre: function preLink(scope, iElement, iAttrs, controller) { ... },
  15. post: function postLink(scope, iElement, iAttrs, controller) { ... }
  16. }
  17. },
  18. link: function postLink(scope, iElement, iAttrs) { ... }
  19. };
  20. return directiveDefinitionObject;
  21. });


  - 기본 내역 : 없은 것은 기본값을 사용한다 

  1. var myModule = angular.module(...);
  2.  
  3. myModule.directive('directiveName', function factory(injectables) {
  4. var directiveDefinitionObject = {
  5. compile: function compile(tElement, tAttrs) {
  6. return function postLink(scope, iElement, iAttrs) { ... }
  7. }
  8. };
  9. return directiveDefinitionObject;
  10. });


  - 간단 내역 : 지시자가 템플릿 변환이 없고 자신의 인스턴스에 대한 것만 관련 있을 때 사용함 

  1. var myModule = angular.module(...);
  2.  
  3. myModule.directive('directiveName', function factory(injectables) {
  4. return function postLink(scope, iElement, iAttrs) { ... }
  5. });


  - Factory method : 지시자를 생성하는 역할이고, compiler가 지시자를 찾을 때 최초 호출하여 초기화 한다. $injector.invoke 를 사용한다 


  - 지시자 정의하기 

    + 지시자 정의는 compiler 에게 이렇게 이렇게 해달라는 설명을 제공하는 과정이다 

    + name : 현재 scope의 명칭 (optional)

    + priority : compile 펑션의 호출 우선 순위를 지정한다

    + terminal : true ?

    + scope : true 이면 새로운 scope 생성, {} 새로운 격리된 scope 생성으로 부모 scope 상속이 없다. 새로운 컴포넌트 만들 때 유용한다

      {} 로 만들때 안의 내용들 

        > @ : local scope property

        > = : bi-directional binding between local property and the parent scope property 

        > & : parent scope 에서 execute expression  방법을 제공 

///////////////

// @ 사용하기

/*
local scope property html
<div ng-app="drinkApp">
<div ng-controller="AppCtrl">
<div drink flavor="strawberry"></div>
</div>
</div>*/
 
var app = angular.module("drinkApp", []);
 
app.controller("AppCtrl", function($scope) {
$scope.ctrlFlavor = "blackberry";
})
 
app.directive("drink", function() {
return {
scope: {
flavor: "@"
},
template: '<div>{{flavor}}</div>'
/*
scope: {flavor: "@"} 표현은 link: function() {...} 과 완전히 동일하다
link: function(scope, element, attrs) {
scope.flavor = attrs.flavor;
}*/
}
})


///////////////

// = 사용하기 (소스)

/*
flavor="{{ctrlFlavor}}" 를 사용하지 않음에 주의한다
<div ng-app="drinkApp">
<div ng-controller="AppCtrl">
Ctrl
<input type="text" ng-model="ctrlFlavor">
Dir
<div drink flavor="ctrlFlavor"></div>
</div>
</div>
*/
 
var app = angular.module("drinkApp", []);
 
app.controller("AppCtrl", function($scope) {
// parent property
$scope.ctrlFlavor = "blackberry";
})
 
app.directive("drink", function() {
return {
scope: {
flavor: "="
},
// local property
template: '<input type="text" ng-model="flavor"> '
}
})


///////////////

// & 사용하기 (소스)

/*
message를 controller parent scope로 전달 이는 scope가 isolated되어 있어도 상위로 값을 전달 할 수 있게 한다
<div ng-app="phoneApp">
<div ng-controller="AppCtrl">
<div phone dial="callHome(message)"></div>
<div phone dial="callHome(message)"></div>
</div>
</div>
*/
 
var app = angular.module("phoneApp", []);
 
app.controller("AppCtrl", function($scope) {
$scope.callHome = function(message) {
alert(message);
}
})
 
app.directive("phone", function() {
return {
scope: {
dial: "&"
},
// {message:value} 객체를 통하여 contrller scope의 callHome에 전달
template: '<input type="text" ng-model="value">' +
'<div class="button" ng-click="dial({message:value})">Call Home</div> '
}
})


/////////////////////////

// @, =, & 종합 사용하기 (소스)

/*
@, &, = integration scope
<div ng-app="phoneApp">
<div ng-controller="AppCtrl">
<phone number="1111111" network="network" make-call="leaveVoicemail(number, message)"></phone>
<phone number="2222222" network="network" make-call="leaveVoicemail(number, message)"></phone>
<phone number="3333333" network="network" make-call="leaveVoicemail(number, message)"></phone>
 
</div>
</div>
*/
 
var app = angular.module("phoneApp", []);
 
app.controller("AppCtrl", function($scope) {
$scope.leaveVoicemail = function(number, message) {
alert("Number: " + number + " said: " + message);
}
})
 
app.directive("phone", function() {
return {
restrict: "E",
scope: {
number: "@", // local scope property
network: "=", // bi-directional binding between local property and parent property
makeCall: "&" // send expressions to parent scope
},
template: '<div class="panel">Number: {{number}} Network:<select ng-model="network" 
ng-options="network for network in networks">' +
'</select></div><input type="text" ng-model="value">' +
'<div class="button" ng-click="makeCall({number: number, message:value})">
Call Home</div> ',
link: function(scope) {
scope.networks = ["KT", "SKT", "LGU+"];
scope.network = scope.networks[0];
}
}
})

  

    + controller : pre-linking 전에 호출되어서 지시자들끼리 공유를 할려고 할때 사용

    + require : 다른 controller에 현재 지시자의 linking function을 전달 할때 사용용

    + restrict : 지시자 선언 종류 

  • E - Element name: <my-directive></my-directive>
  • A - Attribute: <div my-directive="exp"> </div>
  • C - Class: <div class="my-directive: exp;"></div>
  • M - Comment: <!-- directive: my-directive exp -->


    + template : HTML file 

    + templateUrl : 로딩할 URL 지정

    + replace : true 이면 현재 엘리먼트를 append가 아닌 replace 한다 

    + transclude : ngTransclude 엘러먼트로 컴파일 및 지시자로 쓰임 

    + compile : compile function 정의

    + link : link function 정의 


  - Compile function

    + template DOM을 변환하는 역할이다 

    + 사용자 정의할 때만 사용하면 되고 기존 지시자에는 사용할 일이 없겠다

function compile(tElement, tAttrs, transclude) { ... }

    + tElement : template element 

    + tAttrs : template attribute

    + transclude : transclude linking function: function(scope, cloneLinkingFn)

    + return 값을 두가지로 줄 수 있다

     > function 형태로 리턴 : linking function 으로 등록하는 것과 동일함 

     > object 형태로 리턴 : pre-linking, post-linking을 제어하고 싶을 때 사용한다 


  - Linking function

    + DOM 업데이트를 위하여 DOM listener를 등록하는 역할이다 

    + template 이 복제된 후에 수행되고 지시자의 대부분 로직이 놓인다 

function link(scope, iElement, iAttrs, controller) { ... }

    + scope : watch 등록할 때 지시자에 의해서 사용 됨 

    + iElement : instance element. 

    + iAttrs : instance attribute

    + controller : controller instance

    + pre-linking function : child element가 link 되기전에 호출

       post-linking function : child element link 된 후에 호출 


  - Attribute Object 

    + compile() 과 link() 에 파라미터로로 전달되는 객체

    + 값의 변화를 $observe 할 수도 있고 $set하거나 .XXX 로 읽을 수도 있다

  1. function linkingFn(scope, elm, attrs, ctrl) {
  2. // get the attribute value
  3. console.log(attrs.ngModel);
  4.  
  5. // change the attribute
  6. attrs.$set('ngModel', 'new value');
  7.  
  8. // observe changes to interpolated attribute
  9. attrs.$observe('ngModel', function(value) {
  10. console.log('ngModel has changed value to ' + value);
  11. });
  12. }



Transclusion 과 Scope 이해하기 

  - 재사용 컴포넌트를 만들고자 하는 다음의 경우를 생각해 보자 

    + show 버튼 클릭하면 다이얼로그 박스가 열린다

    + 다이얼로그 박스는 제목이 있고, 

  1. <div>
  2. <button ng-click="show=true">show</button>
  3. <dialog title="Hello {{username}}."
  4. visible="show"
  5. on-cancel="show = false"
  6. on-ok="show = false; doSomething()">
  7. Body goes here: {{username}} is {{title}}.
  8. </dialog>
  9. </div>

     + dialog 위젯이 변환되면 아마 하기와 같이 나올것이다 

      > username이 들어간 title을 넣어 줘야 한다 

      > onOk, onCancel 버튼이 동작해야 한다 

      > scope 에서 local 변수에 대한 맵핑작업이 필요하다 

  1. <div ng-show="visible">
  2. <h3>{{title}}</h3>
  3. <div class="body" ng-transclude></div>
  4. <div class="footer">
  5. <button ng-click="onOk()">Save changes</button>
  6. <button ng-click="onCancel()">Close</button>
  7. </div>
  8. </div> 


    + scope 정의 

     > transclude DOM 은 다이얼로그 위젯의 격리된 scope의 child 가 되어서 어느 것과도 binding되지 않고 영향을 미지치치 않고 title에 값을 맵핑할 수 있다. 즉, transclude scope는 original scope의 자식이 되기 때문이다 (아직 이해 실패^^;) 

  1. transclude: true,
  2. scope: {
  3. title: '@', // the title uses the data-binding from the parent scope
  4. onOk: '&', // create a delegate onOk function
  5. onCancel: '&', // create a delegate onCancel function
  6. visible: '=' // set up visible to accept data-binding
  7. },
  8. restrict: 'E',
  9. replace: true


  - Transclude 옵션을 설정하는 간단한 예제 (소스)

/*
Simple transclude sample dom
<div ng-app="phoneApp">
<div ng-controller="AppCtrl">
<panel>
<div class="button">Click me!</div>
</panel>
</div>
</div>
*/
 
var app = angular.module("phoneApp", []);
 
app.controller("AppCtrl", function($scope) {
 
})
 
// transclude: true와 <div ng-transclude를 설정하지 않으면 DOM의 <div class="button"이 안보이고 
// template의 내용만 보이게 된다. template 내용안에 DOM 의 button class가 같이 보이게 하기 위한 방법

> 버튼이 안보임


> transclude 설정으로 버튼이 보임


app.directive("panel", function() {
return {
restrict: "E",
transclude: true,
template: '<div class="panel" ng-transclude>This is a panel component</div>'
}
})


전반적으로 처음 접하는 개념이어서 정리가 서툴다. 다시 반복하여 리팩토링 예정임... ^^



미스코가 직접 이야기하는 Directive에 대해서 들어보자 

  - 유튜브 

    + Directive의 compile & link 개념의 C의 컴파일과 링크에서 영감을 받았다 

    + compile은 scope를 핸들링하지 않는다. template자체를 변경할 수 있다. 즉 compile은 template에 대한 해석만을 담당한다.

    + link는 element instance에 대해서 조작을 한다. 예로 element.addClass()와 같은 호출. 

    


  - 디렉티브 만들기 : 간단 총정리하기 

    


<참조>

  - 원문 : Developer Guide - Directives

  - AngularJS Directive로 컴포넌트 만들기 (필독)

  - 동영상강좌 : http://egghead.io

    + isolated @, =, & 소스 : https://gist.github.com/ysyun/5385927

    + 테스트를 위한 index.html 파일 내역 : CSS로 foundation을 사용한다 

 - Directives 이해 높이기-소스포함 (필독)

 - Directives Link, Apply, Digest 동작 흐름

posted by 윤영식
2013. 4. 10. 19:48 AngularJS/Concept

앵귤러군의 컴포넌트는 무엇이 있고 어떻게 상호작용하는지 알아보자 



Startup 

  - Angular는 어떻게 Hello World를 출력하는가?

  - 브라우져는 HTML를 로드하고 DOM객체로 파싱한다

  - DOMContentLoaded 이벤트가 발생하면 Angular 는 애플리케이션 경계를 결정해주는 ng-app 지시자를 찾는다

  - ng-app으로 명시된 모듈은 $injector를 환경설정한다 

  - $injector는 $compile & $rootScope를 생성한다 

  - $compile 서비스를 통해 DOM을 컴파일하고 $rootScope와 Link를 맺는다 

  - ng-init 을 통해서 scope 내의 name property에 World를 할당한다 

  1. <!doctype html>
  2. <html ng-app>
  3. <head>
  4. <script src="http://code.angularjs.org/1.0.5/angular.min.js"></script>
  5. </head>
  6. <body>
  7. <p ng-init=" name='World' ">Hello {{name}}!</p>
  8. </body>
  9. </html>


  - 흐름도



Runtime

  - Angular와 브라우져간의 이벤트 루프를 돌며 상호 작용을 어떻게 하는가?

    + 브라우져 이벤트 루프는 이벤트 - 사용자 행위, 타이머, 서버응답에 대한 네트워크 이벤트등 - 발생을 기다린다

    + 이벤트의 콜백이 수행되고, JavaScript Context로 들어간다음 DOM 구조를 조작한다

    + JavaScript Context를 나오면 DOM 기반의 다시 그려진 화면으로 바뀐다 


  - 전통적인 JavaScript execution context 와 Angular execution context 동작 방식에 차이가 있다 

    + Angular execution context가 하는 일 : data-binding, exception 처리, 프로퍼티 변경 감시 등등

    + $apply를 통하여 JavaScript execution context에서 Angular execution context로 들어갈 수 있다

    + $apply의 직접호출은 사용자정의 이벤트 콜백과 3th-party 라이브러리 콜백일때만 사용하고 directive에서 자동 호출된다

    + 이에 대한 자세한 이해는 다음의 블로그를 꼭 읽어보길 바란다 : AngularJS and scope.$apply Posting


  - 다이어그램 이해 

    + scope.$apply(stimulusFn) 호출로 Angular Execution Context로 들어가고, stimulusFN은 이안에서 수행할 일이다 

    + Angular가 stimulusFn 을 수행하고 애플리케이션의 상태를 변경한다 

    + Angular는 다음으로 $digest 루프로 들어가고, 이는 $evalAsync 큐와 $watch 목록을 처리하는 작은 루프이다.   

    + $evalAsync는 현재 스택 프레임 외부에서 발생하는 스케쥴 잡에 사용된다 

    + $watch 펑션은 변경을 감지하고 DOM에 새로운 값으로 업데이트 하는 역할을 수행한다 

    + Angular 의 $digest 루프가 끝나면 Angular와 JavaScript context를 빠져나온다.  

   


  - 소스를 보고 다시 보자

    + Compilation 구간

      > ng-model과 input directive는 keydown시에 listener를 <input> 태그에 설정토록 한다

      > {{name}}은 $watch가 name 변경을 알 수 있게 설정토록 한다

    + Runtime 구간

      > H를 입력하면 브라우져가 keydown 이벤트를 발생시킨다 

      > input directive는 Angular Execution Context에서 모델값 업데이트를 위하여 값의 변경을 잡아서 $apply("name = 'H';") 를 호출한다 

      > Angular는 모델에 name = H 를 적용한다 

      > $digest 루프가 시작되고 $watch 는 name property 변경을 감지한 후 DOM을 업데이트 하도록 알려준다. 

      > 그런후 Angular Execution Context를 빠져나오고 keydown 이벤트와 JavaScript Execution Contex를 빠져나온다 

  1. <!doctype html>
  2. <html ng-app>
  3. <head>
  4. <script src="http://code.angularjs.org/1.0.5/angular.min.js"></script>
  5. </head>
  6. <body>
  7. <input ng-model="name">
  8. <p>Hello {{name}}!</p>
  9. </body>
  10. </html>



Scope

  - scope는 모델 변경을 감지하고 표현하기 위해 Execution context 를 제공하는 책임을 갖는다 

  - scopes는 DOM 구조와 가깝게 하이어라키 구조를 갖는다 

  - 소스보기 

  1. <!doctype html>
  2. <html ng-app>
  3. <head>
  4. <script src="http://code.angularjs.org/1.0.5/angular.min.js"></script>
  5. <script src="script.js"></script>
  6. </head>
  7. <body>
  8. <div ng-controller="GreetCtrl">
  9. Hello {{name}}!
  10. </div>
  11. <div ng-controller="ListCtrl">
  12. <ol>
  13. <li ng-repeat="name in names">{{name}}</li>
  14. </ol>
  15. </div>
  16. </body>
  17. </html>


 해당 소스에 대해서 이런 구조이다 



Controller 

  - controller는 뷰뒤에 있는 반드시 수행하는 코드이다

  - controller 역할은 모델을 생성하고 콜백 메소드를 가지고 view로 퍼블리싱을 담당한다 

  - view는 템플릿으로 Scope의 투영체이고, Scope는 Model과 View의 연결하며 controller로 이벤트를 보낸다 

  - view 와 controller 분리 이유 

    + controller 는 자바스크립트이고 업무적 행위를 정의한다. 또한 DOM rendering 정보가 일체 없다 

    + view template은 HTML이고 형태를 선언하고 행위는 없다 

  - Controller가 view를 인지하지 않으므로 많은 view 에 대한 처리를 담당할 수도 있다

  



Model 

  - 모델은 화면 템플릿에 합쳐지는 데이터를 가지고 있는 일반 자바스크립트 객체이다

  - Scope가 모델을 reference (참조) 하고 타입 제약을 갖지 않는다 

 



View 

  - 일반적인 것은 템플릿 스트링과 모델을 합쳐서 HTML을 만들고 DOM으로 해석되어 브라우져에 표현된다 

  - Angular는 템플릿이 HTML이어서 바로 DOM으로 해석되고 DOM안에 directive가 템플릿 엔진인 $compile 를 통해 $watch를 설정하고 모델의 변경을 계속 감시하게 된다. 따라서 모델값과 템플릿의 re-merging할 필요가 없게 된다. 

  - Angular Model은 화면에 대하여 single source-of-truth 이다 

 

  - 소스 : 동시에 바뀐다 

  1. <!doctype html>
  2. <html ng-app>
  3. <head>
  4. <script src="http://code.angularjs.org/1.0.5/angular.min.js"></script>
  5. </head>
  6. <body>
  7. <div ng-init="list = ['Chrome', 'Safari', 'Firefox', 'IE'] ">
  8. <input ng-model="list" ng-list> <br>
  9. <input ng-model="list" ng-list> <br>
  10. <pre>list={{list}}</pre> <br>
  11. <ol>
  12. <li ng-repeat="item in list">
  13. {{item}}
  14. </li>
  15. </ol>
  16. </div>
  17. </body>
  18. </html>



Directives

  - 지시자는 HTML 을 확장하여 주고 행위를 일으키는 주체이다 

    + custom attributes

    + element name

    + class name 

// html

  1. <!doctype html>
  2. <html ng-app="directive">
  3. <head>
  4. <script src="http://code.angularjs.org/1.0.5/angular.min.js"></script>
  5. <script src="script.js"></script>
  6. </head>
  7. <body>
  8. <div contentEditable="true" ng-model="content">Edit Me</div>
  9. <pre>model = {{content}}</pre>
  10. </body>
  11. </html>


// css

  1. div[contentEditable] {
  2. cursor: pointer;
  3. background-color: #D0D0D0;
  4. margin-bottom: 1em;
  5. padding: 1em;
  6. }


// js

  1. angular.module('directive', []).directive('contenteditable', function() {
  2. return {
  3. require: 'ngModel',
  4. link: function(scope, elm, attrs, ctrl) {
  5. // view -> model
  6. elm.bind('blur', function() {
  7. scope.$apply(function() {
  8. ctrl.$setViewValue(elm.html());
  9. });
  10. });
  11.  
  12. // model -> view
  13. ctrl.$render = function(value) {
  14. elm.html(value);
  15. };
  16.  
  17. // load init value from DOM
  18. ctrl.$setViewValue(elm.html());
  19. }
  20. };
  21. });



Filters

  - 데이터의 변환를 담당한다 

  - | 파이프를 사용한다 

  1. <!doctype html>
  2. <html ng-app>
  3. <head>
  4. <script src="http://code.angularjs.org/1.0.5/angular.min.js"></script>
  5. </head>
  6. <body>
  7. <div ng-init="list = ['Chrome', 'Safari', 'Firefox', 'IE'] ">
  8. Number formatting: {{ 1234567890 | number }} <br>
  9. array filtering <input ng-model="predicate">
  10. {{ list | filter:predicate | json }}
  11. </div>
  12. </body>
  13. </html>



Modules and Injector

  - injector 는 Angular Application (ng-app) 에 하나만 존재한다 

  - injector는 명칭을 통해서 오브젝트 인스턴스를 찾는 방법을 제공한다 

  - 오브젝트 인스턴스에 대한 캐쉬를 가지고 있어서 생성된 것을 주거나 없으면 생성한다 

  - moduleprovider로 알려진 injector의 인스턴스 팩토리를 통해 환경 설정하는 방법이다

  


  - 소스

  1. // Create a module
  2. var myModule = angular.module('myModule', [])
  3.  
  4. // Configure the injector
  5. myModule.factory('serviceA', function() {
  6. return {
  7. // instead of {}, put your object creation here
  8. };
  9. });
  10.  
  11. // create an injector and configure it from 'myModule'
  12. var $injector = angular.injector(['myModule']);
  13.  
  14. // retrieve an object from the injector by name
  15. var serviceA = $injector.get('serviceA');
  16.  
  17. // always true because of instance cache
  18. $injector.get('serviceA') === $injector.get('serviceA');

 


$

  - $는 앵귤러의 오브젝트 명칭임을 나타내므로 가급적 충돌을 방지하기 위하여 $는 쓰지 말자 



Backbone.js & Angular.js 

  - angular는 62 page 부터 시작


  - 91 page : Angular Basic Service $로 시작  예) $q 

  - 92 page : 전역 API 예) element는 jQuery lite



<참조>

  - 원문 : Developer Guide - Concepts

  - 다양한 모듈들 : http://ngmodules.org/  ==> Angular안에서 Backbone사용하기 

  - Boostrap 연동 : http://angular-ui.github.io/

  - AngularJS & scope.$apply 의미

posted by 윤영식
2013. 4. 10. 17:35 AngularJS/Concept

애귤러군은 HTML을 어떻게 컴파일할까 알아보자 



1) 개요

  - HTML element 또는 attribute를 확장할 수 있게 해준다. 즉, 사용자 정의하여 새로운 HTML syntax를 브라우징할 수 있게 한다

  - Angular가 해석하는 Behavior Extension 을 directive 라고 한다 

  - Angular는 미리 번들링된 directives 들도 있지만 사용자가 만들 수도 있다 

  - 지시자의 해석은 서버에서도 미리 컴파일 되는 것도 아니다. 컴파일은 그냥 웹브라우저에서 수행한다  



2) 컴파일러

  - Compiler는 앵귤러 서비스이다 

  - 컴파일시 two phases

    + Compile : DOM/directives 해석

    + Link : scope를 가지고 directive를 결합시키고, 실시간 화면을 만든다. 모델이 변하면 뷰도 변하도록 바인딩한다

    예) ng-repeat : 성능향상을 위해 복제된 템플릿은 한번의 Compile이 필요하고, 각 복제된 인스턴스별로 한번 Link된다  



3) 지시자

  - 컴파일 과정에서 HTML 생성시 정의한 것과 매칭시키기 위한 행위자(Behavior) 이다. 

  - element name, attribute, class name, comment 에 놓일 수 있다

  - span 앨리먼트의 애트리뷰트로 사용한 draggable

  1. <!doctype html>
  2. <html ng-app="drag">
  3. <head>
  4. <script src="http://code.angularjs.org/1.0.5/angular.min.js"></script>
  5. <script src="script.js"></script>
  6. </head>
  7. <body>
  8. <span draggable>Drag ME</span>
  9. </body>
  10. </html>



4) 뷰와 모델 바인딩

  - 일반적인 경우 

    + 데이터 변화면 다시 template과 model을 합쳐서 DOM안의 innerHTML 로 보여줘야 함

  



  - 앵귤러 경우

    + Angular compile가 지시자를 가지고 있는 DOM 을 템플릿으로 사용, linking function을 통해서 scope model 과 live view 바인딩을 해준다

    + 별도의 innerHTML 입력이나 이벤트 핸들링등의 코드가 전혀 필요없다 

  


<참조>

  - 원문 : Developer Guide - HTML compiler

posted by 윤영식
2013. 4. 10. 16:52 AngularJS/Concept

앵귤러의 시작 스텝에 대해서 알아보자 



1) 자동 초기화

  - <tag ng-app="name">

    + Angular는 DOMContentLoaded  이벤트가 발생하면 자동 초기화를 수행한다 

    + 이때 Angular는 애플리케이션 root를 만들어주는 ng-app 지시자(Directive)를 찾는다

    + Angular는 Directive 와 관련한 Module을 로드한다

    + 애플리케이션 injector 를 생성한다 

    + ng-app 지시자를 root로 해서 DOM을 Angular appliction으로 컴파일 해준다

  1. <!doctype html>
  2. <html ng-app="optionalModuleName">
  3. <body>
  4. I can add: {{ 1+2 }}.
  5. <script src="angular.js"></script>
  6. </body>
  7. </html>



2) 수동 초기화

  - ng-app을 사용하지 않는다

  - 모든 문서와 소스가 로딩된후 angular.bootstrap(RootDocument)를 컴파일 한다 

  1. <!doctype html>
  2. <html xmlns:ng="http://angularjs.org">
  3. <body>
  4. Hello {{'World'}}!
  5. <script src="http://code.angularjs.org/angular.js"></script>
  6. <script>
  7. angular.element(document).ready(function() {
  8. angular.bootstrap(document);
  9. });
  10. </script>
  11. </body>
  12. </html>



<참조>

  - 원문 : Developer Guide - Bootstrap

posted by 윤영식
2013. 4. 10. 16:38 AngularJS/Concept

AngularJS의 개발자 가이드를 요약해 본다. 



1) 개요

  - 데이터 바인딩에 {{}} 를 사용한다 : Data binding as in {{}}.

  - HTML안에 directives를 설정하여 제어한다 : DOM control structures for repeating/hiding DOM fragments.

  - 입력 폼에 대한 유효성 검사 : Support for forms and form validation.

  - HTML에 코드넣기 : Attaching code-behind to DOM elements.

  - 재사용가능 컴포넌트 형태의 HTML그룹핑 :  Grouping of HTML into reusable components.



2) 적합성

  - CRUD 시스템 개발에 잘 맞음

  - 게임이나, DOM 조작이 많은 GUI Editor등에는 안맞을 수 있고 오히려 jQuery가 잘 맞을 수 있다

  - DOM과 애플리케이션 로직을 분리하는데 최적 : MVC framework

  - Real Browser 테스트를 위하여 최적 : Karma

  - AJAX 통한 데이터 CRUD의 데이터 조작에 대한 반복코드 제거 

  - Guice 스타일의 필요한 모듈을 DI(Dependency Injection) 을 제공



3) 코드 이해

  1. <!doctype html>
  2. <html ng-app>
  3. <head>
  4. <script src="http://code.angularjs.org/1.0.5/angular.min.js"></script>
  5. <script src="script.js"></script>
  6. </head>
  7. <body>
  8. <div ng-controller="InvoiceCntl">
  9. <b>Invoice:</b>
  10. <br>
  11. <br>
  12. <table>
  13. <tr><td>Quantity</td><td>Cost</td></tr>
  14. <tr>
  15. <td><input type="integer" min="0" ng-model="qty" required ></td>
  16. <td><input type="number" ng-model="cost" required ></td>
  17. </tr>
  18. </table>
  19. <hr>
  20. <b>Total:</b> {{qty * cost | currency}}
  21. </div>
  22. </body>
  23. </html>


  - ng-app : Angular가 애플리케이션을 자동 초기화 해주게 하는 지시자이다 

  - ng-model : 양방향 데이터 바인딩하고, 간단한 유효성검사도 수행 (유효하지 않으면 input 박스가 빨간색으로 변함)

  - <script src="http://code.angularjs.org/xxx/angular.min.js"> 에서 angular.min.js 버전별 호출

  - {{ expression | filter }} : 수식을 넣을 수 있고, | 를 이용하여 필터링 가능



<참조>

  - 개발자 가이드 : 개요

 

posted by 윤영식
2013. 4. 9. 09:56 AngularJS/Concept

재사용성을 위하여 사용자 정의 컴포넌트는 어떻게 만들 수 있는지 알아보자. Markdown 컴포넌트를 만들어 본다



1) 코딩하기 

  - <markdown> 태그안에 들어가는 마크다운 포멧을 해석하여 주는 showdown library는 GitHub에서 다운로드 받는다 

  - 해당 내용을 showdown 라이브러리에서 해석할 수 있도록 컴포넌트를 만든다 


  - 사전준비 

     + 소스복제 : git clone https://github.com/ysyun/prototyping

     + 모듈소스 : cd prototyping/angularjs/component


  - component.html 소스코드 

    + ng-app 태그를 통하여 어떤 모듈을 사용할지 지정

    + showdown 로컬에 다운로드

    + component.js 안에 myApp안에 사용할 markdown directive 를 정의한다

<!doctype html>
<html lang="en" ng-app="myApp">
<head>
<meta charset="UTF-8">
<title>AngularJS Hello World using Directives</title>
<script src="http://code.angularjs.org/1.1.4/angular.min.js"></script>
<script src="../../underscore/underscore-min.js"></script>
     <script src="../../showdown/compressed/showdown.js"></script>
<script src="component.js"></script>
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<markdown>
[[MobiCon]](http://mobicon.tistory.com/)

This is Test
============
    'function() { console.log('hi youngsik'); }
</markdown>


</body>
</html>


  - component.js 소스코드 

    + angular.module 정의

    + ng-app=myApp 에 대한 모듈이며, 의존관계 없음

    + new Showdown.converter()를 통하여 마크다운 포멧 해석기 생성

    + 'E' : DOM Element 지정, A-attribute, C-class, M-comment (참조)

angular.module('myApp', [])
    .directive('markdown', function() {
        var converter = new Showdown.converter();
        return {
            restrict : 'E',
            link : function (scope, element, attrs) {
                var htmlDiv = converter.makeHtml(element.text());
                console.log(htmlDiv);
                element.html(htmlDiv);
            },
// template: '<textarea rows="10" cols="10"></textarea>'
        }
    })


** 소스 : https://github.com/ysyun/prototyping/tree/master/angularjs/component



2) 동영상

  - component-1
 
  - component-2



<참조>

  - 원문 : http://blog.angularjs.org/2012/05/custom-components-part-1.html

              http://blog.angularjs.org/2012/05/custom-components-part-2.html

posted by 윤영식
2013. 4. 5. 16:27 AngularJS/Prototyping

AngularJS를 가지고 Twitter Search 기능을 프로토타입핑한다. Angular에서는 jsonp를 어떻게 다루는지 살펴보자 



1) Twitter 검색

  - 검색어를 넣고 트위터로 jsonp 검색을 하고 결과를 출력한다


  - angularJS와 angularJS resource 를 사용한다

    + 서버사이드 데이터를 RESTful하게 가져오기 위한 방법을 제공한다

    + API : http://docs.angularjs.org/api/ngResource.$resource (1.1.4 소스)


  - 사전준비 

     + 소스복제 : git clone https://github.com/ysyun/prototyping

     + 트윗소스 : cd prototyping/angularjs/twitter


  - twitter.html 소스

    + <html ng-app="Twitter"> 를 반드시 추가한다. 설정하지 않으면 하기 에러발생. twitter.js에서 모듈명으로 사용


    + twitter.js angular controller 파일을 별도로 코딩한다

    + <div ng-controller="TwitterCtrl" 추가 

    + <input ng-model="searchTerm" 프로퍼티 추가하여 controller 내의 doSearch function 안에서 검색 파라미터로 사용한다 

    + <button ng-click="doSearch()" 추가하여 검색하기 버튼 클릭하면 호출될 function지정

    + <tr ng-repeat 추가 : angular $resource를 통한 jsonp 호출 결과를 리스팅한다 

<!doctype html>

<html lang="en" ng-app="Twitter">
<head>
<meta charset="UTF-8">
<title>AngularJS Hello World Twitter</title>
<script src="http://code.angularjs.org/1.1.4/angular.min.js"></script>
<script src="http://code.angularjs.org/1.1.4/angular-resource.min.js"></script>

<script src="twitter.js"></script>
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">

</head>
<body>

<div ng-controller="TwitterCtrl">
<form class="form-horizontal">
<input type="text" ng-model="searchTerm">
<button class="btn" ng-click="doSearch()">
<i class="icon-search"></i>Search
</button>
</form>

<table class="table table-striped">
<tr ng-repeat="tweet in twitterResults.results">
<td>{{tweet.text}}</td>
</tr>
</table>
</div>

</body>
</html>


  - twitter.js 소스 

    + angular.module : twitter.html에서 <html ng-app="Twitter" 명칭을 넣어줌. $resource를 사용하기 위하여 ngResource 의존

    + $scope.twitter.get() 호출하게 되면 jsonp 방식으로 호출하여 결과값을 가져온다

angular.module('Twitter', ['ngResource']);

function TwitterCtrl($scope, $resource){
$scope.twitter = $resource('http://search.twitter.com/:action',
{action:'search.json', q:'angularjs', callback:'JSON_CALLBACK'},
{get:{method:'JSONP'}});

// $scope.twitterResults = $scope.twitter.get(function(){ alert(' hi dowon jsonp '); });

$scope.doSearch = function () {
$scope.twitterResults = $scope.twitter.get({q:$scope.searchTerm});
}

}



2) 동영상


  - 소스 : https://github.com/ysyun/prototyping/tree/master/angularjs/twitter

posted by 윤영식
2013. 4. 5. 14:36 AngularJS/Prototyping

Backbone 으로 todo 목록을 처음 만들어 보았을 때 이해와 타입핑을 하는데 많은 시간을 소비하였다. Angular를 접하면서 Todo 목록 만들기가 얼마나 쉽고 빠르게 몇분만에 개발되는지 감상해 보자. 직접 해보았는데 20분정도 소요된것 같다. 그간 배운 빅데이터는 이와 같은 쉬운 웹앱기술로 통합되어 스마트 기기에 표현될 수 있겠다. 



1) 할일 목록 만들기 

   - 사전준비 

     + 소스복제 : git clone https://github.com/ysyun/prototyping

     + 투두소스 : cd prototyping/angularjs/todo


   - 할일 목록을 등록하고 완료체크를 하고 완료된 것은 삭제하는 기능을 갖는다

   

  - Bootstrap, Underscore를 이용하고, Angular Controller를 별도 .js로 분리한다 

  - todo.html

    + <html ng-app> 추가

    + <script> : angular.js, underscore.js, todo.js, bootstrap.css 파일을 연결한다

    + <div ng-controller> 추가 : controller function은 todo.js안에 구현 

    + getTotalTodos() : controller function 안에 구현 - 목록이 변경되면 자동 호출되어 바인딩됨 

    + <li ng-repeat> 추가 : todos 목록 배열을 열거한다 

    + <form> 의 <input ng-model="formTodoText"> 속성을 정의하고 controller function에서 사용토록 한다  

    + <button ng-click="clearCompleted()" 추가 : controller function에서 구현한다

<!doctype html>
<html lang="en" ng-app>
<head>
<meta charset="UTF-8">
<title>AngularJS Hello World using Directives</title>
<script src="http://code.angularjs.org/1.1.4/angular.min.js"></script>
<script src="../../underscore/underscore-min.js"></script>
<script src="todo.js"></script>
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="todo.css">
</head>
<body>

<div ng-controller="TodoCtrl">
<h2>Total count : {{getTotalTodos()}}</h2>
<ul class="unstyled">
<li ng-repeat="todo in todos">
<input type="checkbox" ng-model="todo.done">
<span class="done-{{todo.done}}">{{todo.text}}</span>
</li>
</ul>
<form class="form-horizontal">
<input type="text" ng-model="formTodoText" ng-model-instant>
<button class="btn" ng-click="addTodo()">
<i class="icon-plus"></i>Add
</button>
</form>

<button class="btn-large" ng-click="clearCompleted()">
<i class="icon-trash"></i>Clear Completed
</button>
</div>

</body>
</html>


    + 브라우져에 출력되는 DOM Element 소스 

    


  - todo.css

.done-true{
text-decoration: line-through;
color: grey;
}


  - todo.js

    + todos : 목록 배열

    + function들 정의

function TodoCtrl($scope) {

$scope.totalTodos = 4;

// 가상 데이터를 만든다
$scope.todos = [
{text:'Learn you', done:false},
{text:'build SPA', done:false}
];

// html에서 전체 건수를 return 해 준다
$scope.getTotalTodos = function() {
return $scope.todos.length;
}

// Underscore.js 를 이용하여 done=false 인 것만 배열로 뽑아낸다
$scope.clearCompleted = function() {
$scope.todos = _.filter($scope.todos, function(todo){
return !todo.done;
});
}

// todos 배열에 html에서 입력한 값을 넣어준다
$scope.addTodo = function() {
$scope.todos.push({text:$scope.formTodoText, done:false});
$scope.formTodoText = '';
}
}



2) TodoList 만들기 동영상

 

  - 소스 : https://github.com/ysyun/prototyping/tree/master/angularjs/todo

posted by 윤영식
2013. 4. 4. 17:59 AngularJS/Prototyping
AngularJS의 세계에 빠져보자.  BackboneJS 를 통하여 자유로운 영혼의 Client Side MV* framework 맛 보았다면 이제 AngularJS를 통하여 좀 더 절제되고 Fast Creating 가능한 방법에 대해 고민해 볼 때가 된 것 같다. 커뮤니티를 통하여 성숙하고 있고, 좋은 문서와 구글이라는 든든한 백그라운드까지 갖추고 있어서 무한한 성장 가능성을 가지고 있는 프레임워크라고 본다


1) Hello World
  - 구글 CDN 참조
  - 소스
<!doctype html>
<html lang="en" ng-app>
<head>
<meta charset="UTF-8">
<title>AngularJS Hello World</title>
</head>
<body>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.min.js"></script>

<script>
function DowonController($scope) {

$scope.DowonProperty = "YoungSik";
}
</script>

<div ng-controller="DowonController">

<input type="text" ng-model="DowonProperty" ng-model-instant/>
Hello World

{{DowonProperty}}

</div>

</body>
</html>
 
  - 코딩 순서
    + angularJS CDN script 태그 추가 
    + <html ng-app> 추가
    + <div ng-controller> 추가 
    + controller function 구현 : $scope에 속성추가/설정
    + ng-model 추가 
    + {{ property }} 클라이언트 템플릿 추가 
    + 수행 : WOW!!!

  - 이해하기 
    + ng-app를 선언한 DOM 하위는 angular를 통하여 제어가 가능하다. 
    + ng-model을 HTML안에서 선언하면 controller function에서 프로퍼티를 사용할 수 있다
    + controller function에서 프로퍼티를 선언하면 HTML안에서 사용할 수 있다
    + 결국 Angular.js 는 양방향 Live Stream(Dynamic) binding을 가능하게 해준다  


2) 동영상
    


  - 소스 : https://github.com/ysyun/prototyping/blob/master/angularjs/hellworld.html



<참조> 

  - 동강 모음 http://johnlindquist.com/

  

  - 페이스북 공개그룹 : https://www.facebook.com/groups/frontiner/permalink/525021784200620/


AngularJS Hello World 
http://www.youtube.com/watch?v=uFTFsKmkQnQ&list=PL1w1q3fL4pmgqpzb-XhG7Clgi67d_OHXz

AngularJS - Getting Started - Google+
https://plus.google.com/communities/115368820700870330756/stream/f1fab692-830e-4341-b010-d0078e18719f

1. Devoxx 2012: AngularJS 
http://www.youtube.com/watch?v=4EVBg1pNdtc&list=PL1w1q3fL4pmgqpzb-XhG7Clgi67d_OHXz

2. Mastering AngularJS - 1. Introduction 
http://www.youtube.com/watch?v=DfstuptCfUo&list=PL1w1q3fL4pmgqpzb-XhG7Clgi67d_OHXz

3. Mastering AngularJS - 2. Using Directives .....감동의 도가니탕
http://www.youtube.com/watch?v=2SZQslQOgNg&list=PL1w1q3fL4pmgqpzb-XhG7Clgi67d_OHXz

4. AngularJS Tutorial .....감동의 도가니탕
http://www.youtube.com/watch?v=WuiHuZq_cg4&list=PL1w1q3fL4pmgqpzb-XhG7Clgi67d_OHXz

5. AngularJS Twitter Search 
http://www.youtube.com/watch?v=IRelx4-ISbs&list=PL1w1q3fL4pmgqpzb-XhG7Clgi67d_OHXz

6. AngularJS - Directive Tutorial 
http://www.youtube.com/watch?v=Yg-R1gchccg&list=PL1w1q3fL4pmgqpzb-XhG7Clgi67d_OHXz

7. AngularJS - Custom Components - Part 1 
http://www.youtube.com/watch?v=A6wq16Ow5Ec&list=PL1w1q3fL4pmgqpzb-XhG7Clgi67d_OHXz

8. AngularJS - Custom Components - Part 2 
http://www.youtube.com/watch?v=nKJDHnXaKTY&list=PL1w1q3fL4pmgqpzb-XhG7Clgi67d_OHXz

9. AngularJS - Communicating Between Controllers 
http://www.youtube.com/watch?v=1OALSkJGsRw&list=PL1w1q3fL4pmgqpzb-XhG7Clgi67d_OHXz

10. AngularJS and DOM Manipulation 
http://www.youtube.com/watch?v=bk-CC61zMZk&list=PL1w1q3fL4pmgqpzb-XhG7Clgi67d_OHXz

11. AngularJS - Handy Tricks With Developer Tools ...어려워 나중에
http://www.youtube.com/watch?v=4TM6wG9UW7s&list=PL1w1q3fL4pmgqpzb-XhG7Clgi67d_OHXz

12. AngularJS Routing ...............................................................내일은 여기서부터
http://www.youtube.com/watch?v=5uhZCc0j9RY&list=PL1w1q3fL4pmgqpzb-XhG7Clgi67d_OHXz

13. AngularJS - Behave like Gmail using Routes with Resolve 
http://www.youtube.com/watch?v=P6KITGRQujQ&list=PL1w1q3fL4pmgqpzb-XhG7Clgi67d_OHXz

14. AngularJS + Chosen Plugin = Awesome 
http://www.youtube.com/watch?v=8ozyXwLzFYs&list=PL1w1q3fL4pmgqpzb-XhG7Clgi67d_OHXz

posted by 윤영식
2013. 1. 24. 21:58 AngularJS/Concept

Twitter Bootstrap과 Node.js를 이용하여 Single Page App을 만들어 보자. 여기서는 Backbone.js를 배제하고 만드는 방법이다. 외국 블로깅 글을 따라서 실행보면서 정리한다. (총 3 part중 part 1)


서버단 코드 만들기


1) GitHub에서 소스 다운로드 및 사전 설정

  - 소스 다운로드 : $ git clone https://github.com/iatek/one-page-app.git

  - node.js 와 npm 이 설치되어 있어야 한다

  - Express 프레이워크 설치 : npm install express

  - ejs 템플릿 엔진 설치 : npm install ejs npm install ejs-locals

$ npm install ejs-locals

npm http GET https://registry.npmjs.org/ejs-locals

npm http 304 https://registry.npmjs.org/ejs-locals

ejs-locals@1.0.2 node_modules\ejs-locals


// ejs 설치 안하면 에러 발생함 

UserXP@NUKNALEA /d/Git_repositories/one-page-app (master)

$ node server


module.js:340

    throw err;

          ^

Error: Cannot find module 'ejs'

    at Function.Module._resolveFilename (module.js:338:15)

    at Function.Module._load (module.js:280:25)

    at Module.require (module.js:362:17)

    at require (module.js:378:17)

    at Object.<anonymous> (d:\Git_repositories\one-page-app\node_modules\ejs-locals\index.js:1:73)

    at Module._compile (module.js:449:26)

    at Object.Module._extensions..js (module.js:467:10)

    at Module.load (module.js:356:32)

    at Function.Module._load (module.js:312:12)

    at Module.require (module.js:362:17)


UserXP@NUKNALEA /d/Git_repositories/one-page-app (master)

$ npm install ejs

npm http GET https://registry.npmjs.org/ejs

npm http 304 https://registry.npmjs.org/ejs

ejs@0.8.3 node_modules\ejs


// 정상 수행됨 

UserXP@NUKNALEA /d/Git_repositories/one-page-app (master)

$ node server

Listening on port 4000 in development mode


// 로컬에서 브라우져 호출시 reponsive web으로 반응한다 -width를 줄이면 메뉴자동변경됨- (데모확인)


  


2) Express 환경파일 설정

  - app.js

app.js


// ejs template engine 사용
var express = require('express'),
	engine = require('ejs-locals'),   
	app = express();

// init export
exports.init = function(port) {
    app.locals({
		_layoutFile:'layout.ejs'
	})
	
    app.configure(function(){
    	app.set('views', __dirname + '/views');
    	app.set('view engine', 'ejs');
    	app.use(express.bodyParser());
    	app.use(express.methodOverride());
        // Static pages 서비스 
        app.use(express.static(__dirname + '/static'));
        // Session과 Cookie 관리 
        app.use(express.cookieParser());
        app.use(express.cookieSession({cookie:{path:'/',httpOnly:true,maxAge:null},secret:'skeletor'}));
    	app.use(app.router);
    	//app.enable("jsonp callback");
    });

    app.engine('ejs', engine);	
    app.configure('development', function(){
        app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); 
        // app.use(express.logger({ format: ':method :url' }));
    });

    app.configure('production', function(){
	   app.use(express.errorHandler()); 
    });

    app.use(function(err, req, res, next){
	   res.render('500.ejs', { locals: { error: err },status: 500 });	
    });
	
    server = app.listen(port);
    console.log("Listening on port %d in %s mode", server.address().port, app.settings.env);
    return app;
}


3) Routing 만들기 

  - server.js : app이 시작하는 초기 파일이다. app route를 정의한다

server.js


  - "app.get", "app.post", "app.del" 을 사용한다 

  - 4개의 HTTP get routing 호출  

    /(default homepage)

    /login

    /logout

    /admin

var port = process.env.PORT || 4000,
    app = require('./app').init(port),
	dirty = require('dirty');
	
var locals = {
	author:'in1'
	// add other vars here
};

var userDb = dirty('user.db');

app.get('/', function(req,res){
    locals.date = new Date().toLocaleDateString();
	
	var appDb = dirty('app.db'),
		sectionsDb;
	
	appDb.on('load', function() {
		sectionsDb = dirty('sections.db');
		sectionsDb.on('load', function() {
			/*
			sectionsDb.forEach(function(key, val) {
				console.log('Found key: %s, val: %j', key, val);
			});
			*/
			res.render('index.ejs', {locals:locals,sections:sectionsDb,app:appDb.get('app'),page:appDb.get('page')});
		});
	});
});
... 중략 ...
app.post('/contact', function(req,res){
	appDb = dirty('app.db');
	console.log('contact form submit.');
	
	appDb.on('load', function() {
		console.log('Form submitted.');
		res.send('Thank you.');
	});
});

/* The 404 Route (ALWAYS Keep this as the last route) */
app.get('/*', function(req, res){
    res.render('404.ejs', locals);
});


4) Persistence 유지 하기 

  - Node-Dirty를 이용하여 disk log형태로 가벼운 데이터베이스겸으로 사용한다

  - 5개의 database 파일안에 관련 표현할 내역이 .db파일에 들어있음 (백만 record 미만 사용하는 앱에 적당)

    app.db : 애플리케이션 래벨 설정

    sections.db : 섹션 설정과 내용 저장

    user.db : 사용자 credential 과 admin 권한

    snippet.db : 미리 생성할 섹션 내용을 위한 코드 조각 저장

    post.db : 연락처와 이메일 폼 결과 저장 

  - server.js에서 require('dirty')


> 다음은 UI Part to be continued...


<참조>

  - Single Page App with Twitter Bootstrap and Node.js : 데모사이트

  - One page app GitHub 소스

  - SPA를 위한 storage : node-dirty

  - Node.js 기초 강의 동영상

  - Node의 MVC Framework : Express.js

  - Bootstrap 확장판

posted by 윤영식
2013. 1. 11. 00:04 AngularJS/Concept

1) Single Page Application을 만들기 위한 기본기 다지기

  - CoffeeScript을 사용한다 : JavaScript 문법오류에서 해방 될 수 있다

  - Backbone을 사용한다 : CoffeeScript와 함께 사용한다 

  - JSON을 통하여 Rest API를 사용한다 

  - 대표적 SPA 서비스 : Trello, Facebook, Gmail


2) Single-Page Application을 위한 Backbone과 Node.js

  - Airbnb 서비스 사용예

  - I18n 


- SPA를 위한 Toolchain

posted by 윤영식
prev 1 2 next