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

Publication

Statistics Graph

Recent Comment

2017.04.23 15:51 Angular/Concept

Angular CLI를 통해 프로젝트를 생성하고 Docker에 Nginx를 띄워서 간단히 연결해 보자. 




Angular CLI 기반 프로젝트 생성


NodeJS버전은 6최신을 사용한다. 

$ npm install -g @angular/cli 


설치가 되었다면 ng 명령을 통해 프로젝트를 생성한다. 

$ ng new angular-docker


프로젝트가 생성되었으면 프로젝트 폴더로 이동해서 소스를 빌드한다. 

$ cd angular-docker

$ ng build

Hash: cfba09939df99de45615

Time: 7424ms

chunk    {0} polyfills.bundle.js, polyfills.bundle.js.map (polyfills) 165 kB {4} [initial] [rendered]

chunk    {1} main.bundle.js, main.bundle.js.map (main) 3.61 kB {3} [initial] [rendered]

chunk    {2} styles.bundle.js, styles.bundle.js.map (styles) 9.77 kB {4} [initial] [rendered]

chunk    {3} vendor.bundle.js, vendor.bundle.js.map (vendor) 2.09 MB [initial] [rendered]

chunk    {4} inline.bundle.js, inline.bundle.js.map (inline) 0 bytes [entry] [rendered]





Docker CE 기반 Nginx 관리


Docker를 잘 모른다면 Docker CE를 설치해 사용한다. 

  - download docker CE (Community Edition)

 - Mac 버전을 설치하면 Docker 가 기동이 된다.

 

     


다음으로 Nginx image 를 docker명령을 통해 내려받는다. 만일 사용하지 않는 Docker image들이 있다면 다 삭제를 한고 작업을 해보자.

// Delete all docker containers

$ docker rm $(docker ps -a -q)


// Delete all docker images

$ docker rmi $(docker images -q)


// Install docker image

$ docker pull nginx

Using default tag: latest


// 설치 내역 확인

$ docker images 

REPOSITORY        TAG                 IMAGE ID            CREATED             SIZE

nginx                  latest              5766334bdaa0   2 weeks ago         183 MB


Angular 빌드 폴더로 이동해서 Nginx를 수행한다. 

$ cd dist

$ docker run -d -p 8080:80 -v $(pwd):/usr/share/nginx/html nginx:latest

48db2f0626c5a10429e95cbbbaf3cc58769cda45a9c1e7084b4f3a260e576838


// 이미지 실행 확인

$ docker ps -a

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                           NAMES

48db2f0626c5        nginx:latest        "nginx -g 'daemon ..."   2 minutes ago       Up 2 minutes        443/tcp, 0.0.0.0:8080->80/tcp   loving_lumiere


수행되고 있는 이미지를 멈추고 제거해 보자. stop, rm 다음은 Container ID의 처음 두자를 입력한다. 

// 멈춤 

$ docker stop 48

48


// 제거

$ docker rm 48

48


// 확인

$ docker ps -a

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES


Kinetic 애플리케이션을 사용해서 CLI 명령과 동일하게 GUI 기반으로 Container를 관리할 수 있다.




Docker Compose 사용하기


컴포즈는 환경을 기반으로 다양한 도커 이미지들을 동시에 기동하고 연결하는 역할을 수행한다. Nginx, NodeJS, MongoDB등을 서로 다른 Docker image기반으로 기동하고 연결하는 환경설정을 docker_compose.yml에 한다. angular-seed 예는 다음과 같다. 

version: '2'


services:


  angular-seed:

    build:

      context: .

      dockerfile: ./.docker/angular-seed.development.dockerfile

    command: npm start

    container_name: angular-seed-start

    image: angular-seed

    networks:

      - dev-network

    ports:

      - '5555:5555'


networks:

  dev-network:

    driver: bridge


빌드하고 수행하기 

$ docker-compose build

$ docker-comopse up


ng-conf 2017 의 docker 이야기




<참조>



신고
posted by peter yun 윤영식
2016.12.20 20:54 Angular/Concept

오늘은 Progressive Web Apps 개발에 대한 세미나 참석 내용을 정리해 본다. 



AMP

Progressive는 구글이 생각하는 서비스에 대한 점진적인 개발 진행 방향을 이야기 한다. 즉, 기술아니라 사용자 경험을 더 좋게 하기 위한 개념이다. AMP (Accelerated Mobile Pages) 는 모바일 페이지를 빠르게 만드는게 목표이다. 네트워크가 빠르면 모바일 웹이 느리다는 것을 느낄 수 없지만 느리면 반응이 느려진다. 해당 기술은 데스크톱에도 동일하게 적용될 수 있다. AMP는 정적 페이지를 위한 것이다. 


- amp html


<html amp>를 넣는다. <style amp-boilerplate>를 넣어 페이지 로딩바가 보이도록 한다. 

<!doctype html>

<html amp>

 <head>

   <meta charset="utf-8">

   <link rel="canonical" href="hello-world.html">

   <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">

   <style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>

   <style amp-custom>

    

   </style>

   <script async src="https://cdn.ampproject.org/v0.js"></script>

 </head>

 <body>Hello World!</body>

</html>


- <link rel="canonical" href="amp.html">  amp를 지원하지 않는 브라우저대응을 위한 대신할 html 지정 

- 속도를 위해 amp로 된 웹페이지를 하나 더 만들어 서비스 하는것이다. amp는 기존의 html, css를 변경해서 적용하는 것이다. 



Service Worker

Web Worker는 메인페이지와 병렬 스크립트를 실행하는 백그라운드 워커를 생성하는 API로서 메세지 전송기반의 스레드 수행과 유사하다. UI Rendering과 분리되어 수행할 수 있다. 따라서 Web Work에서는 


- DOM을 직접 건드릴 수 없다. 

- 자체적인 글로벌 스코프

- 일부 속성과 API만 허가: 보면서 디버깅할 수 있는 것이 없다


Service Worker는 오프라인일 경우에도 웹 어플리케이션의 기동을 가능토록 하는 훅(Hook)을 포함하여, 어플리케이션으로 하여금 지속적인 백그라운드 포로세싱의 장점을 취하도록 하는 방법. 브라우저에서 로컬 웹서버가 있는 것과 같다. 


- 지속적인 백그라운드 처리

- 브라우저에 무언가 설치하는 것이다


정적인 파일에 대한 요청이 있을 때 네트워크가 끊어졌을 때 로컬 브라우저에서 서비스를 할 수 있게 한다. 백그라운드에서 정적 파일을 가져와서 브라우저 요청을 로컬 Service Worker 에서 처리해 준다. Application Cache의 스펙 오류를 해결하기 위해 Service Worker가 나왔다. 


- 기본 https 에서만 작동한다. 해킹방지: 중간 공격자를 피하기 위함

- 대신 127.0.0.1, localhost 테스트는 가능하다 


예, 오프라인 웹앱, 구글의 공룡게임 - 브라우저에 함께 존재한다. 


배워야 할 것은 


- 리소스 저장을 위한 Cache Storage - promise객체를 반환 

- 이벤트 생명 연장의 꿈: .waitUntile()  이벤트가 종료되지 않도록 한다  

- 패치 onfetch() 브라우저에서 리소스 접근 요청될 때 호출되는 이벤트 -> FetchEvent 요청내용을 담고 있다. .responseWith() 통해 응답한다



NAMP-CARD 

https://ampbyexample.com 좀 더 심화된 예제 사이트 이다. sw.js를 작성한다. 


var CACHE_NAME = 'pwa-workshop.github.id-namp-card-cache-v1';

// 하기 해당하는 내용을 캐쉬에 저장한다 

var urlsToCache = [

'/namp-card',

    '/index.html',

    '/manifest.json',

    '/user.png'

];


