programing

내용이 있는 경우에만 슬롯 표시

yoursource 2022. 8. 29. 23:22
반응형

내용이 있는 경우에만 슬롯 표시

내용이 있는 슬롯만 표시할 수 있는 방법이 있나요?

예를 들어, 나는 심플한 빌딩을 만들고 있다.Card.vue다음 내용이 있는 경우에만 바닥글을 표시합니다.

템플릿:

<template>
    <div class="panel" :class="panelType">
        <div class="panel-heading">
            <h3 class="panel-title">
                <slot name="title">
                    Default Title
                </slot>
            </h3>
        </div>

        <div class="panel-body">
            <slot name="body"></slot>

            <p class="category">
                <slot name="category"></slot>
            </p>
        </div>

        <div class="panel-footer" v-if="hasFooterSlot">
            <slot name="footer"></slot>
        </div>
    </div>
</template>

스크립트:

<script>
    export default {
        props: {
            active: true,
            type: {
                type: String,
                default: 'default',
            },
        },

        computed: {
            panelType() {
                return `panel-${this.type}`;
            },

            hasFooterSlot() {
                return this.$slots['footer']
            }
        }
    }
</script>

표시 내용:

<card type="success"></card>

위 구성요소는 바닥글을 포함하지 않으므로 렌더링되지 않아야 하지만 렌더링됩니다.

사용해보았습니다.this.$slots['footer']단, 정의되지 않은 값이 반환됩니다.

누구 팁 있는 사람?

다음 사이트에서 입수할 수 있습니다.

this.$slots.footer

이게 효과가 있을 거야

hasFooterSlot() {
  return !!this.$slots.footer;
}

.

확인하셔야 합니다vm.$slots그리고 또vm.$scopedSlots할 수 있을 것 같아요.

hasSlot (name = 'default') {
   return !!this.$slots[ name ] || !!this.$scopedSlots[ name ];
}

나는 비슷한 문제에 부딪혔지만 광범위한 코드 베이스에 걸쳐서 원자 설계 구조화된 컴포넌트를 만들 때 쓰는 것은 피곤할 수 있다.hasSlot()TDD에 관한 한 항상 테스트하는 또 하나의 방법...그렇게 말하면, 당신은 언제나 원초적인 논리를 넣을 수 있습니다.v-if특히 새로운 개발자가 코드 구조를 체크할 경우 템플릿이 어수선하고 읽기 어려울 수 있습니다.

부모를 제거하는 방법을 알아내는 임무를 맡았습니다div슬롯이 제공되지 않은 경우 슬롯의 수

문제:

<template>
  <div>
    <div class="hello">
      <slot name="foo" />
    </div>
    <div class="world">
      <slot name="bar" />
    </div>
  </div>
</template>

//instantiation
<my-component>
  <span slot="foo">show me</span>
</my-component>

//renders
<div>
  <div class="hello">
    <span slot="foo">show me</span>
  </div>
  <div class="world"></div>
</div>

보다시피, 문제는 컴포넌트 작성자가 필요없다고 판단했을 때 스타일링 문제를 제공할 수 있는 거의 '불필요한' div를 가지고 있다는 것입니다.bar슬롯을 클릭합니다.

물론 갈 수 있다<div v-if="$slots.bar">...</div>또는<div v-if="hasBar()">...</div>하지만 내가 말했듯이, 그것은 지루해지고 결국 읽기가 더 어려워질 수 있다.

솔루션

제 해결책은 일반적인 것을 만드는 것이었습니다.slot주변 div가 있는 슬롯을 만든 컴포넌트...를 참조해 주세요.

//slot component
<template>
  <div v-if="!!$slots.default">
    <slot />
  </div>
</template>


//usage within <my-component/>
<template>
  <div>
    <slot-component class="hello">
      <slot name="foo"/>
    </slot-component>
    <slot-component class="world">
      <slot name="bar"/>
    </slot-component>
  </div>
</template>

//instantiation
<my-component>
  <span slot="foo">show me</span>
</my-component>

//renders
<div>
  <div class="hello">
    <span>show me</span>
  </div>
</div>

저는 이 아이디어를 시도할 때 유스케이스 문제에 부딪혔고, 때로는 이 접근방식의 이점을 위해 마크업 구조를 변경해야 했습니다.이 방법을 사용하면 각 컴포넌트 템플릿 내에서 작은 슬롯체크를 할 필요가 줄어듭니다.이 컴포넌트는 이 컴포넌트를<conditional-div />컴포넌트...

또, 어트리뷰트를 적용하는 것도 주의할 필요가 있습니다.slot-component인스턴스화(<slot-component class="myClass" data-random="randomshjhsa" />Atribut이 contain div of the contained div로 흘러들어가기 때문에)는 괜찮습니다.slot-component템플릿입니다.

이게 도움이 됐으면 좋겠다.

UPDATE 이 플러그 인을 썼기 때문에 Import의 필요성custom-slot각 컨슈머 컴포넌트의 컴포넌트는 더 이상 필요하지 않으며 main.js 인스턴스화에 Vue.use(SlotPlugin)만 입력하면 됩니다.(아래 참조)

