Lightning Web Components 組合(五)

使用組合咱們能夠用來設計複雜的組件。
組合一些比較小的組件,能夠增長組件的從新性以及可維護性。
經過如下一個簡單的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>
 
 

owner

onwer 是擁有該模版的組件,在以上的demo中onwer 是example-todo-app
onwer 控制全部他包含的組合組件,onwer 能夠web

  • 在組合組件中設置public 屬性
  • 調用組合組件中的public 方法
  • 監聽經過組合組件分發的事件

container

container包含其餘組件,但它自己包含在全部者組件中,在以上的demo 中example-todo-wrapper
是一個container,container 不如onwer 強大,container 能夠api

  • 讀取,可是不能修改包含的組件的public 屬性
  • 調用組合組件的額public 方法
  • 監聽由它包含的組件冒出的一些事件,但不必定是全部事件

父子

父組件能夠包含子組件,父組合能夠是一個onwer 或者containerrapp

在子組件設置屬性

要將包含層次結構從全部者傳遞到子項,全部者能夠在子組件上設置屬性或屬性。
HTML中的屬性變爲JavaScript中的屬性賦值dom

  • 參考demo
    todoApp.js
 
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>
 
 
  • 說明
    JavaScript中的屬性名稱是駝峯大小寫,而HTML屬性名稱是kebab大小寫(以破折號分隔)
    以匹配HTML標準。在todoApp.html,item-name標記中的屬性映射到的itemName JavaScript
    屬性todoItem.js。

調用子組件的方法

對於public 的方法能夠經過@api 裝飾器進行配置
如下是一個簡單的demoatom

  • 組件class
 
// 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>
 
 
  • 建立調用組件方法的cnotainer
<!-- 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>
 
 
  • container 組件class
// 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

  • 非命名slot
    子組件模版
<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>
 
 
  • 命名slot
    參考格式
 
<!-- 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>
 
 
  • slotchange 事件
    格式
 
<!-- container.html -->
<template>
    <slot onslotchange={handleSlotChange}></slot>
</template>
 
 

代碼處理

//container.js
handleSlotChange (e) {
   console.log("New slotted content has been added or removed!");
}
 
 

query selector

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>
 
 
  • 操做api
// 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>]
    }
}
 
 
  • 訪問slot 中的元素
    組件不包含經過slot 傳遞的參數,若是咱們須要訪問能夠經過this.querySelector() 以及 this.querySelectorAll()
    參考代碼
 
// 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>]
    }
}
 
 

經過slot 以及data 進行組件組合

  • 經過slot 組合組件
    參考組件模版
 
<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
    }
]

參考資料

https://lwc.dev/guide/composition#pass-markup-into-slots

相關文章
相關標籤/搜索