self.addEventListener('install', function(event) {

event.waitUntil(

caches.open(CACHE_NAME).then(function(cache) {

console.log(`Opened cache for namp-card ${new Date()}`);

return cache.addAll(urlsToCache);

})

);

});


self.addEventListener('fetch', function(event) {

    event.waitUntile(

        // 캐쉬안에 브라우저 요청과 매칭되는 것이 있으면 리턴을 해준다. 

        // 없으면 fetch(event, request.url)로 원격 서버에 요청 

        caches

            .match(event.request.url)

            .then(function(res) {

                return res || fetch(event, request.url);

            })

    )

});


등록을 한다. amp-install-serviceworker 스크립트를 이용하면 하기 내용이 들어간다. 개발자가 직접 넣을 수 없다. 

navigator.serviceWorker.register('/sw.js', { scope: '/' })

          .then(function(registration) {

                console.log('Service Worker Registered');

          });


https://airhorner.com/ 예의 DevTool에서 Service Worker를 볼 수 있다. 




모바일 기기에 설치형 웹앱 만들기

설치형이란? 


- 네트워크 불안하거나 오프라인인 경우에도 동작

- 홈스크린에서 바로 실행

- 앱의 이름과 아이콘을 제공


동작 가능한 웹앱


- 서비스워커의 지원 필요

- 프리캐쉬(Pre-cache)된 애플리케이션 프레임워크(App-shell) 사용

- 서비스워커를 통해 Indexed DB 또는 히스토리컬 데이터 디스플레이 


반드시 웹 메니페이스(Manifest)를 작성해야 함


- 설치 방법을 기술: 브라우저에서 Add to Home Screen 하면 모바일 바탕화면에 아이콘이 나온다. -> 굳이 앱을 설치하지 않아도 접근 가능토록 한다

- Splash 윈도우 사용가능: 아이콘 지정, 텍스트 지정 

- Prompt (부추기기)를 통해 Add to Home Screen를 하도록 유도한다. 이를 통해 앱의 설치없이 전환율을 높인다. 

- 노드 모듈의 pwa-manifest를 설치해서 사용할 수도 있다. 



실제로 수행하는 순서 참조


-  https://github.com/pwa-workshop/roadshow/blob/master/turn-into-an-installable-webapp.md

- github.com의 개인 프로필로에서 https 를 사용토록 한다.  



<참조> 

https://github.com/pwa-workshop/namp-card

https://www.ampproject.org/ko

신고
posted by peter yun 윤영식
2016.10.23 22:59 Angular/Concept

Angular v2가 정식 릴리즈되었다. Angular v1 은 Two-way data-binding 이라는 독특한 특징으로 인해 많은 사용자 층을 확보했지만 장점 만큼이나 성능상의 단점도 존재했었다. 또한 처음엔 쉬운듯 하면서 좀 더 깊게 들어가볼려고 하면 학습곡선이 갑작이 껑충뛰기도 했다. 가장 많이 사용했던 Directive(지시자)가 대표적이다. 많은 개발자가 만들어 놓은 지시자를 쉽게 가져다 쓸 수는 있지만 직접 만들어 애플리케이션에 접목하려 할 때 첫 문턱을 만나게 된다. 그리고 jQuery사용에 익숙한 개발자에게 Angular v1 시점상의 차이로 Angular v1 방식의 개발패턴을 요구하기도 했다. 관성은 무섭다. 기존에 사용하던 방식을 버리고 Angular v1에 맞춰서 애플리케이션을 만들어 가기란 곤혹스럽다. Angular v2 또한 그런 인식의 전환을 요구할까? 그렇다 그리고 아니다. 






웹 애플리케이션 흐름

웹 애플리케이션 개발을 위해 우리가 사용하는 jQuery같은 라이브러리나 Angular, Backbone같은 프레임워크의 가장 1차적인 목적은 무엇일까? 나는 Data Projection이라 생각한다. 데이터를 화면에 출력하기 위해 DOM을 얼마나 쉽게 조작하고 상호 작용할 수 있느냐가 선택의 기준이라 생각한다. Data Projection을 일관되고 확장가능하고 배포가능하게 하는 방식으로 기술은 발전해 왔고, 현재는 화면에 대한 제어방식이 컴포넌트 기반 방식으로 발전해 오고 있다. 


Data Projection의 역사를 보면 초장기엔 Server Side Rendering 를 사용해 웹 애플리케이션을 개발했다. 예로 JSP, PHP, ASP 같이 서버 미들웨어서 데이터를 조회하고 HTML을 조작하여 결과 HTML을 브라우져에 전송하던 시대이다. 




1세대에는 AJAX가 나오고 다양한 라이브러리나 프레임워크가 나왔다. 이때는 데이터변경에 대한 DOM반영이 서버에서 클라이언트 개발자의 몫으로 넘어오게 되었다. 즉, 직접 DOM 을 얻어와서 특정 위치에 넣어 주어야 했고, DOM에서 발생하는 이벤트를 Listening해서 처리하고 DOM에 반영하는 모든 작업이 웹 개발자가 직접 코딩하던 단계였다. Java의 프레임워크 역사로 보면 Struts 로 비유할 수 있지 않을까 싶다. 




2세대로 넘어오게 되면 Model을 DOM 에 반영하는 방식은 자동화 된다. 여기에 대표적인 프레임워크가 Ember 와 Angular v1 이다. 이때 부터 Single Page Application (SPA) 개발이라는 용어가 나오게 된다. URI 변경에 대한 대응으로 Routing  개념이 나오고, Data Projection후 원하는 일부 DOM을 변경하는 역할이 프레임워크로 넘어갔고, 웹 개발자는 좀 더 애플리케이션 비즈니스 로직에 집중토록 만들었다. Java 프레임워크로 비유하자면 Struts와 Spring Framework 초기버전의 중간 지대 정도 쯤이라 생각한다.  이때부터 Frontend (프론트앤드)라는 직군이 웹 개발자와 분리되기 시작한 지점이라 생각한다. 이에 대한 자세한 설명은 태곤님이 작성한 "[번역] 프론트엔드 개발자는 왜 구하기 어렵나요?"를 참조하자. 2011년을 기점으로 2013년 웹 애플리케이션 프레임워크가 정착을 해가는 시기였고, 현재는 대부분의 스타트업이나 중견기업에서 2세대 웹 애플리케이션 프레임워크를 선택할 경우 프론트엔드 개발자와 백앤드 개발자를 구분하여 팀을 구성하고 있는 추세이다.





3세대는 2세대의 과도기를 거쳐 2세대의 장점을 흡수 하면서 성능상의 이슈를 해결하고, 점점 복잡해 지고있는 웹 애플리케이션을 보다 직관적이고 쉽게 개발할 수 있게 노력하고 있다. 대표적인 프레임워크로는 Facebook의 React와 Google의 Angular v2 (이하 Angular)이다. Angular는 Component기반 개발 방식으로 표준인 Web Components를 지원하며 Typescript를 기본 언어로 채택했다. Typescript는 Type 시스템을 제공하기 때문에 개발단계에서 버그의 가능성을 쉽게 찾을 수 있도록 도와준다. React와 Angular에 대한 장단점은 손창욱님의 "React보다 Angular v2에 더 주목해야 하는 이유"를 참조하자. Java의 Spring Framework이 성숙하면서 Annotation 같은 기능이 추가되듯, Angular v2 프레임워크는 Java의 Spring 프레임워크 최신버전과 비유할 수 있다. 



Angular v1에 대한 개발 및 컨설팅을 3년 가까이 하면서 올해 초 Angular v2를 공부하고 기존 v1 코드를 v2 코드로 전환하면서 코드 베이스는 50%가량 줄었고, 반응속도는 30%가량 개선되었다. 8명 프론트앤드 개발자와 컨버전을 진행하면서 이구동성으로 말하는 것은 "코드가 직관적으로 변했다. 코드량이 현저히 줄었다. Typescript의 타입체킹으로 인해 실수를 최소화 할 수 있었다" 이다. 



