使用組合咱們能夠用來設計複雜的組件。
組合一些比較小的組件,能夠增長組件的從新性以及可維護性。
經過如下一個簡單的demo,將會展現關於owner
以及container
的概念,在實際的項目中
example-todo-item
通常是經過for:each
循環動態填充的html
<!-- todoApp.html -->
<template>
<example-todo-wrapper>
<example-todo-item item-name="Milk"></example-todo-item>
<example-todo-item item-name="Bread"></example-todo-item>
</example-todo-wrapper>
<template>
onwer 是擁有該模版的組件,在以上的demo中onwer 是example-todo-app
onwer 控制全部他包含的組合組件,onwer 能夠web
container包含其餘組件,但它自己包含在全部者組件中,在以上的demo 中example-todo-wrapper
是一個container,container 不如onwer 強大,container 能夠api
父組件能夠包含子組件,父組合能夠是一個onwer 或者containerrapp
要將包含層次結構從全部者傳遞到子項,全部者能夠在子組件上設置屬性或屬性。
HTML中的屬性變爲JavaScript中的屬性賦值dom
import { LightningElement, api } from 'lwc';
export default class TodoApp extends LightningElement {}
todoApp.htmlide
<template>
<example-todo-item item-name="Milk"></example-todo-item>
<example-todo-item item-name="Bread"></example-todo-item>
</template>
todoitems.jsui
import { LightningElement, api } from 'lwc';
export default class TodoItem extends LightningElement {
@api itemName;
}
todoitems.htmlthis
<template>
<div>{itemName}</div>
</template>
itemName
JavaScript 對於public 的方法能夠經過@api 裝飾器進行配置
如下是一個簡單的demoatom
// videoPlayer.js
import { LightningElement, api } from 'lwc';
export default class VideoPlayer extends LightningElement {
@api videoUrl;
@api
get isPlaying() {
const player = this.template.querySelector('video');
return player !== null && player.paused === false;
}
@api
play() {
const player = this.template.querySelector('video');
// the player might not be in the DOM just yet
if (player) {
player.play();
}
}
@api
pause() {
const player = this.template.querySelector('video');
if (player) {
// the player might not be in the DOM just yet
player.pause();
}
}
// private method for computed value
get videoType() {
return 'video/' + this.videoUrl.split('.').pop();
}
}
<!-- videoPlayer.html -->
<template>
<div class="fancy-border">
<video autoplay>
<source src={videoUrl} type={videoType} />
</video>
</div>
</template>
<!-- methodCaller.html -->
<template>
<div>
<example-video-player video-url={video}></example-video-player>
<button onclick={handlePlay}>Play</button>
<button onclick={handlePause}>Pause</button>
</div>
</template>
// methodCaller.js
import { LightningElement } from 'lwc';
export default class MethodCaller extends LightningElement {
video = "https://www.w3schools.com/tags/movie.mp4";
handlePlay() {
this.template.querySelector('example-video-player').play();
}
handlePause() {
this.template.querySelector('example-video-player').pause();
}
}
getter
參考@api get isPlaying() {
const player = this.template.querySelector('video');
return player !== null && player.paused === false;
}
@api play(speed) { … }
slots
slot 是一個佔位符,咱們能夠傳遞數據到組件體中,slot 是web components 的一部分
slot 包含命名以及非命名類型,同時對於slot 內容的變更,咱們能夠經過事件進行操做url
<template>
<h1>Content in Slot Demo</h1>
<div>
<slot></slot>
</div>
</template>
slot 佔位符填充
<example-slot-wrapper>
<example-slot-demo>
<h1>Content in Slot Demo</h1>
<div>
<slot><p>Content from Slot Wrapper</p></slot>
</div>
</example-slot-demo>
</example-slot-wrapper>
<!-- namedSlots.html -->
<template>
<p>First Name: <slot name="firstName">Default first name</slot></p>
<p>Last Name: <slot name="lastName">Default last name</slot></p>
<p>Description: <slot>Default description</slot></p>
</template>
slot 內容填充
<!-- slotsWrapper.html -->
<template>
<example-named-slots>
<span slot="firstName">Willy</span>
<span slot="lastName">Wonka</span>
<span>Chocolatier</span>
</example-named-slots>
</template>
<!-- container.html -->
<template>
<slot onslotchange={handleSlotChange}></slot>
</template>
代碼處理
//container.js
handleSlotChange (e) {
console.log("New slotted content has been added or removed!");
}
querySelector() 以及 querySelectorAll() 是標準的dom 操做api,經過此api 咱們能夠操做組件的dom元素
<!-- example.html -->
<template>
<div>First <slot name="task1">Task 1</slot></div>
<div>Second <slot name="task2">Task 2</slot></div>
</template>
// example.js
import { LightningElement } from 'lwc';
export default class Example extends LightningElement {
renderedCallback() {
this.template.querySelector('div'); // <div>First</div>
this.template.querySelector('span'); // null
this.template.querySelectorAll('div'); // [<div>First</div>, <div>Second</div>]
}
}
// namedSlots.js
import { LightningElement } from 'lwc';
export default class NamedSlots extends LightningElement {
renderedCallback() {
this.querySelector('span'); // <span>push the green button.</span>
this.querySelectorAll('span'); // [<span>push the green button</span>, <span>push the red button</span>]
}
}
<example-parent>
<example-custom-child></example-custom-child>
<example-custom-child></example-custom-child>
</example-parent>
爲了支持這種模式,組件做者使用slot 元素,可是組件做者必須管理經過slot 傳遞元素的
生命週期,解決方法,經過通知事件,父組件須要知道子組件能夠進行
通訊,能夠在父組件經過附加在slot 元素上的事件handler
參考代碼
組件模版
<!-- parent.html -->
<template>
<div onprivateitemregister={handleChildRegister}>
<!– Other markup here -->
<slot></slot>
</div>
</template>
代碼
handleChildRegister(event) {
// Suppress event if it’s not part of the public API
event.stopPropagation();
const item = event.detail;
const guid = item.guid;
this.privateChildrenRecord[guid] = item;
}
處理事件通知自組件的父級,父組件須要使用全局惟一的id 才能使用組件,因此咱們看到以上代碼使用了guid
要從子組件分發事件,須要使用connectedCallback
參考
connectedCallback() {
const itemregister = new CustomEvent('privateitemregister', {
bubbles: true,
detail: {
callbacks: {
select: this.select,
},
guid: this.guid,
}
});
this.dispatchEvent(itemregister);
}
要通知父組件,自組件不可用,咱們須要再父子組件之間創建雙向通訊
在註冊期間子組件發送回調到父組件
父組件經過回調調用子組件,將兩一個回調作爲參數傳遞
子組件在取消註冊的時候調用父組件的回調
參考處理:
父組件模版
<!-- parent.html -->
<template>
<slot onprivateitemregister={handleChildRegister}>
</slot>
</template>
處理通知子組件不可用的事件
// parent.js
handleChildRegister(event) {
const item = event.detail;
const guid = item.guid;
this.privateChildrenRecord[guid] = item;
// Add a callback that
// notifies the parent when child is unregistered
item.registerDisconnectCallback(this.handleChildUnregister);
}
handleChildUnregister(event) {
const item = event.detail;
const guid = item.guid;
this.privateChildrenRecord[guid] = undefined;
}
自組件在取消註冊時調用的組組件回調
// child.js
connectedCallback() {
const itemregister = new CustomEvent('privateitemregister', {
bubbles: true,
detail: {
callbacks: {
registerDisconnectCallback: this.registerDisconnectCallback
},
guid: this.guid,
}
});
this.dispatchEvent(itemregister);
}
// Store the parent's callback so we can invoke later
registerDisconnectCallback(callback) {
this.disconnectFromParent = callback;
}
子組件通知父組件自生不可用
disconnectedCallback() {
this.disconnectFromParent(this.guid);
}
將數據傳遞給子組件
通常組件註冊已經完成,咱們能夠經過暴露的回調方法進行父子之間的數據通訊
參考:
this.privateChildrenRecord[guid].callbacks.select();
父組件能夠傳遞數據到子組件,以下
this.privateChildrenRecord[guid].callbacks.setAriaLabelledBy('my-custom-id');
子組件對應暴露的屬性
@track ariaLabelledby;
setAriaLabelledBy(id) {
this.ariaLabelledby = id;
}
<template>
<div class="example-parent">
<template for:each={itemsData} for:item="itemData">
<example-child
onclick={onItemSelect}
id={itemData.id}
key={itemData.id}>
</example-child>
</template>
</div>
</template>
傳遞的數據格式以下:
itemsData = [
{
label : 'custom label 1',
id : 'custom-id-1'
selected : false
},
{
label : 'custom label 2',
id : 'custom-id-2'
selected : false
}
]