使用 vue.js 2.0 打开引导模式

Posted

技术标签:

【中文标题】使用 vue.js 2.0 打开引导模式【英文标题】:Open bootstrap modal with vue.js 2.0 【发布时间】:2017-08-10 22:49:31 【问题描述】:

有人知道如何使用 vue 2.0 打开引导模式吗?在 vue.js 之前,我只需使用 jQuery 打开模式:$('#myModal').modal('show');

但是,我应该在 Vue 中执行此操作吗?

谢谢。

【问题讨论】:

你的模态是一个组件吗?还是只是模板的一部分? 嗨 - 它在它自己的 Vue 组件中 基本上还是 jQuery。 $(this.$el).modal('show')。如果您想完全消除 jQuery,还有很多工作要做,因为 Bootstrap 依赖于它。 你可以使用Bootstrap-Vue 【参考方案1】:

我将Vue.js Modal example 和Bootstrap 3.* live demo 合并。

基本上,我使用了 Vue.js 模态示例,但用引导模态 html 标记替换了(排序)Vue.js“html”部分,节省一件事(我认为)。我不得不从引导程序 3 中剥离外部的 div,然后它就可以工作了,瞧!

所以相关代码是关于引导程序的。只需从引导标记中剥离外部 div 即可。所以...

呃,开发人员的网站,我不能轻易粘贴代码?这对我来说一直是一个严重的问题。我是唯一一个?根据历史,我是个白痴,有一种简单的方法可以粘贴代码,请指教。每次我尝试时,充其量都是一种可怕的格式化黑客攻击。 如果需要,我将提供一个示例 jsfiddle,说明我是如何做到的。

【讨论】:

您可以发布带有缩进、反引号或嵌入小提琴的代码。 Here is a detailed reference。问题编辑器也有插入代码的按钮。【参考方案2】:

我的代码基于 Michael Tranchida 的回答。

引导 3 html:

<div id="app">
  <div v-if="showModal">
    <transition name="modal">
      <div class="modal-mask">
        <div class="modal-wrapper">
          <div class="modal-dialog">
            <div class="modal-content">
              <div class="modal-header">
                <button type="button" class="close" @click="showModal=false">
                  <span aria-hidden="true">&times;</span>
                </button>
                <h4 class="modal-title">Modal title</h4>
              </div>
              <div class="modal-body">
                modal body
              </div>
            </div>
          </div>
        </div>
      </div>
    </transition>
  </div>
  <button id="show-modal" @click="showModal = true">Show Modal</button>
</div>

引导程序 4 html:

<div id="app">
  <div v-if="showModal">
    <transition name="modal">
      <div class="modal-mask">
        <div class="modal-wrapper">
          <div class="modal-dialog" role="document">
            <div class="modal-content">
              <div class="modal-header">
                <h5 class="modal-title">Modal title</h5>
                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                  <span aria-hidden="true" @click="showModal = false">&times;</span>
                </button>
              </div>
              <div class="modal-body">
                <p>Modal body text goes here.</p>
              </div>
              <div class="modal-footer">
                <button type="button" class="btn btn-secondary" @click="showModal = false">Close</button>
                <button type="button" class="btn btn-primary">Save changes</button>
              </div>
            </div>
          </div>
        </div>
      </div>
    </transition>
  </div>
  <button @click="showModal = true">Click</button>
</div>

js:

new Vue(
  el: '#app',
  data: 
    showModal: false
  
)

css:

.modal-mask 
  position: fixed;
  z-index: 9998;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, .5);
  display: table;
  transition: opacity .3s ease;


.modal-wrapper 
  display: table-cell;
  vertical-align: middle;

在jsfiddle

【讨论】:

如何使用 BS 3 溢出? 为了防止表单提交,将 preventDefault() 添加到按钮。 &lt;button @click.prevent="showModal = true"&gt;Click&lt;/button&gt;。为防止加载时模态闪烁,请添加 v-cloak:&lt;div v-if="showModal" v-cloak&gt; 和在 css 内:[v-cloak] display:none; 优秀的答案。只是要补充一点,对于 Bootstrap 4,关键是根本没有添加类“模态”div。它将对话框拉出屏幕,似乎需要 JS 来打开它,这当然可以在 mounted() 事件中完成。尽管如此,这种方法似乎太复杂了,我发现这个解决方案更干净。一,实际上可以跳过所有内容,直到&lt;div class="modal-dialog" ...&gt;,因为我们自己使用 Vue 显示组件。除非,当然你想要一些花哨的过渡。 我试过了,它似乎没有播放过渡动画。我也试过the example from the Vue 3 docs,它也不播放过渡动画。我对 CSS 过渡不是很有经验。我研究了导入 Bootstrap javascript 模块(以及 jQuery 和 Popper),嗯,对于这么简单的东西来说,实在是太臃肿了。 (实际上我几乎可以这么说 Vue。几乎......)去他妈的!对话框会立即出现和消失。 好的,我解决了。在使用 &lt;transition&gt; 元素时,您实际上需要定义一个过渡(现在我想起来这听起来很明显!)。见the docs。您还需要将v-if 放在&lt;transition&gt; 中。【参考方案3】:

