typescript Vuejs 3和Bootstrap 5模态可重用构件的程序化显示

63lcw9qa  于 2023-02-10  发布在  TypeScript
关注(0)|答案(2)|浏览(239)

特灵创建一个基于Bootstrap 5的(半)可重用模态组件,使用vuejs 3和可组合API。设法使其部分工作,
给定(大部分是标准Bootstrap 5模态,但基于“show”prop添加了类,并在主体和页脚中添加了插槽):

<script setup lang="ts">
defineProps({
  show: {
    type: Boolean,
    default: false,
  },
  title: {
    type: String,
    default: "<<Title goes here>>",
  },
});
</script>

<template>
  <div class="modal fade" :class="{ show: show, 'd-block': show }"
    id="exampleModal" tabindex="-1" aria-labelledby="" aria-hidden="true">
    <div class="modal-dialog">
      <div class="modal-content">
        <div class="modal-header">
          <h5 class="modal-title" id="exampleModalLabel">{{ title }}</h5>
          <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
        </div>
        <div class="modal-body">
          <slot name="body" />
        </div>
        <div class="modal-footer">
          <slot name="footer" />
          <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
            Close
          </button>              
        </div>
      </div>
    </div>
  </div>
</template>

并被“召唤"

<script setup lang="ts">
import { ref } from "vue";
import Modal from "@/components/Common/Modal.vue";

let modalVisible= ref(false);

function showModal(){
 modalVisible.value = true;
}
</script>

<template>
  <button @click="showModal">Show Modal</button>
  <Modal title="Model title goes here" :show="modalVisible">
    <template #body>This should be in the body</template>
    <template #footer>
      <button class="btn btn-primary">Extra footer button</button>
    </template>
</Modal>
</template>

我得到了一个模态的“显示”,但是淡入动画不起作用,背景不可见,模态中的数据按钮不起作用(也就是说,它不会关闭)。我觉得这与我的整个方法有关。

注意:我不能使用带有data-bs-toggle="modal" data-bs-target="#exampleModal"属性的标准按钮,因为这个模型的实际触发器来自另一个组件的逻辑(就像设置一个bool一样),并且可重用模态组件将独立于它的触发器---它也感觉不到正确的'Vue'方式来完成它。
所以我想我只是在“显示"html,我需要以某种方式示例化一个引导模式...只是不确定如何去做
package.json(以及相关的)

"dependencies": {
    "@popperjs/core": "^2.11.2",
    "bootstrap": "^5.1.3",
    "vue": "^3.2.31",
  },

Code sand box here(在代码沙盒上工作时无法获得新的合成API和TS,因此使用标准选项API方法进行了轻微重写,因此代码略有不同,但表现出相同的行为)

wn9m85ua

wn9m85ua1#

好吧..所以几个小时后我想出了一个解决方案,张贴在这里,因为它可能会帮助其他人。引导模式'对象'需要创建。所以首先必须从引导导入模式对象。它的创建需要一个DOM引用,所以必须添加一个ref到html元素,脚本中有一个ref属性来保存指向它的链接。t填充,直到组件被装载,因此Bootstrap模态对象的构造需要在Onmounted中完成,因为ref现在将链接然后,我没有传递show prop down,因为在父元素和子元素之间保持同步很麻烦,我只是在对话框组件本身上公开了一个show方法(感觉也更优雅一些)。由于<script setup>对象是CLOSED BY DEFAULT,因此方法的公开是通过defineExpose完成的。

<script setup lang="ts">
import { onMounted, ref } from "vue";
import { Modal } from "bootstrap";
defineProps({
  title: {
    type: String,
    default: "<<Title goes here>>",
  },
});
let modalEle = ref(null);
let thisModalObj = null;

onMounted(() => {
  thisModalObj = new Modal(modalEle.value);
});
function _show() {
  thisModalObj.show();
}
defineExpose({ show: _show });
</script>

<template>
  <div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby=""
    aria-hidden="true" ref="modalEle">
    <div class="modal-dialog">
      <div class="modal-content">
        <div class="modal-header">
          <h5 class="modal-title" id="exampleModalLabel">{{ title }}</h5>
          <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
        </div>
        <div class="modal-body">
          <slot name="body" />
        </div>
        <div class="modal-footer">
          <slot name="footer"></slot>
          <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
            Close
          </button>
        </div>
      </div>
    </div>
  </div>
</template>

而“父母”