Angular v2 왜 배워야 하는가?

Angular를 왜 배워야 하는가? 답하자면 안배워도 된다. 단순 홈페이지나 업무 화면이라면 쉽고 더 빨리 만들 수 있는 워드프레스나 서비스를 이용하거나 DOM 핸들링 라이브러리나 플러그인을 사용해 개발하는 편이 낳다. 하지만 솔루션의 복잡한 요구사항을 지속적으로 반영해야 하고 DOM제어가 복잡해 질 가능성이 높다면 jQuery, React 같은 라이브러리 보다는 Angular 같은 프레임워크를 선택하는 것이 좋다. 그리고 최근에는 ES2015 표준이 확정되었고 최신 브라우져에 대부분 기능이 구현되고 있다. 2세대와 3세대 Data Projection의 가장 큰 개발 방식의 차이는 ES2015의 이해에서부터 시작한다.  즉, ES2015 문법을 잘 알고 사용하면 좀 더 쉽고 간단하게 코드 베이스를 유지하면서 오류를 최소화할 수 있다. 예로 -> 펑션은 this에 대한 오류를 방지하고, Set/Map등 Collection은 Java의 Collection과 유사한다. 



Angular v2 시작하면 초기에 배워야 하는 것들이 갑작이 늘어난다. 이것은 2세대와 3세대의 개발 패턴이 바뀌었음을 시사한다. ES2015 문법은 그대로 TypeScript에 녹아 있고, Type System과 Annotation 기능이 녹아 들어 더욱 편리한 개발을 가능토록 한다. 따라서 ES2015의 Syntax와 개념을 이해해야 한다. 그리고 Typescript를 다시 공부해야 한다. 또한 요즘 인기를 누리고 있는 Reactive Programming을 표방한 대표적인 라이브러리인 RxJS를 Angular가 근간으로 사용하고 있다. 따라서 RxJS 에 대한 개념과 사용법을 익혀야 한다. 그런후 Web Components 란 무엇인지 알아야 하고, Angular 프레임워크의 아키텍쳐를 구성하는 개념인 Change Detection 동작원리, Dependency Management, Modulization 을 알아야 하고, 다음으로 주변의 Tooling System으로 SystemJS (Webpack), Gulp 등을 알아야 한다. 


이렇게 열거해 보니 참으로 배울 것이 많다. 다시 말하지만 안 배워도 된다. 하지만 자신의 근육을 한단계 업그레이드 시키기 위해 고통스러운 인내의 시간은 필요하다. 배워야 하는 기준은 두가지 정도로 이야기 해본다. 


첫째, 서비스 버전업을 위해 요구사항이 계속 증가하고 있는가?

둘째, 더 적고 직관적인 코드 베이스를 유지하면서 성능을 높이고 싶은가?


 

프론트엔드 개발자 직군이 새롭게 자리잡게된  5년기간 동안 많은 부분이 기존의 백앤드 개발 패턴과 유사해 지고 있다. 모듈 의존성 관리, 빌드 시스템, 프레임워크의 발전은 Java개발자들이 초장기 프레임워크 없이 개발하다 Struts를 만났을 때 기쁨에서 Spring을 만나 자유를 얻었지만 여전히 배워야 할 것들은 더욱 증가했음을 알것이다. 그러나 어쩌겠는가 우리는 더 게을러 지고싶다는 욕구가 있고 프레임워크가 그것을 만족시켜줄것이라는 희망을 품고 있는 한 배움과 진보는 계속될 뿐이다. 



참조


신고
posted by peter yun 윤영식
2016.05.01 15:34 Angular/Concept

컴포넌트 기반 개발시에 어떤 설계방식으로 해야하는지 알아본다. 새롭게 알게된 GistRun 서비스를 사용해서 테스트해 볼 것이다. 예제의 내용은 최근 Rangle.io의 Angular2 Component 온라인 강좌를 참조한다.





GistRun 사용하기 


github기능중 코드 조각을 관리할 수 있는 서비스가 있다. 한번에 최대 10개까지 파일을 만들 수 있다.  여기를 클릭해 보면 Gist에 입력한 한 주제에 대한 파일을 보여주고 Gist의 내역을 테스트 해 볼 수 있다. 



plunker 또는 jsfiddle의 내용도 불러와서 나의 Gist에 저장을 할 수 있다. 각 서비스의 장점이 있지만 일단 GistRun은 UI가 깔끔하고 속도가 빠르다. 그리고 수정사항에 대한 버전을 관리할 수 있고, Github의 종속된 서비스이므로 소스를 관리한다는 입장에 일관성을 가질 수 있을 것으로 보인다. Gist만으로 모자랐던 것을 GistRun이 생명력을 불어 넣은 격이다. 





Angular2 Component 가이드


Angular2는 컴포넌트로 시작해서 컴포넌트로 끝난다. 컴포넌트기반으로 UI를 설계할 때 자주 나오는 방식이 프리젠테이션과 컨테이너의 분리이다. 


  - Container 컴포넌트

    + 데이터와 비즈니스 로직을 다룬다

    + 컴포넌트 트리(Tree)를 렌더링 한다

    + 재사용하기 어렵다 


  - Presentational 컴포넌트

    + 상태를 포함하지 않는다. 따라서 컴포넌트 속성에 대한 수순(pure) 펑션으로 구성한다

    + 표현만을 담당한다

    + 재사용이 가능하다 


Thinking in React의 내용은 Angular2의 컴포넌트 개발 방식과 거의 일치한다. Container 컴포넌트는 하위 컴포넌트의 이벤트에 대해 XHR 요청을 수행하는 Angular2 Service를 Inject해서 서버로 부터 데이터를 요청하고, Component Tree를 통해 하위로 데이터를 내려 보낼 수 있다. 



오렌지색 부분은 전체 애플리케이션을 감싸는 Container이고 초록색은 테이블의 Row를 표현한다. 프리젠테이션 부분은 상단의 Search Input, 하단의 Row들이 된다. Angular2에서 검색을 위한 Input과 Button, Title 애플리케이션을 다음과 같이 진행한다. 



Seach The Site 레이블을 표현하는 컴포넌트를 개발한다.  @은 Typescript의 Decorator를 이용해 Angular2에 구현된 것으로 Parent -> Child 방향으로 DOM Properties를 통해 값을 받고자 할 때 사용한다. 즉, RangleLabel 컴포넌트를 사용하는 Parent 컴포넌트가 name 속성에 [] 을 이용해 값을 설정한다.  

  - 컴포넌트를 사용할 때 [name]="<expression>"  [name] 부분을 target이라 하고 <expression> 부분을 source라고 한다. 

  - []을 사용할 때 source는 항상 template expression 즉, 식이어야 한다. (statement "문"은 이벤트에서 사용함) 

  - target에 []가 있으면 source는 항상 expression으로 수행된다.

import { Component, Input } from 'angular2/core';


@Component({

  selector: 'rangle-label',

  template: '<label class="rangle-label">{{ name }}</label>',

  styles: [ `

    .rangle-label {

      color: #422D3F;

      display: block;

      font-weight: bold;

      letter-spacing: .2em;

      text-transform: uppercase;

    }

  `]

})

export class RangleLabel {

  @Input() private name: string;

}


예) 부모 컨테이너에서 사용할 때: 부모 컨테이너 컴포넌트의 name 속성을 자식 컴포넌트인 rangle-label에 내려보낸다.  

     <rangle-label [name]="name"></rangle-label>