modal doc

Vue.component('modal', 
      template: '#modal-template'
    )

    // start app
    new Vue(
      el: '#app',
      data: 
        showModal: false
      
    )

<script type="text/x-template" id="modal-template">
  <transition name="modal">
    <div class="modal-mask">
      <div class="modal-wrapper">
        <div class="modal-container">

          <div class="modal-header">
            <slot name="header">
              default header
            </slot>
          </div>

          <div class="modal-body">
            <slot name="body">
              default body
            </slot>
          </div>

          <div class="modal-footer">
            <slot name="footer">
              default footer
              <button class="modal-default-button" @click="$emit('close')">
                OK
              </button>
            </slot>
          </div>
        </div>
      </div>
    </div>
  </transition>
</script>

<!-- app -->
<div id="app">
  <button id="show-modal" @click="showModal = true">Show Modal</button>
  <!-- use the modal component, pass in the prop -->
  <modal v-if="showModal" @close="showModal = false">
    <!--
      you can use custom content here to overwrite
      default content
    -->
    <h3 slot="header">custom header</h3>
  </modal>
</div>

【讨论】:

【参考方案4】:

试图写一段代码,使用 VueJS 的转场来操作原生的 Bootsrap 动画。

HTML:

    <div id="exampleModal">
      <!-- Button trigger modal-->
      <button class="btn btn-primary m-5" type="button" @click="showModal = !showModal">Launch demo modal</button>
      <!-- Modal-->
      <transition @enter="startTransitionModal" @after-enter="endTransitionModal" @before-leave="endTransitionModal" @after-leave="startTransitionModal">
        <div class="modal fade" v-if="showModal" ref="modal">
          <div class="modal-dialog" role="document">
            <div class="modal-content">
              <div class="modal-header">
                <h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
                <button class="close" type="button" @click="showModal = !showModal"><span aria-hidden="true">×</span></button>
              </div>
              <div class="modal-body">...</div>
              <div class="modal-footer">
                <button class="btn btn-secondary" @click="showModal = !showModal">Close</button>
                <button class="btn btn-primary" type="button">Save changes</button>
              </div>
            </div>
          </div>
        </div>
      </transition>
      <div class="modal-backdrop fade d-none" ref="backdrop"></div>
    </div>

Vue.JS:

    var vm = new Vue(
      el: "#exampleModal",
      data: 
        showModal: false,
      ,
      methods: 
        startTransitionModal() 
          vm.$refs.backdrop.classList.toggle("d-block");
          vm.$refs.modal.classList.toggle("d-block");
        ,
        endTransitionModal() 
          vm.$refs.backdrop.classList.toggle("show");
          vm.$refs.modal.classList.toggle("show");
        
      
    );

Example on Codepen 如果您不熟悉 Pug,请在 HTML 部分的下拉窗口中单击 查看编译的 HTML

这是 Modals 如何在 Bootstrap 中工作的基本示例。如果有人将其用于一般用途,我将不胜感激。

有一个很棒的代码?!

【讨论】:

【参考方案5】:

来自https://getbootstrap.com/docs/4.0/getting-started/javascript/#programmatic-api

$('#myModal').modal('show')

您可以通过 Vue 方法执行此操作,并且效果很好。

【讨论】:

您的回答遭到反对,但没有给出任何理由。我在网络应用程序中使用您的答案,我的印象是打开这样的对话框会导致内存泄漏。但我没有确凿的证据证明这一点。如果有人能解释为什么这个答案不起作用,那就太好了。 仅使用程序化 API 不会导致内存泄漏。此处的调用将仅显示附加到元素的现有模式。但是,如果它在您使用的代码中不起作用,您可能会遇到由于 Vue 重新渲染而导致模式最初附加到的 DOM 元素不再存在的问题。这是使用传统 Javascript 和基于 jQuery 的库的主要问题:它们依赖于 DOM 元素不会一直在变化,这对于像 Vue 这样的响应式框架来说是一个无效的假设。 如果 Bootstrap 能适应响应式框架就好了,Vue 不是唯一的,甚至不是最流行的。 这可能会被否决,因为它是 jquery 而不是 vue。 Bootstrap 4 是用 jQuery 实现的。该 jQuery 代码直接来自 Bootstrap 文档。【参考方案6】:

使用$nextTick() 函数对我有用。它只是等到 Vue 更新了 DOM,然后显示模态:

HTML

<div v-if="is_modal_visible" id="modal" class="modal fade">...</div>

JS


  data: 
    isModalVisible: false,
  ,
  methods: 
    showModal() 
      this.isModalVisible = true;
      this.$nextTick(() => 
        $('#modal').modal('show');
      );
    
  ,

【讨论】:

【参考方案7】:

这是打开 Bootstrap 模式的 Vue 方式..

引导程序 5

既然 Bootstrap 5 不再需要 jQuery,那么模块化地使用 Bootstrap 模态组件就很容易了。您可以简单地使用 data-bs 属性,或者像这样创建一个 Vue 包装器组件...

<bs-modal id="theModal">
        <button class="btn btn-info" slot="trigger"> Bootstrap modal </button>
        <div slot="target" class="modal" tabindex="-1">
                <div class="modal-dialog">
                    <div class="modal-content">
                        <div class="modal-header">
                            <h5 class="modal-title">Modal title</h5>
                            <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                        </div>
                        <div class="modal-body">
                            <p>Modal body text goes here.</p>
                        </div>
                        <div class="modal-footer">
                            <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
                            <button type="button" class="btn btn-primary">Save changes</button>
                        </div>
                    </div>
                </div>
        </div>
</bs-modal>


const  Modal  = bootstrap

const modal = Vue.component('bsModal', 
    template: `
        <div>
            <slot name="trigger"></slot>
            <slot name="target"></slot>
        </div>
    `,
    mounted() 
        var trigger = this.$slots['trigger'][0].elm
        var target = this.$slots['target'][0].elm
        trigger.addEventListener('click',()=>
            var theModal = new Modal(target, )
            theModal.show()
        )
    ,
)

Bootstrap 5 Modal in Vue Demo

引导程序 4

Bootstrap 4 JS 组件需要 jQuery,但在 Vue 组件中使用 jQuery 不是必需的(或可取的)。而是使用 Vue 操作 DOM...

   <a href="#reject" role="button" class="btn btn-primary" @click="toggle()">Launch modal</a>
   <div :class="modalClasses" class="fade" id="reject" role="dialog">
       <div class="modal-dialog">
             <div class="modal-content">
                  <div class="modal-header">
                    <h4 class="modal-title">Modal</h4>
                    <button type="button" class="close" @click="toggle()">&times;</button>
                  </div>
                  <div class="modal-body"> ... </div>
              </div>
       </div>
   </div>

var vm = new Vue(
  el: '#app',
  data () 
    return 
        modalClasses: ['modal','fade'],
    
  ,
  methods: 
    toggle() 
        document.body.className += ' modal-open'
        let modalClasses = this.modalClasses
    
        if (modalClasses.indexOf('d-block') > -1) 
            modalClasses.pop()
            modalClasses.pop()
    
            //hide backdrop
            let backdrop = document.querySelector('.modal-backdrop')
            document.body.removeChild(backdrop)
        
        else 
            modalClasses.push('d-block')
            modalClasses.push('show')
    
            //show backdrop
            let backdrop = document.createElement('div')
            backdrop.classList = "modal-backdrop fade show"
            document.body.appendChild(backdrop)
        
    
  
)

Bootstrap 4 Vue Modal Demo

【讨论】:

【参考方案8】:

我的首要任务是继续使用 Bootstrap 代码,因为他们努力使模式工作,修复滚动条等等。我发现现有的提案试图模仿这一点,但它们只是完成了一部分。我什至不想碰运气:我只想使用实际的引导代码。

另外,我想要一个程序界面,例如调用dialog.show(gimme plenty of parameters here),而不仅仅是在某处切换变量(即使该变量可能是一个复杂对象)。

我还想为实际的对话框内容提供 Vue 的反应性和组件渲染。

要解决的问题是,如果 Vue 发现组件的 DOM 被外部操纵,它会拒绝合作。因此,基本上,我将声明模态本身的外部 div 移出组件并注册了组件,以便我还可以通过程序访问对话框。

这样的代码是可能的:

window.mydialog.yesNo('Question', 'Do you like this dialog?')

解决方案。

main.html(基本上只是包装我们组件的外部 div):