const SLOT_COMPONENT = {
  name: 'custom-slot',
  template: `
    <div v-if="$slots.default">
      <slot />
    </div>
  `
}

const SLOT_PLUGIN = {
  install (Vue) {
    Vue.component(SLOT_COMPONENT.name, SLOT_COMPONENT)
  }
}

export default SLOT_PLUGIN

//main.js
import SlotPlugin from 'path/to/plugin'
Vue.use(SlotPlugin)
//...rest of code

즉, 이것은 인라인으로 실시합니다.

<template lang="pug">
  div
    h2(v-if="$slots.title")
      slot(name="title")
    h3(v-if="$slots['sub-title']")
      slot(name="sub-title")
</template>

CSS는 이를 대폭 간소화합니다.다음 코드를 사용하여 voila!

.panel-footer:empty {
  display: none;
}

다음은 Vue 3 구성 API의 솔루션입니다.

<template>
    <div class="md:grid md:grid-cols-5 md:gap-6">

        <!-- Here, you hide the wrapper if there is no used slot or empty --> 
        <div class="md:col-span-2" v-if="hasTitle">
            <slot name="title"></slot>
        </div>

        <div class="mt-5 md:mt-0" 
            :class="{'md:col-span-3': hasTitle, 'md:col-span-5': !hasTitle}">
            <div class="bg-white rounded-md shadow">
                <div class="py-7">
                    <slot></slot>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
import {ref} from "vue";

export default {
    setup(props, {slots}) {
        const hasTitle = ref(false)

        // Check if the slot exists by name and has content.
        // It returns an empty array if it's empty.
        if (slots.title && slots.title().length) {
            hasTitle.value = true
        }

        return {
            hasTitle
        }
    }
}
</script>

처음에는 https://stackoverflow.com/a/50096300/752916이 작동한다고 생각했지만, $1200 때문에 좀 더 확장해야 했습니다.슬롯은 반환값에 관계없이 항상 truthy한 함수를 반환합니다.이것이 나의 해결책이지만, 이 질문에 대한 진짜 답은 "이렇게 하는 것은 반작용이며 가능하면 피해야 한다"는 결론에 도달했다.예를 들어 슬롯에 넣을 수 있는 별도의 바닥글 구성 요소를 만듭니다.

해키 솔루션

   hasFooterSlot() {
        const ss = this.$scopedSlots;
        const footerNodes = ss && ss.footer && ss.footer();
        return footerNodes && footerNodes.length;
   }

베스트 프랙티스(바닥글에 도움이 되는 컴포넌트)

const panelComponent = {
  template: `
    <div class="nice-panel">
        <div class="nice-panel-content">
          <!-- Slot for main content -->
          <slot />
        </div>
        <!-- Slot for optional footer -->
        <slot name="footer"></slot>
    </div>
  `
}
const footerComponent = {
  template: `
    <div class="nice-panel-footer">
        <slot />
    </div>
  `
}

var app = new Vue({
  el: '#app',
  components: {
    panelComponent,
    footerComponent
  },
  data() {
    return {
      name: 'Vue'
    }
  }
})
.nice-panel {
  max-width: 200px;
  border: 1px solid lightgray;
}

.nice-panel-content {
  padding: 30px;
}

.nice-panel-footer {
  background-color: lightgray;
  padding: 5px 30px;
  text-align: center;
}
<script src="https://unpkg.com/vue@2.6.11/dist/vue.min.js"></script>
<div id="app">
  <h1>Panel with footer</h1>

  <panel-component>
    lorem ipsum
    <template #footer>
      <footer-component> Some Footer Content</footer-component>
    </template>
  </panel-component>

  <h1>Panel without footer</h1>

  <panel-component>
    lorem ipsum
  </panel-component>
</div>

제가 제대로 이해했으면 좋겠어요. " " " " " " " " " " " " " " " " " " " 를 하면 안 요?<template>태그: 슬롯이 비어 있는 경우 렌더링되지 않습니다.

<slot name="foo"></slot>

다음과 같이 사용합니다.

<template slot="foo">
   ...
</template>

@Bert 답변은 다음과 같은 동적 템플릿에서는 작동하지 않는 것 같습니다.<template v-slot:foo="{data}"> ... </template> 을 사용하게 되었습니다. 결국 다음을 사용하게 되었습니다.

 return (
        Boolean(this.$slots.foo) ||
        Boolean(typeof this.$scopedSlots.foo == 'function')
      );

테스트 완료

vue 3에서는 다음과 같이 동작합니다.

먼저 onMounted를 사용하여 값을 가져오고 다음으로 onUpdate를 사용하여 값을 업데이트합니다.

   <template>
     <div v-if="content" class="w-1/2">
          <slot name="content"></slot>
     </div>
   </template>


 <script>
     import { ref, onMounted, defineComponent, onUpdated } from "vue";   
     export default defineComponent({
        setup(props, { slots }) {
        const content = ref()
        onMounted(() => {
                if (slots.content && slots.content().length) {
                    content.value = true
                }
            })
            onUpdated(() => {
                content.value = slots.content().length
                console.log('CHECK VALUE', content.value)
            })
})
</script>

언급URL : https://stackoverflow.com/questions/44077277/only-show-slot-if-it-has-content

반응형