다음으로 Input 컴포넌트를 만든다. value 속성에 대해 ngModel처럼 양방향 바인딩을 주기 위해서는 컨벤션(Convention)처럼 Input은 value이고, Output은 valueChange이어야 한다. 

  - [()] 표식은 banana in the box 표현으로 양방향 데이터 바인딩이다. 

  - [] 은 Input으로 parent -> child로 속성값을 내려 보낼 수 있다. (top down)

  - () 은 Output으로 child -> parent로 값을 올려 보낼 수 있다. (bubble up)

import { Component, Input, Output, EventEmitter } from 'angular2/core';


@Component({

  selector: 'rangle-text-field',

  template: `

    <input class="rangle-text-field"

      [placeholder]="placeholder"

      #field (keyup)="handleKeyup(field.value)">

  `,

  styles: [ `

    .rangle-text-field {

      border-radius: 3px;

      border: 1px solid #ccc;

      box-sizing: border-box;

      display: inline-block;

      font-size: inherit;

      font-weight: inherit;

      height: 2.5rem;

      padding: .5rem;

    }

  `]

})

export class RangleTextField {

  @Input() private placeholder: string;

  @Input() private value: String;

  @Output() private valueChange = new EventEmitter<String>();


  handleKeyup(fieldValue: string): void {

    this.valueChange.emit(fieldValue);

  }

}


예) 부모 컨테이너에서 사용할 때 searchTerm은 부모 컨테이너 컴포넌트의 속성이다. value를 내려보내기도 하고, 이벤트를 받기도 한다. 

      <rangle-text-field 

        placeholder="Enter Keyword"

        [(value)]="searchTerm">

      </rangle-text-field>


버튼을 만들어 보자. 템플릿의 target=source 에서 target이 ()로 감싸지면 이벤트를 나타내고 source는 statement "문"을 쓴다. expression과 statement에 대한 이해는 Angular.io의 Template Syntax 설명을 참조한다.  여기서는 클릭이 되면 바로 부모의 handleSearch가 호출된다. 

import { Component, Input, Output, EventEmitter } from 'angular2/core'


@Component({

  selector: 'rangle-button',

  template: `

    <button

      [ngClass]="dynamicStyles()"

      class="rangle-button"

      (click)="onClick.emit()">

      {{ name }}

    </button>

  `,

  styles: [ `

    :host {

      display: flex;

      align-items: stretch;

    }

    .rangle-button {

      border: none;

      border-radius: 3px;

      color: white;

      font-weight: bold;

      letter-spacing: .2em;

      margin-left: 0.5rem;

      padding: 0.5rem;

      text-transform: uppercase;

    }

    .primary {

      background: #E5373A;

    }

    .normal {

      background: #422D3F;

    }

  `]

})

export class RangleButton {

  @Input() name: string;

  @Input() isPrimary: boolean;

  @Output() onClick = new EventEmitter();


  dynamicStyles() {

    return this.isPrimary ? 'primary' : 'normal';

  }

}


예) 부모 컨테이너 컴포넌트에서 사용할 때, handleSearch는 부모 컨테이너 컴포넌트의 메소드이다. 

      <rangle-button name="Search"

           [isPrimary]="true"

           (click)="handleSearch(searchTerm)">

      </rangle-button>


다음으로 Input 과 Button 컴포넌트를 재사용할 수 있는 컨테이너를 만들어 본다. Label만을 포함하고 있고 언제는 원하는 형태로 Input과 Button을 템플릿상에 포함 시킬 수 있다. 이를 위해 ng-content 태그를 사용한다. Angular v1의 transclude와 유사하다. 하지만 큰 차이점은 역시 Angular2의 컴포넌트 아키텍쳐는 Comopnent Tree이기 때문에 Change Detection(CD)는 항시 가장 위에서 아래로 점검되고 렌더링된다. Angular v1은 parent - child 관계가 있다하더라도 Parent가 먼저 렌더링 될지 Child가 먼저될지 보장을 못했다. (물론 Directive에 priority 옵션이 있지만 적용하기에 따라 순서가 정확한지 알 수 없었다.) 즉, Angular2에서 ng-content 태그를 사용해도 렌더링에 예측이 가능하다. 





import { Component, Input } from 'angular2/core';

import { RangleLabel } from './rangle-label';


@Component({

  selector: 'rangle-bar',

  directives: [ RangleLabel ],

  template: `

    <rangle-label [name]="name">

    </rangle-label>

    <div class="row">

      <ng-content></ng-content>

    </div>

  `,

  styles: [`

    :host {

      background: #F8F8F8;

      border: solid #ccc 1px;

      display: block;

      padding: 1rem;

      margin: 1rem;

    }

    .row {

      display: flex;

      margin-top: 0.5rem;

    }

  `]

})

export class RangleBar {

  @Input() name: string;

}


예) 부모 컨테이너 컴포넌트 사용 때 

    input과 button이 있는 경우 
    <rangle-bar name="Search the site">
        <rangle-text-field placeholder="Enter Keyword"
             [(value)]="searchTerm">
        </rangle-text-field>
        <rangle-button name="Search"
            [isPrimary]="true"
            (click)="handleSearch(searchTerm)">
        </rangle-button>
    </rangle-bar>

    또는 
   
   button만 있는 경우 
   <rangle-bar name="Other Stuff">
        <rangle-button name="Button 1"
            [isPrimary]="true">
        </rangle-button>
        <rangle-button name="Button 2"></rangle-button>
        <rangle-button name="Button 3"></rangle-button>
    </rangle-bar>


당연히 <ng-content>에는 input, button 컴포넌트외에 다양한 컴포넌트가 올 수 있다. 결과는 다음과 같다. 



실데이터를 요청하는 코드는 RangleBar 에서 XHR에 대한 요청을 하고 결과를 전달하는 과정을 거치면 될 된다. Redux를 사용한다면 Action Creator를 수행한다. 애플리케이션 소스를 보자. App 컴포넌트는 가장 최상위 애플리케이션으로 Container 컴포넌트와 Presentational 컴포넌트를 사용해 화면 레이아웃을 만들고 있다. 

import { ViewEncapsulation, Component } from 'angular2/core';

import { RangleBar }  from './rangle-bar';

import { RangleButton } from './rangle-button';

import { RangleTextField } from './rangle-text-field';


@Component({

  selector: 'app',

  directives: [ RangleBar, RangleTextField, RangleButton ],

  template: `

    <rangle-bar name="Search the site">

      <rangle-text-field placeholder="Enter Keyword"

        [(value)]="searchTerm">

      </rangle-text-field>

      <rangle-button name="Search"

        [isPrimary]="true"

        (click)="handleSearch(searchTerm)">

      </rangle-button>

    </rangle-bar>


    <rangle-bar name="Other Stuff">

      <rangle-button name="Button 1"

        [isPrimary]="true">

      </rangle-button>

      <rangle-button name="Button 2"></rangle-button>

      <rangle-button name="Button 3"></rangle-button>

    </rangle-bar>

    <p> inside viewCapsulation </p>

  `,

  styles: [ 'p { background: red; border: dashed yellow 2px; }' ],

  encapsulation: ViewEncapsulation.Native

})

export class App {

  private searchTerm: string;


  handleSearch(searchTerm: string): void {

    alert(`You searched for '${searchTerm}'`);

  }

}


GistRun의 소스와 Rangle.io 동영상을 참조하자.

  - Container & Presentational Component 개발 방식에 대한 소개 

  - Component LifeCycle에 대한 소개: ng-content를 특별히 View Content라 하고, 그외 템플릿에 직접 설정된 컴포넌트를 View Child라 한다. Life Cycle에서는 둘의 시점이 나뉘어져 있다. View Content가 먼저 호출된다.  

  - ElementRef 주입받아 컴포넌트의 DOM을 사용할 수 있다.





<참조>

  

  - Angular2 Component Basic (pptsrc)- Rangle.io

  - Thinking in React

  - Presentational and Container Component - Dan Abramov

  - Rangle.io's Angular2 GitBook 

  - Change Detection Reinvented - Victor Savkin's