<script setup lang="ts">
import { ref } from "vue";
import Modal from "@/components/Common/Modal.vue";

let thisModal= ref(null);

function showModal(){
 thisModal.value.show();
}
</script>

<template>
  <button @click="showModal">Show Modal</button>
  <Modal title="Model title goes here" ref="thisModal">
    <template #body>This should be in the body</template>
    <template #footer>
      <button class="btn btn-primary">Extra footer button</button>
    </template>
</Modal>
</template>

..也许应该另外添加一个OnUnmount来清理对象以使其整洁。

5us2dqdw

5us2dqdw2#

我没有使用TS,但它可以很容易地转换成TS。请确保您保持无障碍功能(焦点陷阱,esc关闭模态,咏叹调属性)!!!这是我如何做到这一点。唯一的事情,我会改变的是背景动画,它有点马虎。(也包括了一个简单的实用程序,以生成唯一的ID)。
儿童:

<template>
  <teleport to="body">
    <focus-trap v-model:active="active">
      <div
          ref="modal"
          class="modal fade"
          :class="{ show: active, 'd-block': active }"
          tabindex="-1"
          role="dialog"
          :aria-labelledby="`modal-${id}`"
          :aria-hidden="active"
      >
        <div class="modal-dialog modal-dialog-centered" role="document" >
          <div class="modal-content">
            <div class="modal-header">
              <h5 class="modal-title text-dark" :id="`modal-${id}`"><slot name="title"></slot></h5>
              <button
                  type="button"
                  class="close"
                  data-dismiss="modal"
                  aria-label="Close"
                  @click="$emit('closeModal', false)"
              >
                <span aria-hidden="true">&times;</span>
              </button>
            </div>
            <div class="modal-body text-dark">
              <slot></slot>
            </div>
            <div class="modal-footer text-dark">
              <button type="button" class="btn btn-danger" @click="$emit('closeModal', true)">Yes</button>
              <button type="button" class="btn btn-success" @click="$emit('closeModal', false)">No</button>
            </div>
          </div>
        </div>
      </div>
    </focus-trap>
    <div class="fade" :class="{ show: active, 'modal-backdrop show': active }"></div>
  </teleport>
</template>

<script>

import { ref, watch} from 'vue'
import IdUnique from '../js/utilities/utilities-unique-id';
import { FocusTrap } from 'focus-trap-vue'

export default {
  name: 'Modal',
  emits: ['closeModal'],
  components: {
    FocusTrap: FocusTrap
  },
  props: {
    showModal: Boolean,
    modalId: String,
  },
  setup(props) {
    const id = IdUnique();
    const active = ref(props.showModal);

    watch(() => props.showModal, (newValue, oldValue) => {
      if (newValue !== oldValue) {
        active.value = props.showModal;
        const body = document.querySelector("body");
        props.showModal ? body.classList.add("modal-open") : body.classList.remove("modal-open")
      }
    },{immediate:true, deep: true});

    return {
      active,
      id
    }
  }
}
</script>

母公司:

<template>
  <div class="about">
    <div v-for="product in products">
      <Product :product="product" :mode="mode"></Product>
    </div>
  </div>
  <template v-if="mode === 'cart'">
    <div class="hello">
      <modal :showModal="showModal" @closeModal="handleCloseModal">
        <template v-slot:title>Warning</template>
        <p>Do you really wish to clear your cart</p>
      </modal>

      <button href="#" @click="handleToggleModal">{{ $t('clearCart') }}</button>
    </div>
  </template>
</template>

<script>
import {ref} from "vue";
import Product from '../components/Product'
import Modal from '../components/Modal'

export default {
  name: 'HomeView',
  components: {
    Product,
    Modal
  },
  setup() {
    const mode = ref('cart');
    const showModal = ref(false);
    let products = JSON.parse(localStorage.getItem('products'));
    
    const handleClearLocalstorage = () => {
      localStorage.clear();
      location.reload();
      return false;
    }

    const handleCloseModal = (n) => {
      showModal.value = false;
      if(n) {
        handleClearLocalstorage();
      }
    }

    const handleToggleModal = () => {
      showModal.value = !showModal.value;
    }

    return {
      handleClearLocalstorage,
      handleCloseModal,
      handleToggleModal,
      showModal,
      mode,
      products
    }
  }
}
</script>

唯一ID:

let Id = 0;

export default () => {
    return Id++;
};

相关问题