본문 바로가기

개발노트/Polymer

Polymer library Guide

해당 문서는 polymer 공식 사이트의 Polymer Library Guides version 1.0 을 기반으로 부분 번역하여 작성 하였습니다.

목차


Web component 개요

1. 탄생배경

HTML 이 기본으로 제공하는 엘리먼트는 브라우저와 운영체제에 따라 다르게 보이는 경우가 있고, 더 발전하는 웹 환경에 대응하기에 한계가 있습니다. 이런 한계를 JindoJS Component와 같은 Java Script 컴포넌트로 보완하고 있으나, JavaScript 컴포넌트는 사용이 어려울 뿐만 아니라 크기가 커서 실행속도가 느리기 때문에 '자주 사용되거나 구조적 분리가 필요한 요소를 다른 요소들과 충돌하지 않는 재활용 가능한 방법' 에 대한 필요성이 재기되었습니다. 특히 UI 요소들이 많은 프론트엔드 개발에서는 '리소스 관점에서 분리되어 있는 HTML, CSS, 자바스크립트를 하나로 묶어 주는 것' 또한 중요한 기능 중의 하나가 됩니다. 소프트웨어 개발에서는 이러한 요소들을 '컴포넌트(Component)' 라는 개념으로 오랫동안 사용해 왔습니다.

이러한 필요성을 바탕으로 W3C 에서는 이러한 컴포넌트 기술을 웹에서 적용할 수 있도록 새로운 규격의 집합을 만들었으며 이 규격들을 묶어 '웹 컴포넌트(Web Component)'라고 부르게 되었습니다.

2. 웹 컴포넌트 규격

웹 컴포넌트는 다음과 같은 4가지 규격으로 구성되어 있습니다.

  • Custom Element : 웹 문서에서 사용할 엘리먼트의 동적인 등록을 통해 컴포넌트의 명시적인 alias 를 선언
  • HTML Template : 로딩 시간에는 비활성화되는 마크업을 정의하고 이를 샐행 시간에 복제할 수 있는 기능을 제공
  • HTML Imports : 웹 문서 내에 외부 리소스를 포함(Import) 하기 위한 기능을 제공
  • Shadow DOM : 컴포넌트의 DOM, CSS, JS를 감추는 캡슐화(encapsulation)와 외부로부터의 간섭을 제어하는 스코프(Scope)의 분리를 제공

3. Polymer 란?

Polymer는 재사용 가능하고 개발자가 사용하기 쉬운 Web 기반의 구성요소 (custom elements)를 쉽고 빠르게 만들 수 있는 라이브러리 입니다.

4. 웹 컴포넌트와 Polymer

웹 컴포넌트는 새로운 custom elements 를 만드는 데 필요한 기본 기능을 제공합니다. Polymer 라이브러리는 이러한 웹컴포넌트 위에서 동작하며 custom element 를 더 간단하게 정의 할 수있는 구문을 제공합니다.

[목차] [Web component 개요]


Polymer 가이드

1. Register an element

custom element 등록

custom element를 등록하기 위해서는 Polymer () 함수를 사용합니다. 이 함수의 인자는 prototype을 전달하고 이 prototype에 새로 만든 custom element 를 설정합니다. prototype은 is 속성을 항상 명시 해야하고, 이는 custom element 의 HTML 태그 이름입니다.
또한 custom element 이름에 반드시 대시(-)를 포함해야합니다.

my-element.html

<link rel="import" href="../../../bower_components/polymer/polymer.html">