신고
posted by peter yun 윤영식
2016.04.16 22:27 Angular/Concept

ExtJSAngular2의 기술적 차이점에 대한 질문을 받게되어 어떻게 대답을 하면 될까? 를 고민하다 정리를 해본다. ExtJS 는 일부 기능은 무료 오픈소스이지만 전체 기능을 사용하기 위해서는 유료이다. 현재 v6 까지 나와있다. 또한 최신버전은 Sencha라는 이름으로 통합되어 나오고, 현재 ExtJS v6 버전을 다운로드 받아 사용할 수도 있다. 따라서 ExtJS나 Sencha 에서 이야기하는 장점이라 말하는 부분을 토대로 Angular v2와 비교해 보기로 한다. 참조는 Top 8 Reasons Why Enterprises Prefer Sencha ExtJS over Angular 문서를 보고 ExtJS 쪽은 영문 요약을 간단히 했고, Angular 쪽은 영어로 작성해 본다.


This document is to write differences between ExtJS v6  framework and Angular v2 framework. I reference the ExtJS site (Top 8 Reasons Why Enterprises Prefer Sencha ExtJS over Angular) and I write my opinion about Angular.

*caution: AngularJS is Angular v1, Angular v2 or Angular is Angular v2. ExtJS is ExtJS v6.





Reason #1 Components, Components, Components


 - ExtJS

   + ExtJS contains components that are optimized for both desktop and mobile devices.

   + customize the theme. mobile apps can be themed to achieve an iOS, Android,  BlackBerry the look and feel.

   + Angular does not come with a component library.


 - Angular v2

  + Angular can easily make components that are usable anytime. If we want to use components i.e grids and charts, we can use the components that have already been made by many companies and open source developers.

  + Developer can build an application with components  and can also use the current web technology such as Web Components together.

  + Angular component architecture is a component-tree for improving performance to render web page views. This architecture is implemented in the ReactJS Framework from Facebook as well.

  + Angular can easily merge with the more popular CSS Framework such as Twitter Bootstrap, Material Design, Semantic UI and Foundation which can be customized to match with any company brand. One of these can be chosen for any brand style.

  + Angular is a platform for the web and mobile. Ionic v2 is a Hybrid mobile app framework. It is based on Angular v2 and has tools that can rapidly develop an app. So, the hybrid app can be developed using Angular in a friendly code style. The hybrid app gives customers a better UX(User eXperience) than mobile browsing.

  + Ionic v2 using Angular has lots of mobile components to support the look and feel for different mobile platforms such as iOS, Android and BlackBerry. We can also customize these styles.





Reason #2 ExtJS Has Ben Battle-Tested Since 2007


 - ExtJS

   + It is launched in 2007.

   + It has long-term success.

   + migration v5 to v6 smoothly


 - Angular v2

   + Angular v1 was launched on October 20, 2010 by Google.

   + Angular v1 also has had long-term success i.e NBC, Intel, ABC News and approximately 8,400 other sites out of 1 million tested in July 2015.

   + Angular v2 already has an upgrade tool to migrate v1 to v2.






Reason #3 A Robust Framework for Building Apps vs Building One's Own Framework


 - ExtJS

   + ExtJS has MVC(Model View Controller)/MVVM (Model View ViewModel) architecture.

   + ExtJS comes with an extensive component library.


 - Angular v2

   + Angular has MVC/MVVM as well.

   + Angular is easily customized for any application's architecture. It can be applied to the one-way data flow architecture like Flux from Facebook in order to easily manage any application's state.

   + Angular is an open source framework so, many developers and companies open their components like grids or charts that have been used within their applications. If there are  components with any issues, many contributors can solve the problems in the open system and components can easily be downloaded from consistency tools such as NPM (Node Package Manager) or Bower.

   + Angular v2 is a new platform for the modern web environment including Web Components, Reactive Programming and Immutable state to solve some issues which Angular v1 experienced. Applications can be developed to be more stable with better performance using these concepts.





Reason #4 Clearly-Defined Legacy Browser Compatibility By Default


 - ExtJS

   + support Internet Explorer (IE) 8


 - Angular v2

   + Angular can support IE 9 (link).

   + If IE 8 is to be supported, poly-fill can be used to support the lower version but MicroSoft has already stopped updating IE 8 securities. So, this version is not recommended because of the danger of hacking.





Reason #5 Integrated Tools Created With a Clear Vision and Purpose


 - ExtJS

   + Senchar has several tools including Senchar Cmd, IDE Plugins, and Senchar Inspector that developers can use to speed up their application development.


 - Angular v2

   + Angular uses the Gulp or Grunt tasker that can extend plugins which are required.

   + There are IDE Plugins (WebStorm, Atom, Sublime Text, MS Code) for Angular  and developers can check out an application's performance with Angular Inspector under the  chrome extension as well.

   + Angular uses the NPM (Node Package Manager) or Bower that all front-end developers use to manage the packages and components for any application.

   + Angular is oriented to use common tooling set in current front-end environments. So, the developer can use these tools in a friendlier way.





Reason #6 No Need to Learn TypeScript or other tools and component libraries with ExtJS6


 - ExtJS

   + ExtJS 6 is a JavaScript Framework.

   + Developer doesn't need to have experience with object-oriented programming (OOP)


 - Angular v2

   + Javascript before ECMAScript 5 doesn't support the OOP but ECMAScript 6 (ES2015) can support it and the developer can simply establish more complex applications with the OOP methodology.

   + TypeScript is a superset of ES2015 and has more features simply to code any sophisticated application such as Decorator. Typescript also includes a feature that transpiles code to ES5 or ES3 code, so it can be released into the production environment without worry.

   + TypeScript can support the Static Type Checking System during the development period. TypeScript can automatically suggest some problems in code so, the developer can recover the mistakes which occured in run-time.

   + If the developer has experience with Java or OOP, they can easily learn TypeScript. It is not a weakness, but rather a strength.





Reason #7 Excellent Design Tools


 - ExtJS

   + Sencha has an excellent set of design tools.

   + Sencha Architect is a perfect tool to create clickable prototypes.

   + ExtJS Stencils can be used to create wireframes and mockups that correctly reflect the look and feel of components


 - Angular v2

   + Angular doesn't have a design tool because it is optional. If it is needed, other professional design or mockup tools can be used .

   + Ionic v2 based on Angular v2 also has  a design tool such as Ionic Creator for mobile and has a testable native app in several mobile platforms (iOS, Android) such as Ionic Viewer.

   + The Angular team has been working closely together with the Ionic team.





Reason #8 Awesome Support and Training Options From the Creators of the Framework


 - ExtJS

   + Sencha has excellent support and training teams.

   + Sencha has a professional services team that can assist with a variety of enterprise development needs.


 - Angular v2

   + There are several professional Angular training companies such as Rangle.io and Egghead.io.

   + Global conferences are always held several times annually such as NG-Conf.

   + If developers want to learn Angular, they can easily get a huge range of materials like books, videos and online courses.

   + The Angular team at Google consists of 20 full-time developers and there are over 221 contributors for Angular v2.

   + It is not a level playing field when comparing ExtJS with Angular in my opinion.



ExtJS is a good framework and has a full set of tools for web development but it can restrict customization needs, because developers can just play around with ExtJS's environment and architecture. The current web trend - software and hardware - has been changing quickly so if some applications need to be adapted into the current environment, Angular v2 would be an excellent choice. Angular v2 can provide any company lots of opportunities to improve the UX and performance for applications but more time needs to be invested learning this framework. 


기회가 된다면 다음부터는 영어 블로그도 써볼 생각이다. 그리고 영어 Angular 관련 책을 출판해 보고 싶다. O'Reilly 또는 Packt Publishing 같은 곳과 연이 된다면... 그전에 열심히 영문 블로깅하면서 영작 실력을 키워야겠지만... 비오는 토요일 밤에...




