【问题标题】:Aurelia conditionally wrapping slots in componentsAurelia 有条件地在组件中包装插槽
【发布时间】:2017-12-20 16:04:03
【问题描述】:

我现在正在创建包含 material-components-web, cards specifically 的 Aurelia 组件,我想知道实现多个内容部分(动作等)的正确方法是什么。

插槽似乎是正确的选择,但我不能始终将操作 div 放在模板上,但前提是实际存在任何操作。

简单地说,我需要检查是否在组件模板中定义了一个插槽。

<template>
    <div class="card">
        <div class="content">
            <slot></slot>
        </div>

        <!-- somehow check here if the slot has been defined -->
        <div class="actions">
            <slot name="actions"></slot>
        </div>
    </div>
</template>

【问题讨论】:

    标签: javascript aurelia


    【解决方案1】:

    使用 CSS :empty 选择器

    CSS 是这项工作的正确工具,而不是 Aurelia。 :empty 选择器将允许您在未填充插槽时使用display: none div.actions

    .card .actions:empty {
      display: none;
    }
    

    根据:empty selector spec作为explained by CSS-Tricks,空格导致empty无法匹配,所以我们只需要把槽周围的空格去掉即可。

    <div class="actions"><slot name="actions"></slot></div>
    

    这里的工作示例:https://gist.run/?id=040775f06aba5e955afd362ee60863aa

    【讨论】:

    • 如果这个回答对你有帮助,请采纳并点赞。如果您想帮助我继续提供高质量的答案,请考虑在 Patreon 上支持我:patreon.com/davismj
    【解决方案2】:

    这是我汇总的一种方法,用于检测任何插槽是否有子项(不包括 HTML cmets)

    TypeScript

    import { autoinject } from 'aurelia-framework';
    
    @autoinject
    export class MyClass {
        private constructor(readonly element: Element) {
        }
    
        private attached() {
        }
    
        get hasSlotChildren(): boolean {
            if (!this.element ||
                !(this.element as any).au) {
                return false;
            }
            let childrenCount = 0;
            const slots = (this.element as any).au.controller.view.slots;
            for (let slotName of Object.keys(slots)) {
                const slot = slots[slotName];
                if (slot.children && 
                    slot.children.length > 0) {
                    for (let child of slot.children) {
                        if (child instanceof Comment) {
                            // Ignore HTML comments
                            continue;
                        }
                        childrenCount++;
                    }
                }
            }
            return childrenCount > 0
        }
    }
    

    HTML

    <template 
        class="my-class" 
        show.bind="hasSlotChildren"
    >
        <slot></slot>
    </template>
    

    【讨论】:

      【解决方案3】:

      开箱即用,没有像$slots 属性那样直接执行此操作的方法,但是您应该能够通过模板控制器实例本身访问插槽:au.controller.view.slots - 内部的特定插槽此数组包含有关插槽本身及其子项的更多信息。

      Here 是一个带有模态组件(自定义模态元素)的 Aurelia 应用程序示例。模态本身有一个插槽,可以在其中投射 HTML。我们有页眉、正文和页脚。

      我们自定义元素中的每个预定义槽都应显示在children 对象内,其中属性名称是我们槽的名称。如果您没有为插槽(默认插槽)提供名称,则其内部名称为:__au-default-slot-key__

      我们首先检查插槽是否存在,然后检查其子项的长度,children 数组存在于每个插槽中。如果一个插槽没有投射到其中的 HTML,它的子项长度将为零。这是可靠的,因为在插槽内定义的默认内容不会放入 children 数组中,只有投影的 HTML 会。

      您会看到工作主要在 modal.html 内部完成,但请密切注意 modal.js,我们在其中注入自定义元素的元素引用,然后使用 au 访问 Aurelia 实例以到达包含我们的插槽本身的控制器。

      这种方法有一个警告:您不能使用if.bind 有条件地删除自定义元素内的 HTML。如果您在包含插槽的 DIV 上使用 if.bind,它实际上会删除其插槽引用,因此无法对其进行检查。要解决这个问题,只需使用show.bind(就像我在提供的运行示例中所做的那样)。

      【讨论】:

      • 完美运行,非常酷。我很惊讶我在这个主题上找不到任何东西,每个人似乎都使用自定义属性来用类来装饰 div,这对于像这样简单的事情来说似乎有点过头了。
      • 后续问题,可能无关:你知道为什么this 不起作用吗?我试图将一些逻辑抽象到视图模型中,因为我将在许多地方检查已定义的插槽。在 attach() 发生之前,似乎 slotDefined() 方法只被调用一次。我也尝试使用 @computedFrom('$slots') 装饰器,但现在组件根本不渲染(没有错误),只有定义的插槽渲染。
      • 暂时使用this hack。似乎 .bind 无法调用函数,所以我不得不使用 getter 来解决它。一切正常,再次感谢您的帮助。
      • 太棒了@marekpw 我会说你的解决方案非常好。能够观察函数是您无法做到的,因为它需要进行脏检查,getter 绝对是解决这个问题的方法,在整理它方面做得很好。
      • 谢谢!它工作得很好(没有脏检查),现在我正在考虑将代码抽象到装饰器中并将其放在 npm 上。我是装饰器的新手(实际上我自己从来没有写过任何东西),我只是想知道你是否认为他们是要走的路?顺便说一句,我使用 Typescript。
      猜你喜欢
      • 1970-01-01
      • 2018-11-26
      • 1970-01-01
      • 2021-02-06
      • 2016-09-29
      • 2014-11-04
      • 2023-01-02
      • 2021-03-03
      • 1970-01-01
      相关资源
      最近更新 更多