<div class="modal fade" id="df-modal-handler" tabindex="-1" role="dialog" aria-hidden="true">
  <df-modal-handler/>
</div>

component-template.html(模态的其余部分):

<script type="text/x-template" id="df-modal-handler-template">
  <div :class="'modal-dialog ' + sizeClass" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title"> title </h5>
        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body" v-html="body"/>
      <div class="modal-footer">
        <button type="button" v-for="button in buttons" :class="button.classes" v-bind="button.arias"
                @click.stop="buttonClick(button, callback)"> button.text 
        </button>
      </div>
    </div>
  </div>
</script>

component-def.js - 包含显示和操作对话框的逻辑,还支持对话框堆栈以防您出错并按顺序调用两个对话框:

  Vue.component('df-modal-handler', 
    template: `#df-modal-handler-template`,
    props: ,
    data() 
      return 
        dialogs: [],
        initialEventAssignDone: false,
      ;
    ,
    computed: 
      bootstrapDialog()  return document.querySelector('#df-modal-handler'); ,
      currentDialog()  return this.dialogs.length ? this.dialogs[this.dialogs.length - 1] : null; ,
      sizeClass() 
        let dlg = this.currentDialog;
        if (!dlg) return 'modal-sm';
        if (dlg.large || ['large', 'lg', 'modal-lg'].includes(dlg.size)) return 'modal-lg';
        else if (dlg.small || ['small', 'sm', 'modal-sm'].includes(dlg.size)) return 'modal-sm';
        return '';
      ,
      title()  return this.currentDialog ? this.currentDialog.title : 'No dialogs to show!'; ,
      body()  return this.currentDialog ? this.currentDialog.body : 'No dialogs have been invoked'; ,
      callback()  return this.currentDialog ? this.currentDialog.callback : null; ,
      buttons() 
        const self = this;
        let res = this.currentDialog && this.currentDialog.buttons ? this.currentDialog.buttons : [close: 'default'];
        return res.map(value => 
          if (value.close == 'default') value = 
            text: 'Close',
            classes: 'btn btn-secondary',
            data_return: 'close'
          ;
          else if (value.yes == 'default') value = 
            text: 'Yes',
            classes: 'btn btn-primary',
            data_return: 'yes'
          ;
          else if (value.no == 'default') value = 
            text: 'No',
            classes: 'btn btn-secondary',
            data_return: 'no'
          ;
          value.arias = value.arias || ;
          let clss = (value.classes || '').split(' ');
          if (clss.indexOf('btn') == -1) clss.push('btn');
          value.classes = clss.join(' ');
          return value;
        );
      ,
    ,
    created() 
      // make our API available
      window.mydialog = this;
    ,
    methods: 
      show: function show() 
        const self = this;
        if (!self.initialEventAssignDone) 
          // created is too soon. if we try to do this there, the dialog won't even show.
          self.initialEventAssignDone = true;
          $(self.bootstrapDialog).on('hide.bs.modal', function (event) 
            let callback = null;
            if (self.dialogs.length) callback = self.dialogs.pop().callback;
            if (self.dialogs.length) event.preventDefault();
            if (callback && callback.df_called !== true) callback(null);
          );
        
        $(self.bootstrapDialog).modal('show');
      ,
      hide: function hide() 
        $(this.bootstrapDialog).modal('hide');
      ,
      buttonClick(button, callback) 
        if (callback)  callback(button.data_return); callback.df_called = true; 
        else console.log(button);
        this.hide();
      ,
      yesNo(title, question, callback) 
        this.dialogs.push(
          title: title, body: question, buttons: [yes: 'default', no: 'default'], callback: callback
        );
        this.show();
      ,
    ,
  );

请注意,此解决方案会在 DOM 中创建一个单独的对话框实例,并重复使用该实例来满足您的所有对话框需求。 (还)没有过渡,所以当有多个活动对话框时,用户体验不是很好。无论如何,这是不好的做法,但我希望它涵盖,因为你永远不知道......

对话框主体实际上是一个 v-html,所以只需用一些参数实例化您的组件,让它自己绘制主体。

【讨论】:

以上是关于使用 vue.js 2.0 打开引导模式的主要内容,如果未能解决你的问题,请参考以下文章

无法 Vue.Js 引导模式

在 vue.js 的弹出模式中显示特定数据

如何使用 link_to 添加引导模式,以便链接内容以模式打开?

Vue.js 路由器:历史模式和 AWS S3 (RoutingRules)

如何在 Laravel 中将 Vue.js 切换到生产模式?

如何检查引导模式是不是打开,以便我可以使用 jquery 验证?