Reference

  - Top 8 Reasons Why Enterprises Prefer Sencha Ext JS over Angular

신고
posted by peter yun 윤영식
2016.01.15 08:16 Angular/Concept

디자인 가이드는 초기 버전의 생각이므로 현재(2015.1.15) beta 버전의 구문 형식과 틀릴 수도 있다는 것을 숙지하고 어떤 사상을 가지고 있는지 알아보는 것에 집중하자. 물고기가 아니라 물고기 잡는 그물에 집중하자는 이야기이다. 




Expressions


Angular의 expression은 Angular1의 expression 과 유사하고 몇가지 더 추가되었다. 


  - Dirty Checking과 긴밀히 통합되어 expression의 prefix 또는 constant 부분에 대한 최적화를 수행한다. 

    좀 더 자세한 사항은 dirty checking 디자인 가이드를 참조한다. 

  - One time binding을 {{::foo}} 와 같이 최초 한번만 바인딩되고 이후 바뀌지 않도록 한다. Angular1에서 사용하던 방법




Directive API


Directive는 ES6 classes를 사용해 정의하고, Annotation을 사용해 meta data를 정의한다.



Class annotation


모든 Directive는 @DecoratorDirective, @TemplateDirective, @ComponentDirective 중 하나의 annotation을 갖는 ES6 class 이다. Annotations는 일반적인 properties를 갖는다. 


  - selector: directive가 적용될 element를 찾을 css selector이다. (string)

  - events: directive가 template안에서 on-.. attribute를 사용해 발생시키는 이벤트 목록이다. (list of string)

  - visibility: element의 subtree안의 directives들이 directive 인스턴스에 접근할 수 있도록 정의한다. ['local' | 'direct-children' | 'any-children']

  - microsyntax: 특별한 properties 구문 형식과 이 properties가 다른 properties와 어떻게 맵핑하는지 정의한다 (string)


아래 예를 보면 NgShowDirective 생성을 통해 element에 ng-show attribute를 사용할 수 있게 한다. 즉, annotation은 툴이 element안에 directive를 인스턴스화하는 과정없이 그리고 코드를 실행하지 않고도 directives를 찾게한다.

import {ng} from '...';


@ng.DecoratorDirective(selector: '[ng-show]')


class NgShowDirective {}



Constructor 에 Denpendency Injection 하기


모든 directives는 Dependency Injection을 통해 contructor의 parameters 값을 갖는다. 즉, DI를 통해 다른 오브젝트 인스턴스틀 갖질 수 있는 것이다. 아래 예를 보면 @Inject annotation을 사용해 DI하고 있다. 

import {ng} from '...';


@ng.DecoratorDirective(selector: '[ng-show]')


class NgShowDirective {

  @Inject(window.HTMLElement, ng.Http, ng.SomeOtherDirective)

  constructor(element, http, someOtherDirective) { ... }

}



Directive communication


http 갖은 것은 ui 오브젝트가 아니기 때문에 element의 subtree안의 모든 directives에 접근할 수 있도록 annotation에 visibility 을 사용할 수 있다. 예로 input element에서 user.value에 대해 NgInputDirective는 input 값의 변경을 Listening하고, NgModelDirective가 validation하며, NgFormDirective은 state를 유지하는 작업을 한다. 여기서 NgModelDirective는 <form> element에 있는 NgFormDirective에 접근할 필요가 있고, NgInputDirective는 같은 element에 있는 NgModelDirective에 접근할 필요가 있다. 이를 아래와 같이 visibility를 설정한다. 

<form>

  <input type="text" ng-model="user.value" required>

</form>


import {ng} from '...';


@ng.DecoratorDirective(

  selector: 'form',

  visibility: 'subtree'

)

class NgFormDirective {

  constructor() { … }

}


@ng.DecoratorDirective(

  selector: 'input[type=text]',

  visibility: 'local'

)

class NgModelDirective {

  @Inject(NgFormDirective)

  constructor(ngForm) { … }

}


@ng.DecoratorDirective(selector: 'input[type=text]',)

class NgInputDirective {

  @Inject(NgModelDirective)

  constructor(ngModel) { … }

}



Bindable properties


Directives의 모든 properties는 데이터 바인딩을 사용할 수 있고, 이를 위해 set(write)에 대해 @ng.PropertySet annotation을 통해 환경설정을 한다. minification 후에도 property 명을 보존하고 directive의 properties에 데이터 바인딩을 쉽게 만들기 위함이다. 


  - 구문: @ng.PropertySet({trigger: ['reference' | 'collection' | 'deferred'], domOnly: [false|true] })

  - trigger

     + reference: reference가 바뀌면 reference 안에서 전달 (default)

     + collection: collection의 entry가 바뀌면 collection 안에서 전달 

     + deferred: directive가 수행할 수 있는 컴파일된 expression을 전달

  - domOnly: DOM만 변경하고 watch하고 있는 다른 property는 제외함 (default: false)

  

예 

import {ng} from '...';


@ng.DecoratorDirective {

  selector: 'dialog'

}

class Dialog {

  constructor() {

    this._content = null;

  }

  get content(content) {

    return this._content;

  }

  @ng.PropertySet({domOnly: true})

  set content(content) {...}

}


<dialog content=”{{1+2}}”>



Properties를 위한 Microsyntax


directive가 여러 값을 다룰 필요가 있을 때 microsyntax로 설정을 한다. 예로 ng-repeat을 하면 track-by expression을 해야할 때. 구문은 다음과 같다. 


  - MICROSYNTAX=(VARIABLE|FIXED|OPTIONAL)+

  - VARIABLE=$\w+

  - FIXED=[^$\[\]]+

  - OPTIONAL=\[MICROSYNTAX\]


ng-repeat을 위한 microsyntax 예를 보면 <div>에 ng-repeat-item-name, ng-repeat-collection, ng-repeat-track-by를 사용한다. 

@TemplateDirective(

  selector: '[ng-repeat]'

  microsyntax: {

   'ng-repeat': '$item-name in $collection [track by $track-by]'

  })

class NgRepeat {

  @ng.PropertySet({trigger: 'reference'})

  set ngRepeatItemName() { … }


  @ng.PropertySet({trigger: 'collection'})

  set ngRepeatCollection() { … }


  @ng.PropertySet({trigger: 'deferred'})

  set ngRepeatTrackByFn;

}



<div ng-repeat="item in items track-by item.id">

<div ng-repeat-item-name="item" ng-repeat-collection="items" ng-repeat-track-by="item.id">



Lifecycle hooks 


모든 directives는 아래 특별한 함수를 구현할 수 있다. 이유는 templates이 initialization과 finalization을 알수 있도록 허용하기 위해 lifecycle hook을 둔다. 


  - attach: templates 안에 바인딩이 된 후 한번만 호출된다. 

  - detach: directive의 element가 destroy될 때 호출된다. 


Component directive는 특별히 아래 함수도 구현할 수 있다. 


  - templateLoaded: component의 template이 로딩되어 component element에 포함될 때 호출된다. 



Events


Directives는 HTMLElement.dispatchEvent를 사용해 사용자 정의 events를 발생시킬 수 있고, HTMLElement.addEventListener를 통해 이벤트를 Listening할 수 있다. 이벤트는 tree를 타고 bubble up 된다. 설정은 아래 예와 같이 class annotation 안에 하고, event가 발생하면 dirty checking 스케쥴이 돌고 template안에 on-... attributes 를 validation한다. 

 @ng.DecoratorDirective(

  selector: 'dialog',

  events: ['close']

)

class Dialog {

  @Inject(window.HTMLElement)

  constructor(element) { ... }

