【问题标题】:Open bootstrap modal with vue.js 2.0使用 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

标签: modal-dialog vue.js twitter-bootstrap-4


【解决方案1】:

我将Vue.js Modal exampleBootstrap 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,所以只需用一些参数实例化您的组件,让它自己绘制主体。

          【讨论】:

            猜你喜欢
            • 2017-09-11
            • 1970-01-01
            • 2019-11-14
            • 2018-12-20
            • 1970-01-01
            • 2019-10-24
            • 2015-11-23
            • 1970-01-01
            • 2021-08-01
            相关资源
            最近更新 更多