property
<input>
태그에 값을 변경하고 저장할 수 있도록 value와 change 이벤트를 넣어주도록 하겠습니다.@customElement("lit-tomato")
class Tomato extends LitElement {
@property() value = "";
onChange(e) {
this.value = e.target.value;
console.log(e.target.value);
}
render() {
return html` <input .value="${this.value}" @keyup="${this.onChange}" /> `;
}
}
<input>
태그에 .value라는 값으로 value property를 넣어주었습니다. 여기서 속성에 ' . '이 붙어있다면 값은<input>
태그의 value property에 바인딩 되게 됨을 의미합니다.' . '은 lit-html에서 property를 바인딩 할 때 사용됩니다.
event listener
@keyup을 넣게 되면<input>
의 'keyup'에 이벤트에 onChange라는 이벤트 헨들러를 추가해 주게 됩니다. 결과적으로, <input>
의 keyup 이벤트가 발생할 때 마다 value property가 변경되며 콘솔이 찍히게 됩니다.attribute
이번에는<input>
의 '.value' 속성을 'value'로 변경해보도록 하겠습니다.return html` <input value="${this.value}" @keyup="${this.onChange}" /> `;
attribute를 전달해 주면 그림과 같이 html 속성으로 추가되는걸 볼 수 있습니다. 몇 가지 attribute를 더 사용해 보도록 하겠습니다.
render() {
return html`
<input
id="my-input"
class="my-class"
type="text"
style="color:blue;"
.value="${this.value}"
@keyup="${this.onChange}"
/>
`
}
boolean attribute
이번에는<input>
태그에 'disabled'를 추가해 보도록 하겠습니다.<input
.value="${this.value}"
@keyup="${this.onChange}"
disabled="false"
/>
@property() value = "4";
@property({ type: Boolean }) disabled = false;
onChange(e) {
this.value = e.target.value;
}
render() {
return html`
<input
id="my-input"
.value="${this.value}"
@change="${this.onChange}"
?disabled="${this.disabled}"
/>
`;
}
커스텀 엘리먼트에서는 값들을 어떻게 바인딩하여 사용 할 수 있는지 알아보도록 하겠습니다.
custom element
앞에서<input>
태그를 사용하여 property를 바인딩 하였드이 커스텀 엘리먼트를 만들어 property를 바인딩 해보도록 하겠습니다. 우선 property를 가진<lit-tomato>
라는 엘리먼트를 만들어<child-tomato>
라는 컴포넌트를 바인딩 시키도록 하겠습니다. 앞의 코드에서 id는 attribute, .value는 property, @change는 event Listener, ?disabled는 boolean Attribute로
<input>
태그에 바인딩 되었습니다. 만약 앞에 기호를 붙이지 않는다면 전부 attribute로 바인딩 되게 됩니다.이번에는 커스텀 엘리먼트를 만들어 속성들을 바인딩해보도록 하겠습니다.
pages/lit-tomato.ts
import { LitElement, html, customElement, property } from "lit-element";
import { component$ } from '@builder.io/qwik';
import "../components/child-tomato";
@customElement("lit-tomato")
class Tomato extends LitElement {
render() {
return html` <child-tomato></child-tomato> `;
}
}
declare global {
interface HTMLElementTagNameMap {
"lit-tomato": Tomato;
}
}
pages/child-tomato.ts
import { LitElement, html, customElement, property } from "lit-element";
@customElement("child-tomato")
class ChildTomato extends LitElement {
render() {
return html`
<style></style>
<h1>i'm child-tomato</h1>
<input />
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"child-tomato": ChildTomato;
}
}
<lit-tomato>
에 앞에서와 같이 전달할 property를 만들도록 하겠습니다.@property({ type: String }) value = "toamto";
@property({ type: Boolean }) disabled = true;
onChange(e) {
this.value = e.target.value;
console.log(e.target.value);
}
<input>
에서와 동일하게 속성을 전달해 보도록 하겠습니다.<child-tomato
id="my-input"
.value="${this.value}"
@change="${this.onChange}"
?disabled="${this.disabled}"
></child-tomato>
components/child-tomato.ts
class ChildTomato extends LitElement {
//*connectedCallback은 커스텀 엘리먼트가 문서의 DOM에 처음 연결될 때 호출됩니다.
connectedCallback() {
super.connectedCallback();
console.log(this.id);
console.log(this.value);
console.log(this.change);
console.log(this.disabled);
}
이벤트 헨들러를 @change로 전달하게 되면 onChange 함수를 값으로 전달받는 것이 아닌,
<child-tomato>
에 change 의 change 이벤트에 이벤트 리스너가 추가 됨을 의미합니다. 이를 확인하기위해 event를 발생시켜 보도록 하겠습니다.components/child-tomato.ts
connectedCallback() {
super.connectedCallback();
this.dispatchEvent(new Event("change"));
// console.log(this.id);
// console.log(this.value);
// console.log(this.change);
// console.log(this.disabled);
}
좀 더 직접적으로 확인해 보기위해 이벤트 이름을 바꿔보도록 하겠습니다!
pages/lit-tomato.ts
<child-tomato
@jerrynim-custom-event="${this.onChange}"
></child-tomato>
class ChildTomato extends LitElement {
connectedCallback() {
super.connectedCallback();
this.dispatchEvent(new Event("jerrynim-custom-event"));
그렇다면 <child-tomato>의 <input>태그에 onChange를 전달하여 사용하려면 어떻게 해야할가요?
<lit-tomato>에서 '@change'를 '.onchange'로 변경해 보도록 하겠습니다.
pages/lit-tomato.ts
<child-tomato
.onchange="${this.onChange}"
></child-tomato>
components/child-tomao.ts
connectedCallback() {
super.connectedCallback();
console.log(this.onchange);
}
class ChildTomato extends LitElement {
customFunction: any;
render() {
return html`
<style></style>
<h1>i'm child-tomato</h1>
<input .value="${this.value}" @keyup="${this.customFunction}" />
input value is ${this.value}
`;
}
}
<lit-tomato>
의 value가<child-tomato>
에 바인딩 되지 않았다는 걸 알수 있습니다. 바인딩 시키기위해 <child-tomato>
에 value property를 만들어 주도록 하겠습니다.components/child-tomato.ts
class ChildTomato extends LitElement {
@property({ type: String }) value = "";
이전에 '?disabled'로 전달해준 속성의 값이 undefined 였습니다. ' ?' 기호는 attribute이기에 property처럼 값을 받지 못하였습니다. disabled 값을 바인딩하기 위해 커스텀 엘리먼트에 'disabled'라는 이름을 가진 attribute가 필요합니다.
<child-tomato>
에 'disabled'라는 attribute를 가진 property를 만들어 보도록 하겠습니다.@property({ type: Boolean, attribute: "disabled" }) disabledValue = false;
<input
.value="${this.value}"
@keyup="${this.customFunction}"
?disabled="${this.disabledValue}"
/>
<lit-tomato>
의 disabled 값은 'true'로 disabled 값이 잘 적용 된 것을 확인할 수 있습니다.children
이번에는<child-tomato>
에게 html 템플릿을 전달해보도록 하겠습니다.pages/lit-tomato.ts
import { LitElement, html, customElement, property } from "lit-element";
import "../components/child-tomato";
@customElement("lit-tomato")
class Tomato extends LitElement {
render() {
return html`<child-tomato>자식</child-tomato>`;
}
}
declare global {
interface HTMLElementTagNameMap {
"lit-tomato": Tomato;
}
}
components/child-tomato.ts
import { LitElement, html, customElement, property } from "lit-element";
@customElement("child-tomato")
class ChildTomato extends LitElement {
render() {
return html`
<style></style>
<div>자식 :</div>
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"child-tomato": ChildTomato;
}
}
첫번째 방법은 간단하게 this.children을 사용하면 됩니다.
class TomatoChild extends LitElement {
render() {
return html`
<style></style>
<div>${this.children}</div>
`;
}
}
<slot>
태그를 사용하는 것입니다.slot MDN
마치며..
Lit-html에서 property를 바인딩 하는 것을 알아 보았습니다. 바인딩하는 법만 알게된다면 투두리스트 정도의 웹을 만드는데 큰 어려움을 없을 거라 생각합니다.다음 포스팅에서는 redux를 사용하여 전역 상태관리를 해보려고 합니다.