  close() {

    var evt = new Event('close');

    this.element.dispatchEvent(evt);

    if (!evt.defaultPrevented()) {

      // really close the dialog...

    }

  }

}




Directive 구현 예


Decorator: ng-show

import {ng} from '...';


@ng.DecoratorDirective(

  selector: '[ng-show]'  

)

class NgShow {

  @Inject(window.HTMLElement)

  constructor(element) {

    this.element = element;

  }


  set ngShow(value) {

    this.element.style.display = value ? 'block' : 'hidden';

  }

}


Template Directive: ng-if

template 태그의 content를 사용하는 간단한 template directive 이다. 

import {ng} from '...';


@ng.TemplateDirective(

  selector: '[ng-if]'  

)

class NgIf {

  @Inject(ng.ViewHole, ng.ViewFactory, Injector)

  constructor(viewHole, viewFactory, injector) {

    this.viewFactory = viewFactory;

    this.viewHole = viewHole;

    this.injector = injector;

    this.view = null; 

  }


  set ngIf(value) {

    if (this.view) {

      this.viewHole.remove(this.view);

    }

    if (value) {

      this.view = this.viewFactory(injector);

      this.viewHole.add(this.view);

    }

  }

}


Template Direcive: ng-include

import {ng} from '...';


@ng.TemplateDirective(

  selector: '[ng-include]'

)

class NgInclude {

  @Inject(ng.ViewHole, ng.Compiler, ng.Http, ng.Directives,

          Injector)

  constructor(viewHole, compiler, http, directives, injector) {

    this.viewHole = viewHole;

    this.compiler = compiler;

    this.directives = directives;

    this.injector = injector;

    this.view = null;

    this.$http = http;

  }


  set ngInclude(value) {

    var self = this;

    this.$http(value).always(removeView).then(addView);


    function removeView() {

      if (self.view) {

        self.viewHole.remove(self.view);

        self.view = null;

      }

    }

    function addView(templateString) {

      var viewFactory = 

            this.compiler(templateString, this.directives);

      this.view = viewFactory(injector);

      this.viewHole.add(this.view);

    }

  }

}


Templat directive: ng-repeat

template directive는 새로운 execution context를 제공할 수 있고, 아래 간단한 예에서 variable을 injector안에 있는 현재 execution context에서 검증한다. 

import {ng} from '...';


@ng.TemplateDirective(

  selector: '[ng-repeat]'

  microsyntax: {

   'ng-repeat': '$item-name in $collection [track by $track-by]'

  })

class NgRepeat {

  @Inject(ng.ViewHole, ng.ViewFactory, Injector injector)

  constructor(viewHole, viewFactory, injector) {

    this.viewHole = viewHole;

    this.viewFactory = viewFactory;

    this.injector = injector;

    this.views = [];

  }

  @ng.PropertySet({trigger: 'reference'})

  set ngRepeatItemName(value) { 

    this.itemName = value; 

    this.update(); 

  }

  @ng.PropertySet({trigger: 'collection'})

  set ngRepeatCollection(arrayChangeRecord:ArrayChangeRecord) { 

    this.arrayChangeRecord = arrayChangeRecord;

    this.update(); 

  }

  @ng.PropertySet({trigger: 'deferred'})

  set ngRepeatTrackByFn(value) { 

    this.trackByFn = value; 

    this.update();

  }

  update() {

    …

    // deleting a view    

    this.viewHole.remove(deletedView);

    …

    // creating a new view

    var childContext = {

         $index: index,

         $odd: !!index%2,

         $even: !index%2 

    };

    childContext[this.itemName] = this.collection[index];

    var view = this.viewFactory(injector, childContext);

    viewHole.append(view);

   }

   ...

  }

}


Component Directive: pane

title이 있는 pane 구현 예

import {ng} from '...';


@ng.ComponentDirective(

  select: 'pane',

  template: ng.inline('<div>{{title}}</div><content/>'),

  css: ng.url('pane.css'),

)

class sample.Pane {

  constructor() {}

  set title() { … }

}




다른 Web Component 프레임워크에 component를 publishing하거나 consuming하기 



Custom Elements로 X-Tags, Polymer를 사용하는 프레임워크 


Angular는 Custom element API가 유효한지 찾고 custom element로 컴포넌트를 자동 등록하고, component directive의 properties가 custom element의 인스턴스를 접근할 수 있게 한다. 


  - custom element로 Angular component를 export 할 경우 

    + custom element API가 유효하면 Javascript 애플리케이션에서 Angular component를 DOM element 접근방식으로 event의 listen과 properties를 접근가능케 만든다.

  - Angular 애플리케이션에서 custom element 사용할 경우 

    + Angular는 native DOM element를 사용하듯이 custom elements를 사용할 수 있고, event도 listen할 수 있다.



가이드-1 다시 보기



<참조> 


  - Angular Templating 디장인 가이드 

  - Creating, Triggering Events 모질라 문서 

신고
posted by peter yun 윤영식
2016.01.14 08:23 Angular/Concept

이번에는 Angular2 Templating에 대한 Design Guide를 살펴본다. Angular1에서 Angular2로 오면서 살아남은 명칭중 하나는 Directive(지시자)이다. 지시자는 Angular 프레임워크에서 컴포넌트의 지위를 얻을 수 있는 방법을 제공했으나, Scope에 대한 격리 옵션과 모델들의 @/&/= 옵션, ControllerAs 등 문법적으로 알고 가야할 사항이 많아서 처음 접근하는 개발자에게 상당한 Learning Curve가 존재한다.




친숙함과 간결함 사이에서 (Familiarity vs Simplicity)


친숙함은 달콤하다. 지금 잘 알고 있는 것을 가지고 현재를 편하게 즐길 수 있도록 해준다. 하지만 변화에 둔하고 러닝커브가 높을 경우 신규로 알아야 할 사람들에게 많은 시간을 투자하게 만든다. 친숙함이 정말 친숙한 것인지 한번 더 돌아보고 주위 사람들이 왜(Why) 어려워 하고 어떻게(How)하면 쉽고 빠르게 익히고 널리 사용할 수 있도록 무엇(What)으로 만들 수 있을지 기술 리더는 고민해 보아야 한다. 


간결함은 하는 역할이 명확하여 쉽게 이해한다. 배움의 시간을 단축시켜주고 즐거움을 줄 수 있다. 그러나 복잡함을 간결하게 표현하기 위해서는 함축적이다. 따라서 그 내면에 있는 내용을 이해하고 쓰면 더 많은 생각들을 읽을 수 있다. 따라서 간결함을 왜 어떻게 무엇으로 만들었는지 알아 보는 것이 중요하겠다. 그 간결함을 쉽게 설명할 수 있다면... Angular2 Templating은 간결함의 시작중 하나라 본다. 


* 주의) 글에서 Angular 또는 Angular2는 Angular version 2를 지칭하고, AngularJS 또는 Angular1는 Angular version 1를 가르킨다. 


 

Templating 개념 둘러보기


Angular는 DOM element와 행위에 대한 것을 연관(연결) 한다. 여기서 행위(Behavior)를 Directive라고 부른다. 행위에 대한 부분이기 때문에 Directive는 CSS Selector와 맵핑되는 DOM element를 위해 인스터스화된 자바스크립이다. DOM element는 애플리케이션과 상호작용하기 위해 properties와 events를 사용하는데, Directive도 DOM element의 properties와 events의 동일 인터페이스를 사용한다. 


Angular 애플리케이션이 사용자 인터페이스는 "Component Directive"를 사용해 구성한다. Component Directive는 애플리케이션의 Data와 Logic을 저장하고, 또한 사용자 인터페이스를 정의하는 html template도 가진다. 컴포넌트 안에서 element와 상호작용하는 컴포넌트는 Decorator Directive이다. 보통 ng-show="false"와 같이 element의 attribute로 동작하고 엘러먼트에 여러개가 놓일 수 있다. 