<script>
    // custom element 등록
    MyElement = Polymer ({
        // `is` 에서 지정한 이름이 element의 HTML 태그 이름이 된다.
        is : 'my-element'

    };
</script>

index.html

<!DOCTYPE html>
<html>
<head>
    <script src = "../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
    <link rel="import" href="my-element.html">
</head>
<body>
    <script>
        document .addEventListener ( 'WebComponentsReady' , function  () {
            // createElement에서 custom element의 인스턴스를 생성
            var myEl1 = document .createElement('my-element');

            // custom element의 생성자에서 인스턴스화
            var myEl2 = new MyElement();

            // 추가한 두 개의 element를 body에 추가
            Polymer.dom(document.body).appendChild(myEl1);
            Polymer.dom(document.body).appendChild(myEl2);
        });
    </script>
</body>
</html>

Polymer() 함수는 브라우저에서 custom element 를 등록하고 생성자를 반환합니다. 이 생성자에서 등록한 element의 인스턴스를 만들 수 있습니다.

[목차] [1. Register an element]


Define a custom constructor (custom 생성자 정의)

Polymer() 함수는 custom element 를 인스턴스화 하여 사용할 수 있는 기본 생성자를 반환합니다. 만약 새로 만든 element의 생성자에 인자들을 전달하고자 할때는 prototype 에 factoryImpl() 함수를 명시하여 사용할 수 있습니다.

Polymer() 함수에서 반환된 생성자는 내부에서 document.createElement()를 사용하여 인스턴스를 생성하고 사용자가 지정한 factoryImpl() 함수를 실행합니다.

my-element.html

<link rel="import" href="../../../bower_components/polymer/polymer.html">
<script>
    MyElement = Polymer ({
        is : 'my-element',

        factoryImpl : function (name, age) {
            this.name = name;
            this.age = age;
        },

        attached : function () {
            console.log('이름 = '+this.name+', 나이 = '+age);
        }
    });
</script>

index.html

<script src = "../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
<link rel="import" href="my-element.html">

<body>
<script>
    document.addEventListener ( 'WebComponentsReady' , function  () {
        //custom element 에 인수를 전달할 인스턴스를 생성
        var myEl = new MyElement('wskim', 20);

        // 추가한 element를 body에 추가
        Polymer.dom(document.body).appendChild(myEl);

    });
</script>
</body>

Note :

  • factoryImpl() 함수는 생성자를 사용하여 element를 생성한 경우에만 호출됩니다.
    HTML 태그 이름을 사용하여 마크업으로 생성된 element ( <my-element></my-element> )또는 document.createElement()를 사용하여 생성된 element의 경우 factoryImpl() 함수가 호출되지 않습니다.
  • factoryImpl() 함수는 element가 초기화 된 후 (ready callback 이후) 호출됩니다.
    ready callback에 관한 내용은 라이프사이클 항목을 참조하시기 바랍니다.

[목차] [1. Register an element]


Extend native HTML elements

Polymer 는 현재 기본 HTML 요소의 상속만을 지원하고 있습니다.( <input>, <button> 등)
기본 HTML 요소를 상속하려면 Polymer() 함수의 prototype 속성에 extends를 추가하고 상속하는 element의 태그이름을 지정합니다.

my-input.html

<link rel="import" href="../../../bower_components/polymer/polymer.html">
<script>
    MyInput = Polymer ({
        is : 'my-input',

       //상속하는 기본 element를 지정
       extends : 'input',

       created : function() {
            this.value = 'my-input';
            this.style.border = '1px solid red';
       }
    });
</script>

index.html

<script src = "../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
<link rel="import" href="my-input.html">

<body>
<script>
    document.addEventListener ( 'WebComponentsReady' , function  () {
        //custom element의 생성자에서 인스턴스 생성
        var myInputEl1 = new MyInput();
        console.log(myInputEl1 instanceof HTMLInputElement); // true

        //createElement 에서 custom element 의 인스턴스 생성
        var myInputEl2 = document.createElement('input', 'my-input');
        console.log(myInputEl2 instanceof HTMLInputElement); // true
</script>
</body>

[목차] [1. Register an element]


main HTML document 에 element 정의

Note : test가 목적일 경우에만 main document 에서 element 를 정의 할 것을 권장합니다.

main HTML document 에 element 를 정의 하기 위해서는 HTMLImports.whenReady(callback) 으로 정의합니다. 해당 document의 모든 imports 가 로딩이 완료 되었을때 'callback'이 호출됩니다.

<!DOCTYPE html>
<html>
  <head>
    <script src="bower_components/webcomponentsjs/webcomponents-lite.js"></script>
    <link rel="import" href="bower_components/polymer/polymer.html">
    <title>main document에서 element 정의</title>
  </head>
  <body>
    <dom-module id="main-document-element">
      <template>
        <p>
          안녕! 난 main document에서 정의 된 Polymer element!
        </p>
      </template>
      <script>
        HTMLImports.whenReady(function () {
          Polymer({
            is: 'main-document-element'
          });
        });
      </script>
    </dom-module>
    <main-document-element></main-document-element>
  </body>
</html>

[목차] [1. Register an element]


Lifecycle callbacks

Polymer 의 기본 prototype 은 polymer의 내장 features을 위해 필요한 작업을 수행하는 표준 Custom element lifecycle callbacks 를 구현하고 있습니다.

Callback Description
created element가 생성되었을때 호출됩니다. 하지만 property 값이 셋팅되거나 local DOM이 초기화 되기 이전입니다.
ready property 값이 셋팅되고 local DOM 이 초기화된 이후에 호출됩니다.
attached element가 해당 document에 attached 된 시점에 호출됩니다. 화면생성이 완료된 시점에서 이벤트 처리 시 사용 됩니다.
detached element가 해당 document에서 detached 된 시점에 호출됩니다.
attributeChanged 선언되지 않은 속성에 대해 attribute 값이 변경될때 호출됩니다. (선언된 속성인 경우에는 자동으로 핸들링 하는 함수를 제공합니다.)

my-element.html

MyElement = Polymer({
  is: 'my-element',

    //콘솔에 찍히는 순서 : created -> ready -> attached
  created: function() {
    console.log(this.localName + '#' + this.id + ' was created');
  },

  ready: function() {
    console.log(this.localName + '#' + this.id + ' has local DOM initialized');
  },

  attached: function() {
    console.log(this.localName + '#' + this.id + ' was attached');
  },

  detached: function() {
    console.log(this.localName + '#' + this.id + ' was detached');
  },

    // 선언되지 않은 속성 'name' 에 대해 setAttribute 함수를 통해 값을 할당 하였을 경우 호출
  attributeChanged: function(name, type) {
    console.log(this.localName + '#' + this.id + ' attribute ' + name +
      ' was changed to ' + this.getAttribute(name));
  }
});

[목차] [1. Register an element]


2. Declare properties

Declared properties (속성 선언)

custom element 외부에 속성을 노출하려면 Polymer() 함수의 prototype 인수 properties 객체를 설정합니다.

example

Polymer({
  is: 'custom-element',

  properties: {
    user: String,
    isHappy: Boolean,
    count: {
      type: Number,
      readOnly: true,
      notify: true
    }
  },

  ready: function() {
    this.textContent = '안녕! 난 커스텀 엘리먼트야!';
  }
});
<custom-element user='wskim' is-happy="true">

properties객체에는 다음과 같은 설정을 할 수 있습니다.

KEY DETAILS
type Type : 생성자 (Boolean, Date, Number, String, Array, Object 와 같은 고유 클래스)
value Type : boolean, number, string, function 속성의 기본값을 지정
reflectToAttribute Type : boolean 속성값이 변경되었을 때 해당 element attribute 에 값이 설정 (Reflecting properties to attribtes 항목 참조)
readOnly Type : boolean true 가 설정된 경우 속성을 읽기 전용으로 설정
notify Type : boolean true 가 설정된 경우 two-way 데이터 바인딩 사용 및 속성값이 변경될때 property-name-changed 이벤트 발생 (notify 속성 property change notification 참조)
computed Type : String 메소드 명과 인자로 구현, 지정된 메소드는 인수로 지정된 값이 변경되면 호출 (Computed 속성 항목 참조)
observer Type : String 메소드 명으로 구현, 해당 속성 값이 변경되면 호출 (Property change observers 항목 참조)

[목차] [2. Declare properties]


Property name to attribute name mapping (property 명과 attribute 명의 매핑)

데이터 바인딩을 실현하려면 attribute 에서 property 로 , property 에서 attribute 변환하는 일련의 직렬화(serialization) 과정이 필요합니다.

  • attribue 명은 property명과 매핑 시 소문자화 됩니다. 예를 들어 attribute 명 firstName 은 property 명 firstname 과 매핑됩니다.

  • 대시(-)가 포함된 attribute 명 first-name은 property 명 firstName 과 매핑됩니다.

반대의 경우, 즉 property 명에서 attribute 명으로 변환시 에도 해당 규칙이 적용됩니다. (reflectToAttribute : true 가 설정 되어있는 경우)

custom-element.html

Polymer({
  is: 'custom-element',

  properties: {
    firstName: String,
    lastName: {
      type: String,
      reflectToAttribute: true,
      value: kim
    }
  },

  attached: function() {
    console.log("안녕! 내 이름은 "+this.lastName+" "+this.firstName+"이야.");
  }
});

index.html

<custom-element first-name="emro"></custom-element>

Configuring default property values (기본값 설정)

속성의 기본값은 properties 객체의 value필드를 사용하여 설정합니다. 이 value에는 값 또는 함수를 지정합니다.

만약 함수를 지정하였을 경우 해당 함수의 return 값이 속성의 값입니다.

속성의 형태가 Object 또는 Array 일 경우, value 필드에 함수를 지정하여 객체의 초기화를 권장합니다.

custom-element.html

Polymer({
  is: 'custom-element',

  properties: {
    mode : {
      type : String ,
      value : 'auto'
    } ,
    data : {
      type : Object ,
      notify : true ,
      value : function(){
          return  {} ;
      }
    }
  },
});

[목차] [2. Declare properties]


Property change observers (속성 변경 모니터링)

속성 변경에 대한 모니터링은 properties 객체 observer속성을 지정하여 수행 할 수 있습니다.
observer 속성에 변경할 때 호출되는 함수 이름을 문자열로 지정합니다. 속성이 변경되면 지정된 함수의 인수에 변경된 값과 이전의 값이 전달됩니다.

custom-element.html

<dom-module id ='custom-element'>
...
<script>
Polymer({
  is: 'custom-element',

  properties: {
    user : {
      type : String ,
      //변경 시 호출되는 함수 이름을 문자열로 지정
      observer: '_userChanged'
    }
  },

  //user 속성값 변경 시 호출
  _userChanged : function (newValue, oldValue){
        console.log(oldValue+'에서 '+newValue+'로 변경');
  }
});
</script>
</dom-module>

Objserving changes to multiple properties (여러 속성을 모니터링)

여러 속성값의 변경을 모니터링 하기 위해 observers 배열을 사용합니다.
observersproperties로 정의 되는 observer에 비해 다음과 같은 차이가 있습니다.

  • observers는 관련된 모든 속성이 정의 될때까지 호출되지 않습니다. ( !==undefinded)
  • observers는 인수에 변경된 값만 받고, 이전의 값은 받을 수 없습니다.

observers 는 여러 속성이 변경될때 호출되는 메소드명을 문자열로 지정하고 인수에 변경을 감지할 대상을 지정합니다. (복수 지정 가능)
observers 함수의 인수중 하나만 변경이 되어도 함수가 호출됩니다.

custom-element.html

Polymer({
  is: 'custom-element',

  properties: {
    first : String,
    last : String,
    age : Number
  },

  observers : [
      // 함수의 인수에 모니터링 대상 property 설정
    '_updateUser(first, last, age)'
  ],

  _updateUser : function(first,last,age) {
      // 변경시의 처리를 구현
  }
});

Observing sub-property changes (하위 속성 모니터링)

observers는 객체의 하위 속성을 모니터링 할 수 있습니다. 하위 속성 경로 (user.name 과 같은)를 인수로 지정합니다. observer함수가 호출될때 전달되어 오는 인수는 변경된 하위 속성의 값입니다.

custom-element.html

<dom-module id="custom-element">
  <template>
    <input value="{{user.name}}">
  </template>
  <script>
    Polymer({
      is: 'custom-element',
      properties: {
        user: {
          type: Object,
          value: function() {
            return {};
          }
        }
      },

      observers: [
        //observer 함수에 모니터링대상 하위 속성을 지정
        '_userNameChanged(user.name)'
      ],

      _userNameChanged: function(name) {
        console.log('새 이름 : ' + name);
      },
    });
  </script>
</dom-module>

Note : 하위 속성을 observer 하기 위해서는

  1. 바인딩을 사용하여 하위 속성의 값 변경
  2. Polymer.Base의 set() 메소드를 사용하여 값 변경

Array mutation methods (배열 변경 방법)

Polymer 의 prototype 은 배열을 변경하는 방법을 제공하고 있습니다. 이러한 메소드는 첫 번째 인수가 path 인 것을 제외하고 기본 Array 가 가지고 있는 메소드에 근거하여 만들어져 있습니다. 첫번째 인수 path는 변경된 배열의 아이템을 특정하기 위한 것 입니다.

모든 Polymer element 는 배열을 변경하기 위해 하기와 같은 방법을 사용합니다.

  • push(path, item1, [..., itemN])
  • pop(path)
  • unshift(path, item1, [..., itemN])
  • shift(path)
  • splice(path, index, removeCount, [item1, ..., itemN])

다음은 일부 메소드 사용 예제입니다.

custom-element.html

<dom-module id="custom-element">
  <template>
    <template is="dom-repeat" items="[[users]]">{{item}}</template>
  </template>

  <script>
    Polymer({

      is: 'custom-element',

      addUser: function(user) {
          //배열에 사용자를 추가
        this.push('users', user);
      },

      removeUser: function(user) {
          //배열에 사용자를 제거
        var index = this.users.indexOf(user);
        this.splice('users', index, 1);
      }

    });
  </script>
</dom-module>

[목차] [2. Declare properties]


Read-only properties (읽기 전용 속성)

property에 대한 의도하지 않은 변경을 방지하기 위해 readonly 속성을 부여하여 읽기 전용으로 설정 할 수 있습니다.
설정방법은 properties 객체의 readOnly 값을 true로 설정합니다.
readonly 로 설정된 property의 값을 변경하려면 _setProperty(value)라는 setter를 사용해야만 합니다.

custom-element.html

Polymer({
    is: 'custom-element',

    properties: {
      response: {
        type: Object,
        readOnly: true,
        notify: true
      }
    },

    responseHandler: function(response) {
      this._setResponse(response);
    }
});

[목차] [2. Declare properties]


Computed 속성

Computed 속성은 다른 property 로 부터 산출된 값을 value로 지정하는 속성입니다.

Computed 속성은 properties 객체 computed에 속성 값을 산출하는 함수를 지정합니다.

custom-element.html

<dom-module id="custom-element">

  <template>
    이름 : <span>{{fullName}}</span>
  </template>

  <script>
    Polymer({

      is: 'custom-element',

      properties: {
        first: String,
        last: String,
        fullName: {
          type: String,
          computed: 'computeFullName(first, last)'
        }

      },

      computeFullName: function(first, last) {
        return first + ' ' + last;
      }

    });
  </script>
</dom-module>

Computed 함수는 관련된 모든 속성이 정의 될 때까지 ( !== undefined 때 까지) 호출되지 않습니다.

Note : observes 함수와의 차이점

  1. computed 함수는 값을 리턴
  2. 가상적인 속성으로 외부에 공개
  3. 인수로 설정된 모든 속성값이 정의 될 때 까지 ( !== undefined 때 까지) 호출되지 않는다.

[목차] [2. Declare properties]


Reflecting properties to attributes

custom element 의 attribute 값에 property의 값을 반영하려면 properties 객체에 reflectToAttribute : true를 설정합니다. property 의 값은 해당 속성에 설정되어있는 값의 형식을 바탕으로 일련의 직렬화(serialization)과정을 수행합니다.

  • String : 직렬화 되지 않습니다. (property 값 그대로 적용)
  • Date, Number : toString() 로 직렬화됩니다.
  • Boolean : true의 경우 attribute가 추가됩니다. (false일 경우 나타나지 않습니다.)
  • Array, Object : JSON.stringify() 로 직렬화 됩니다.

custom-element.html

Polymer({
    is: 'custom-element',

    properties: {
      user: {
          type : Object,
        value : {
            id: 'wskim',
            company : 'emro'
        },
        reflectToAttribute: true
      },

      manager:{
          type : Boolean,
        value : true,
        reflectToAttribute : true
      },

      current: {
          type : Date,
        value : function() {
            return new Date();
        },
        reflectToAttribute:true
      }
    },
});
<!--element 결과-->
<custom-element user="{"id":"wskim", "company":"emro"}" manager current= "Thu Aug 27 2016 21:12:52 GMT+0900"></custom-element>

[목차] [2. Declare properties]


3. Behaviors

Behavior 정의

Polymer는 custom element 에서 Behavior라는 모듈을 상속받을 수 있도록 지원합니다.

Behavior는 lifecycle callbacks, properties 선언, observer(observers), listeners 등 Polymer prototype 과 유사하게 사용가능 합니다.

Polymer prototype 이 Behavior를 상속하려면 behaviors 배열에 대사의 Behavior 를 지정합니다.

my-element.html

<link rel="import" href="highlight-behavior.html">

<script>
  Polymer({
    is: 'my-element',
    behaviors: [HighlightBehavior]
  });
</script>

[목차] [3. Behaviors]


Behavior 상속

기존의 Behavior를 상속받는 새로운 Behavior 를 만들 수 있습니다. Behavaor를 상속하려면 behaviors 배열에 대상 Behavior를 지정합니다.

my-element.html

<link rel="import" href="oldbehavior.html">

<script>
  // oldbehavior 를 상속받는 새로운 Behavior 구현
  NewBehaviorImpl = {
      //구현
  }

  // 새로만든 Behavior 정의
  NewBehavior = [ OldBehavior, NewBehaviorImpl ]
</script>

Note : NewBehaviorImpl 에 정의 된 것이 OldBehavior에 정의 된 것보다 우선합니다.

[목차] [3. Behaviors]


4. Local DOM

Local DOM template

Local DOM 을 정의하기 위해서 <dom-module><template>를 사용합니다.
<dom-module>id 속성을 추가하고 이를 Polymer () 함수의 프로토타입 is 인수에 정의합니다.
Polymer는 <template>에 정의 된 내용을 Local DOM 속으로 복제합니다.

local-dom.html

<dom-module id="local-dom">

  <template>안녕! 나는 local-dom 엘리먼트!</template>

  <script>
    Polymer({
      is: 'local-dom'
    });
  </script>

</dom-module>

[목차] [4. local DOM]


Automatic node finding (노드 탐색)

Polymer는 Local DOM에 인스턴스화 된 노드의 정적 맵을 구축하고 이러한 노드에 액세스하기 위한 기능을 제공합니다.
<template> 안의 id가 존재하는 노드에 접근하려면
this.$ 라는 해시에 id명을 지정합니다. (dom-repeat, dom-if 템플릿을 사용하여 동적으로 생성 된 노드는 포함되지 않습니다.)

동적으로 생성 된 노드를 검색하려면 $$ 메소드를 사용 합니다.

`this.$$(selector)`

$$는 local DOM 에서 selector 와 일치하는 첫 번재 노드를 반환합니다.

<dom-module id="em-module">
    <template>
        <div id='div1'>
            <sc-text-field id="text"></sc-text-field>
            <sc-textarea-field id="area01"></sc-textarea-field>
            <sc-textarea-field id="area02"></sc-textarea-field>
        </div>
    </template>
    <script>
        Polymer({
            is : "em-module",             
            ready : function() {
                // id 가 text인 element 에 접근
                this.$.text.value = '안녕! 나는 text-field!';
                // local DOM 내의 첫번째 sc-textarea-field element에 접근
                this.$$(‘sc-textarea-field’).value = "안녕! 나는 textarea-field!";
            }
        });
    </script>
</dom-module>

[목차] [4. local DOM]


DOM API

Polymer는 DOM을 조작 하는 사용자 정의 API를 제공합니다.

Note : 모든 DOM 조작은 표준 DOM API 를 직접사용하는 것이 아니라 Polymer가 제공하는 API를 사용해야 합니다.

제공되는 API메소드 및 속성은 표준 DOM 과 같은 서명을 가집니다 만,
메서드 또는 속성의 반환 값이 노드 목록일경우 NodeList 대신 노드의 Array를 반환합니다.

Polymer는 다음과 같은 메소드와 속성을 제공합니다.

  • Adding and removing children (자식노드의 추가/삭제):
    • Polymer.dom(parent).appendChild(node)
    • Polymer.dom(parent).insertBefor(node, beforeNode)
    • Polymer.dom(parent).removeChild(node)
    • Polymer.dom.flush()

비동기 작업 : 삽입, 추가, 삭제 작업은 상황에 따라 지연처리 될 수 있습니다.
따라서 이러한 작업 직후에 DOM 에서
정보를 검색( offsetHeight, getComputedStyle() 등 ) 시에는 먼저 Polymer.dom.flush() 메소드를 호출하도록 권장됩니다.

  • Parent and child APIs (부모노드 관련 API) :
    • Polymer.dom(parent).childNodes
    • Polymer.dom(parent).children
    • Polymer.dom(node).parentNode
    • Polymer.dom(node).firstChild
    • Polymer.dom(node).lastChild
    • Polymer.dom(node).firstElementChild
    • Polymer.dom(node).lastElementChild
    • Polymer.dom(node).previousSibling
    • Polymer.dom(node).nextSibling
    • Polymer.dom(node).textContent
    • Polymer.dom(node).innerHTML
  • Query selector :
    • Polymer.dom(parent).querySelector(selector)
    • Polymer.dom(parent).querySelectorAll(selector)
  • Content APIs:
    • Polymer.dom(contentElement).getDistributedNodes()
    • Polymer.dom(node).getDestinationInsertionPoints()
  • Node mutation APIs (노드 변경 API):
    • Polymer.dom(node).setAttribute(attribute, value)
    • Polymer.dom(node).removeAttribute(attribute)
    • Polymer.dom(node).classList

[목차] [4. local DOM]


DOM API examples

Adding and removing children

  • Polymer.dom(parent).appendChild(node)
<dom-module id="append-child">
    <template>
        <div id = 'div1'>
            <sc-button on-click="appendChild">click</sc-button>
        </div>
    </template>
    <script>
        Polymer({
            is : "append-child",             
            appendChild : function() {
                var childNode = document.createElement('sc-button');
                childNode.text = "appendChild";
                //div1 element에 자식노드 추가        
                Polymer.dom(this.$.div1).appendChild(childNode);
            }
        });
    </script>
</dom-module>
  • Polymer.dom(parent).insertBefore(node, beforeNode)
<dom-module id="insert-before">
    <template>
        <div id = 'div1'>
            <sc-button on-click="insertBefore">click</sc-button>
            <div id = 'div2'>
                <sc-text-field id="text01" value= 'start'></sc-text-field>
                <sc-text-field id="text02" value= 'end'></sc-text-field>
            </div>
        </div>
    </template>
    <script>
        Polymer({
            is : "insert-before",             
            insertBefore : function() {
                var childNode = document.createElement('sc-button');
                var beforeNode = this.$.text02;
                childNode2.text = "insertBefore";
                //beforeNode 로 지정된 text02 element 이전에 노드 추가
                Polymer.dom(this.$.div2).insertBefore(childNode,beforeNode);
            }
        });
    </script>
</dom-module>
  • Polymer.dom(parent).removeChild(node)
<dom-module id="remove-child">
    <template>
        <div id = 'div1'>
            <sc-button on-click="removeChild">click</sc-button>
            <sc-text-field id="remove" value="remove"></sc-text-field>
        </div>
    </template>
    <script>
        Polymer({
            is : "remove-child",             
            removeChild : function() {
                //div1 내에 선언된 remove element 를 제거
                Polymer.dom(this.$.div1).removeChild(this.$.remove);
            }
        });
    </script>
</dom-module>

Query selector

  • Polymer.dom(parent).querySelector(selector)
  • Polymer.dom(parent).querySelectorAll(selector)
<dom-module id="query-selector">
    <template>
        <div id = 'div1'>
            <sc-text-field id=‘text01’></sc-text-field>    
            <sc-text-field id=‘text02’></sc-text-field>
            <sc-button on-click="selector">selector</sc-button>
            <sc-button on-click="selectorAll">selectorAll</sc-button>
        </div>
    </template>
    <script>
        Polymer({
            is : "query-selector",             
            selector : function() {
                var textInput = Polymer.dom(this.root).querySelector('sc-text-field');
                textInput.value = 'querySelector';
            }
            selectorAll : function() {
                var textInputs = Polymer.dom(this.root).querySelectorAll('sc-text-field');
                for (i=0; i<textInputs.length; i++){
                    textInputs[i].value = 'querySelectorAll';
                }
            }
        });
    </script>
</dom-module>

참고 :
var textInput = Polymer.dom(this.root).querySelector('sc-text-field');
var textInput = Polymer.dom(this.root).querySelector('#text01');
var textInput = this.$$('sc-text-field');
var textInput = this.$$('#text01');
var textInput = this.$.text01;
다양한 접근 방식으로 동일한 element에 접근가능합니다.

Node mutation APIs

  • Polymer.dom(node).setAttribute(attribute, value)
  • Polymer.dom(node).removeAttribute(attribute)
<dom-module id="node-mutation">
    <template>
        <div id = 'div1'>
            <sc-text-field id= "text01"></sc-text-field>
            <sc-button on-click="setAttribute">setAttribute</sc-button>
            <sc-button on-click="removeAttribute">removeAttribute</sc-button>
        </div>
    </template>
    <script>
        Polymer({
            is : "node-mutation",             
            setAttribute : function() {
                Polymer.dom(this.$.text01).('value','setAttribute');
                //item.value = 'setAttribute' 와 동일
            },

            removeAttribute : function() {
                Polymer.dom(this.$.text01).removeAttribute('value');
            }
        });
    </script>
</dom-module>

[목차] [4. local DOM]


5. Events

Event listener setup

이벤트와 이벤트 핸들러 함수 이름을 맵핑시키는 listeners 객체를 통해 host element 에 이벤트 리스너를 추가할 수 있습니다.
또한, this.$ 컬렉션에서 nodeId.evenetName 과 같은 문법으로 엘리먼트에 이벤트 리스너를 추가할 수 있습니다.

my-custom.html

<dom-module id="my-custom">
  <template>
    <div>I will respond</div>
    <div>to a tap on</div>
    <div>any of my children!</div>
    <div id="special">I am special!</div>
  </template>

  <script>
    Polymer({

      is: 'my-custom',

      listeners: {
        'tap': 'regularTap',
        'special.tap': 'specialTap'
      },

      regularTap: function(e) {
        alert("Thank you for tapping");
      },

      specialTap: function(e) {
        alert("It was special tapping");
      }

    });
  </script>
</dom-module>

[목차] [5. Event]


Annotated event listener setup

템플릿 내에 on-event 어노테이션을 이용하여 이벤트 리스너를 local DOM 에 추가할 수 있습니다.
이러한 방법은 이벤트 리스너를 바인딩하기 위해 엘리먼트에 id 를 줄 필요성을 없애줍니다.

my-custom.html

<dom-module id="my-custom">
  <template>
    <button on-tap="handleTap">Kick Me</button>
  </template>
  <script>
    Polymer({
      is: 'my-custom',
      handleTap: function() {
        alert('Ow!');
      }
    });
  </script>
</dom-module>

이벤트 명은 HTML attribute 로 간주되기 때문에 항상 소문자로 변환됩니다. (이벤트 명은 대소문자가 구분되지 않기 때문에 항상 소문자로 쓰기를 권장드립니다.)
이벤트 핸들러 명은 대소문자가 구분됩니다.

[목차] [5. Event]


Imperatively add and remove listeners

listenunlisten 이라는 메서드가 있습니다.

this.listen(this.$.myButton, 'tap', 'onTap');
this.unlisten(this.$.myButton, 'tap', 'onTap');

만약 리스너를 추가했다면 반드시 제거해주어야 합니다. 일반적으로 attached 시점에 리스너를 추가하고, detached 시점에 제거해줍니다.
listeners 객체 혹은 annotated event listeners 를 사용한 경우, 폴리머가 자동으로 이벤트 리스너를 추가/제거해줍니다.

[목차] [5. Event]


Custom events

fire 메서드를 이용하여 host element 에서 커스텀 이벤트를 실행할 수 있습니다. 또한, fire 메서드의 인자로 이벤트 핸들러에 데이터를 보낼 수 있습니다.

my-custom.html

<dom-module id="my-custom">
  <template>
    <button on-click="handleClick">Kick Me</button>
  </template>

  <script>
    Polymer({

      is: 'my-custom',

      handleClick: function(e, detail) {
        this.fire('kick', {kicked: true});
      }

    });

  </script>

</dom-module>
<my-custom></my-custom>

[목차] [5. Event]


Event retargeting

Shadow DOM 은 이벤트의 타겟을 receiving element 와 같은 스코프에 있는 타겟으로 변경하는 event retargeting 이라는 특징을 가지고 있습니다.
(예를 들어, 리스너가 메인 다큐먼트에 있다면 그 타겟도 shadow tree 내에 있는 엘리먼트가 아닌 메인 다큐먼트 내에 있는 엘리먼트이어야 합니다.)

event retargeting 이 퍼포먼스 측면에서 코스트가 크기 때문에, Shady DOM 은 event retargeting 을 하지 않습니다. 대신, 필요한 경우 이벤트를 리타겟 시키는 메커니즘을 폴리머에서 제공합니다.

Polymer.dom(event) 를 이용하여 shady DOM 과 shadow DOM 과 동일한 타겟 데이터를 제공하는 노멀라이즈된 이벤트 객체를 얻을 수 있습니다. 노멀라이즈된 이벤트는 다음과 같은 속성을 가집니다.

  • rootTarget : shadow retargeting 되기 이전의 오리지널 타겟 혹은 루트 타겟. (shadow DOM 의 event.path[0] 과 동일, shady DOM 의 event.target 과 동일)
  • localTarget : retargeted 된 이벤트 타겟. (shadow DOM 의 event.target 과 동일)
  • path : 이벤트가 패스될 노드에 대한 어레이. (shadow DOM 의 event.path 와 동일)

event-retargeting.html

<dom-module id="event-retargeting">
  <template>
    <button id="myButton">Click Me</button>
  </template>

  <script>
    Polymer({

        is: 'event-retargeting',

        listeners: {
          'click': 'handleClick',
        },

        handleClick: function(e) {
          console.info(e.target.id + ' was clicked.');
        }
      });
  </script>
</dom-module>

index.html

<event-retargeting></event-retargeting>

<script>
  var el = document.querySelector('event-retargeting');
  el.addEventListener('click', function(){
    var normalizedEvent = Polymer.dom(event);

    console.info('rootTarget is:', normalizedEvent.rootTarget);
    console.info('localTarget is:', normalizedEvent.localTarget);
    console.info('path is:', normalizedEvent.path);
  });
</script>

위의 예제에서 오리지널 이벤트는 <event-retargeting> 엘리먼트의 local DOM tree 내부에 있는 <button> 에서 트리거 됩니다.
리스너는 메인 다큐먼트인 <event-retargeting> 엘리먼트에 추가됩니다. 해당 엘리먼트의 구현부를 숨기기 위해,
이벤트가 <button> 태그가 아닌 <event-retargeting> 에서 보여지도록 retarget 되어야 합니다.

[목차] [5. Event]


6. Data binding

Binding annotations

바인딩 어노테이션은 중괄호 {{ }} 또는 대괄호 [[ ]] 에서 속성 또는 하위 속성 이름을 포함하여 구성합니다.

  • 대괄호 [[]]는 one-way 바인딩을 지원합니다. 데이터 흐름은 "host -> child" 로 아래로 흐릅니다. (downward)

  • 중괄호 {{}}는 지정된 속성의 설정에 따라 one-way 바인딩 혹은 two-way 바인딩을 지원합니다. (notify 속성을 사용하여 two-way 바인딩)

다음 예제는 자식요소의 name속성과 호스트 요소 myName속성을 바인딩합니다.

host-element.html

<child-element name="{{myName}}"></child-element>

Binding to text content (텍스트 콘텐츠에 바인딩)

child element의 textContent에 바인딩 하려면 엘리먼트안에 바인딩 어노테이션을 작성합니다.
textContent 바인딩은 항상 one-way (host-to-child) 입니다.

<dom-module id="text-content">

    <template>
      First: {{firstName}}<br>
      Last: {{lastName}}
    </template>

    <script>
      Polymer({
        is: 'text-content',
        properties: {
          firstName: String,
          lastName: String
        }
      });
    </script>

</dom-module>
<text-content first-name="emro" last-name="Kim"></text-content>

Compound 바인딩

문자열 리터럴과 바인딩을 결합 할 수 있습니다.

<img src$="https://www.example.com/profiles/{{userId}}.jpg">

<span>Name: {{lastname}}, {{firstname}}</span>

Compound 바인딩은 개별 바인딩의 값이 변화하면 다시 평가됩니다.
Compound 바인딩은 [[]], {{}} 두 어노테이션을 사용할 수 있지만 둘다 항상 one-way (host-to-target) 입니다.

Binding to sub-properties (하위 속성 바인딩)

바인딩 어노테이션은 하위 속성의 경로를 포함 할 수 있습니다.

<dom-module id="sub-properties">

  <template>
    <user-element first="{{user.first}}" last="{{user.last}}"></user-element>
  </template>

  <script>
    Polymer({
      is: 'sub-properties',
      properties: {
        user: Object
      }
    });
  </script>

</dom-module>

Property change notification and two-way binding (속성값 변경에 대한 통지 및 two-way 바인딩)

Polymer는 two-way 바인딩을 지원합니다. 이는 child element 에서 변경 한 데이터가 상위 host element 의 데이터를 변경하는 것을 허용합니다.

two-way 바인딩을 하려면 host element 와 child element 에서 다음의 세가지 조건이 충족되어야 합니다.

  • host element의 child element 바인딩은 중괄호 {{}}를 사용해야합니다. 만약 대괄호를 [[]] 를 사용하면 child element에서
    중괄호를 사용하더라도 결과적으로 one-way(host-to-child) 바인딩됩니다.

  • child element 의 속성값변경을 host element 에 전달하려면 속성 설정에서 notify 플래그를 true 로 설정해야합니다.
    그렇지 않은 경우에는 one-way(host-to-child) 바인딩됩니다.

  • 바인딩되는 child element의 속성은 readOnly 플래그를 true로 설정해서는 안됩니다.
    그렇지 않은 경우에는 one-way(host-to-child) 바인딩됩니다.

Example 1 : Two-way binding (양방향)

<script>
  Polymer({
    is: 'child-element',
    properties: {
      childProp: {
        type: String,
        notify: true
      }
    }
  });
</script>
<dom-module id="host-element">

    <template>
        <child-element child-prop="{{value}}"></child-element>
    </template>

    <script>
      Polymer({
        is: 'host-element',
        properties: {
            hostProp: String
        }
      });
    </script>

</dom-module>

Example 2 : One-way binding (단방향-downward)

<script>
  Polymer({
    is: 'child-element',
    properties: {
      childProp: {
        type: String,
        notify: true
      }
    }
  });
</script>
<dom-module id="host-element">

    <template>
        <child-element child-prop="[[value]]"></child-element>
    </template>

    <script>
      Polymer({
        is: 'host-element',
        properties: {
            hostProp: String
        }
      });
    </script>

</dom-module>

Example 3 : One-way binding (단방향-downward)

<script>
  Polymer({
    is: 'child-element',
    properties: {
      childProp: {
        type: String, // notify : true 가 지정 되지 않음.
      }
    }
  });
</script>
<dom-module id="host-element">

    <template>
        <child-element child-prop="{{value}}"></child-element>
    </template>

    <script>
      Polymer({
        is: 'host-element',
        properties: {
            hostProp: String
        }
      });
    </script>

</dom-module>

Example 4 : One-way binding (단방향-upward, child-to-host)

<script>
  Polymer({
    is: 'child-element',
    properties: {
      childProp: {
        type: String,
        notify: true,
        readOnly : true
      }
    }
  });
</script>
<dom-module id="host-element">

    <template>
        <child-element child-prop="{{value}}"></child-element>
    </template>

    <script>
      Polymer({
        is: 'host-element',
        properties: {
            hostProp: String
        }
      });
    </script>

</dom-module>

Example 5 : Error (모순)

<script>
  Polymer({
    is: 'child-element',
    properties: {
      childProp: {
        type: String,
        notify: true,
        readOnly : true
      }
    }
  });
</script>
<dom-module id="host-element">

    <template>
        <child-element child-prop="[[value]]"></child-element>
    </template>

    <script>
      Polymer({
        is: 'host-element',
        properties: {
            hostProp: String
        }
      });
    </script>

</dom-module>

Change notification protocol

속성에서 notify : true가 지정되면 Polymer 는 이벤트를 발생시키고, 데이터 변경을 상위로 전달합니다.

  • 속성이 변경되면 element 는 연관된 host 에 변경을 알리기 위해 이벤트를 발생시킵니다.

  • 이벤트 이름은 property-name-changed 로 발생됩니다. 예를 들어 userName 이라는 속성의 경우
    user-name-changed 라는 이벤트 명입니다.

  • 해당 이벤트 핸들러에 전달되는 이벤트 객체에는 변경된 새 값이 설정되어 있으며, event.detail.value에 엑세스 할 수 있습니다.

Two-way binding to native elements (기본 엘리먼트의 two-way 바인딩)

기본 element 또는 Polymer 이외의 elements 의 two-way 바인딩은 다음의 어노테이션에 따라 지정할 수 있습니다. :

target-prop="{{hostProp::target-change-event}}"
  • target-prop : 바인딩 element의 속성을 지정합니다.
  • host-prop : target-prop와 바인딩되는 host element의 속성을 지정합니다.
  • target-change-event : target-prop 값 변경 발생 이벤트 이름을 지정합니다.
<!-- input 이벤트를 수신하고 hostValue 속성을 <input>.value 로 설정 -->
<input value="{{hostValue::input}}">

<!-- change 이벤트를 수신하고 hostChecked 속성을 <input>.checked 로 설정 -->
<input type="checkbox" checked="{{hostChecked::change}}">

Polymer element의 two-way 바인딩은 기본 명명 규칙이 적용되기 때문에 이벤트 이름을 지정 할 필요는 없습니다.
다음 두 예제는 같은 의미입니다.

<!-- child-value-changed 이벤트를 수신 -->
<child-element value="{{hostValue::child-value-changed}}">

<!-- 기본 명명 규칙이 적용되어 child-value-changed 이벤트를 수신  -->
<child-element value="{{hostValue}}">

[목차] [5. Data binding]