zoukankan      html  css  js  c++  java
  • Lightning Web Components 组合(五)

    使用组合我们可以用来设计复杂的组件。
    组合一些比较小的组件,可以增加组件的重新性以及可维护性。
    通过以下一个简单的demo,将会展示关于owner 以及container 的概念,在实际的项目中
    example-todo-item 一般是通过for:each 循环动态填充的

     
    <!-- 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 可以

    • 在组合组件中设置public 属性
    • 调用组合组件中的public 方法
    • 监听通过组合组件分发的事件

    container

    container包含其他组件,但它本身包含在所有者组件中,在以上的demo 中example-todo-wrapper
    是一个container,container 不如onwer 强大,container 可以

    • 读取,但是不能修改包含的组件的public 属性
    • 调用组合组件的额public 方法
    • 监听由它包含的组件冒出的一些事件,但不一定是所有事件

    父子

    父组件可以包含子组件,父组合可以是一个onwer 或者containerr

    在子组件设置属性

    要将包含层次结构从所有者传递到子项,所有者可以在子组件上设置属性或属性。
    HTML中的属性变为JavaScript中的属性赋值

    • 参考demo
      todoApp.js
     
    import { LightningElement, api } from 'lwc';
    export default class TodoApp extends LightningElement {}
     
     

    todoApp.html

    <template>
        <example-todo-item item-name="Milk"></example-todo-item>
        <example-todo-item item-name="Bread"></example-todo-item>
    </template>
     
     

    todoitems.js


    import { LightningElement, api } from 'lwc';
    export default class TodoItem extends LightningElement {
        @api itemName;
    }
     
     

    todoitems.html


    <template>
        <div>{itemName}</div>
    </template>
     
     
    • 说明
      JavaScript中的属性名称是驼峰大小写,而HTML属性名称是kebab大小写(以破折号分隔)
      以匹配HTML标准。在todoApp.html,item-name标记中的属性映射到的itemName JavaScript
      属性todoItem.js。

    调用子组件的方法

    对于public 的方法可以通过@api 装饰器进行配置
    以下是一个简单的demo

    • 组件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 内容的变动,我们可以通过事件进行操作

    • 非命名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

  • 相关阅读:
    kill命令
    linux grep命令
    ps命令详解
    Linux chmod命令详解
    Linux netstat命令详解
    多线程同步机制的几种方法
    C++_运算符重载 总结
    malloc/free与new/delete的区别
    python发送邮件
    linux之nfs
  • 原文地址:https://www.cnblogs.com/rongfengliang/p/10971506.html
Copyright © 2011-2022 走看看