Angular는 양방향 데이터 바인딩을 사용한다. 데이터 바인딩은 template안에 특별한 html attribute를 이용해 설정한다. 이때 바인딩은 expression이라고 부르는 것을 이용해 정의한다. expression은 함수(Function)를 호출하거나 데이터에 접근하는 것으로 모든 expression은 execution context안에서 실행된다. 이런 작용은 Dependency Injection Container 안에서 아래와 같이 수행된다.





Directive Type


지시자의 종류 목록


Provides execution context for expressions

Provides a hole in the DOM to insert template instances

Isolates the DOM/expressions/css for reuse

Decorator Directive

no

no

no

Template Directive

can create child execution context, don't have to.

yes

no

Component Directive

isolated execution context, always.

no

yes


Directive에 대한 일반 제약조건은 Directive는 DOM 구조를 제거(remove)/변경(change) 해서는 안된다. Template Directive와 Component Directvie는 별도의 Execution Context를 가질 수 있다. Angular에서 template directive가 execution context를 갖는 것에 유념하자.



Decorator Directive


새로운 element를 만들고 해당 element에서 뭐든 할 수 있다. 그러나 만들지 않은 것은 다른 element는 attribute 변경만 가능하다.



Template Directive


원래 있던 element/template 영역의 특정 부분에 template 인스턴스를 넣을 수 있고, template directive는 자식(child) execution context를 생성한다. 예로 ng-repeat는 row 마다 execution context를 만들지만 ng-if는 부모(parent) execution context를 재사용한다. Template Directive 종류로는 ng-if, ng-repeat, ng-view, ng-switch, ng-include 가 있다. 일반적인 문법을 보자 

<template ng-repeat>

  <div> ... </div>

</template>


template element 자체는 제거되고 template의 일부가 되지 않는다. 즉, template directive는 <template> element만을 허용한다. 중첩으로 아래와 같이 사용할 수도 있다. 

<template ng-repeat>

  <template ng-if>

    <div>...</div>

  </template>

</template>

    

template directives를 사용하면 element에 아래와 같이 template로 변경된다. 

<ul>

  <li ng-repeat>

    <span ng-if> ... </span>

  </li>

</ul>

은 아래 내용과 완전 동일하다

<ul>

  <template ng-repeat>

    <li>

      <template ng-if>

        <span> ... </span>

      </template>

    </li>

  </template>

</ul>


위이 template 태그는 Angular1의 directive의 compile/link 과정과 동일하다. 즉, Angular2에서는 compile/link과정을 template directive로 대체하는 것이다. 


    // Angular1의 이런 구문이 사라지는 것이다. 

    return {  

           ....

            templateUrl: 'plugins/taskers/wafer-traffic/wafer-traffic.html',

            replace: true,

            link: link,

            controller: ctrl,  

            ....

        };

    function link() {}

    function ctrl() {}



Component Directive 


javascript 로직, html template 과 옵션으로 css style을 컴포넌트에 가지고 있다. 이를 위해서 template안의 expressions을 격리(isolate)한다. - execution context의 격리: component directive 인스턴스가 template안의 expresssions를 위한 새로운 execution context가 되는 것이다. 즉, template안의 expressions은 component directive 인스턴스의 함수(function) 또는 properties만 접근 가능하다는 것이다. 이것은 Angular1의 directive 정의시 scope: {...} 격리하는 방법과 controllerAs를 사용해서 this로 접근하는 것등을 개발자가 정의할 필요없이 Component directive로 통일한 것으로 보인다. 프로젝트를 진행하다보면 거의 대부분 scope: { model: '=' } 과 controllerAs를 default로 놓고 사용하기 때문에 프레임워크 단에서 simple화 한 것은 환영할만 하다. 


  // Angular1의 이런 구문이 사라지는 것이다. 

  return {

            restrict: 'EA',

            scope: {

              model: '='

            },

            controller: trafficCtrl,

            controllerAs: 'traffic',

            bindToController: true

        };


- DOM 과 CSS의 격리: Component directives는 다른 directive에서 컨텐츠 변경을 못하도록 Shadow DOM을 사용한다. 따라서 Shadow DOM안에서의 element events는 외부 element로 전파되지 않는다. (buble out of component directive) 

   + Properties와 events: Template은 component의 properties들과 데이터 바인딩 되고, 이벤트는 상위 컴포넌트로 전파할 수 있다. 

   + CSS properties: 템플릿은 Shadow DOM을 기본으로 사용하는 컴포넌트에 한해 스타일을 변경할 수 있다. 

   + Child fragment: 템플릿은 Shadow DOM의 <content> 태그를 사용하는 템플릿을 넣는 자식 elements를 제공한다. 자식 elements의 expression은 밖의 템플릿 execution context와 계속 연결된다. 


expression을 위한 Execution Context는 클래스 인스턴스이고 이를 통해 expression에 대한 type checks등 다양한 일을 수행할 수 있다. 



Directives를 인스턴스화 하는 순서 


자식 elements전에 부모 element의 directives가 먼저 인스턴스화 하고, 만약 template directive가 존재하면 이것을 인스턴스화 하고, 다음으로 element의 decorator directive를 인스턴스화 한다. 순서는 directive 생성자의 dependency injection 순서에 의해 결정된다. 그리고 나서 최종 component directive가 인스턴스화 한다. 즉, 다음과 같다. 


>> Parent Element 

     template directive --> decorator directives --> component directive

         >> Child Elements

                template directive --> decorator directives --> component directive




templates에 데이터 바인딩 설정 방법


elements 또는 directives의 events 와 바인딩 하기


형식: @on-[event name]="[expression]" 

        예) <button on-click="doSomething()">

의미: click 이벤트가 발생하면 doSomething을 호출한다. 

DOM events를 기반으로 하는 thrid party 이벤트 시스템을 바인딩 할 수 있고, Directives에 새로운 이벤트를 정의해도 listener를 달 필요가 없다.



elements 의 properties 와 바인딩 하기


형식: @bind-[property name]="expresion" 

         예) <input bind-value="user.name">

의미: expression인 user.name에 대한 값 변경을 감시하다가 element property값을 갱신 한다. 


만일 element의 property가 바뀌었는데 expression은 writable하지 않거나, expression의 값이 바뀌었는데 property가 writable하지 않으면 에러가 발생한다. 양방향 데이터 바인딩은 directive와 element property 사이에 일어나기 때문이다. element의 attribute로 하지 않고 property로 바인딩 하는 이유는 다음과 같다. 


  - 거의 대부분의 attribute는 대응하는 property를 가진다. 만일 대응하는 property가 없으면 attribute가 대신한다. 

  - element properties는 항시 현재 값을 가진다. 그러나 attributes는 때때로 초기값을 지정해 주어야 한다. 예) <input>의 value attribute.

  - native attributes는 브라우저가 알아서 자동으로 대응하는 property 값을 바꿔준다. 

    예) <img>의 src property는 src attribute 바뀌면 자동을 변경된다)



elements의 properties 넣기 


형식: @[property name="a {{ [expression] }} b"

        예) <input title="some Text {{someValue}}">

의미: <input bind-title=" 'a ' + someValue + ' b' ">  


{{}} 를 사용하면 항상 string으로 변환하고, 단방향 연결이 된다. Angular1 directive의 @ 과 같다. 



text 넣기


형식: {{ [expression] }}

        예) hello {{user}}

의미: hello <span bind-text-content=" ' ' + user "> 


text node 내역과 데이터 바인딩도 항시 string으로 변환되고, 단방향이다. 



Guide-2 이어보기



<참조>


  - Templating 디자인 가이드 

  - Template Syntax 개발자 가이드 



신고
posted by peter yun 윤영식
prev 1 next

티스토리 툴바