7

I have a few plain Sweetalert2 modals in a Vue project. I want to use a custom component inside an alert. For example:

<template>
  <h1>Hello {{name}}</h1>
</template>
<script>
module.exorts: {
  props: ["name"]
}
</script>

my_template.vue

And, in my sweetalert modal:

swal({
  titleText: "Hi",
  html: '<my-template name="hello"></my-template>'
});

I'm not sure if this is even possible or how to do it.

angrykoala
  • 2,619
  • 4
  • 21
  • 36

3 Answers3

6

Technically it looks possible:

Vue.component('my-component', {
  template: '<div>A custom component!</div>'
})

new Vue({
  el: '#modal',
  beforeCreate:  swal({
    titleText: "Hi",
    html: '<div id="modal"><my-component></my-component></div>'
  })
})

But you may want to wrap it in a function. Take a look at my fiddle:

JS Fiddle

It's just an idea, for me it doesn't look good, but still working. Also I have to mention, that you will create new instance of vue every time you open your dialog this way.

Option 2 from comment to my answer:

Vue.component('my-component', {
    template: '<div>A custom component!</div>'
})    

swal({
    html: "<my-component></my-component>"
})
  
new Vue({
    el: swal.getHtmlContainer()
})  
 

Fiddle

Limon Monte
  • 44,025
  • 43
  • 163
  • 189
soeik
  • 867
  • 6
  • 14
  • 3
    or, even better: `html: ''`, and then `new Vue({ el: swal.getContent() })` – Limon Monte Feb 08 '18 at 12:01
  • @LimonMonte thanks for an idea. I've added it to my answer, if you don't mind. – soeik Feb 08 '18 at 12:19
  • @KirillStepanov in realistic situation, our apps are full of data binding everywhere. This solution is about creating a new Vue object and make it render $swal.html, but the data binding won't work.... :( By the way, Limon Monte is the owner of sweetalert2. – Justin Moh Apr 27 '18 at 18:01
  • @JustinMoh I agree, but we don't know context of question. I think passing properties in vue instance to render component is still possible. But I have no idea why not to use just template literal. Don't see any benefit in using vue for this. – soeik Apr 28 '18 at 15:19
  • 4
    @KirillStepanov what i'm currently using `html: '
    '`, then in `onBeforeOpen:`, use `let ComponentClass = Vue.extend(MyComponent); let instance = new ComponentClass({ propsData: { item: this.item } })` to extend the vue component in the context, then mount it onto dom `instance.$mount(); document.getElementById('swalHtml').appendChild(instance.$el)`.
    – Justin Moh Apr 29 '18 at 11:16
  • The second fiddle will only work if you change it to use "swal.fire({... – J.Kirk. Jul 31 '19 at 20:46
  • I'm trying to use a prop to pass a variable but it's not working.. – Atef Jan 22 '20 at 10:42
  • @Atef ensure you're using `propsData` and not props – Matt Wohler Apr 20 '20 at 15:48
0

I have managed to make it work as follows:

I include all the logic of the template between backticks: ` `

you will also need edit the vue.config.js file and add inside the configurewebpack object add this: 'vue$':'vue/dist/vue.esm.js'

configureWebpack: {
resolve: {
  alias: {
    'src': resolveSrc('src'),
    'chart.js': 'chart.js/dist/Chart.js',

    // add this line for include components inside swal alert
    'vue$':'vue/dist/vue.esm.js'
  }
 }
}

Once this is done you must relaunch the project "npm run dev"

This is my complete example, tested and working

openSweet() {
  Vue.component('my-comp', {
      template: `
            <div class="card-content">
              <div class="span2">
                    <div class="col-sm-6 col-md-2 col-lg-3">
                        <div class="row">
                          <div style="margin-top: 6px;" >
                            <p-switch v-model="switchTrip.state" type="primary" 
on-text="ON" off-text="OFF" style="justify-content:center"></p-switch>
                            <h5 class="card-title" style="margin-left: 
25px;">Recorridos</h5>
                          </div>
                        </div>

                        <div class="row">
                          <div style="margin-top: 6px;" >
                            <p-switch v-model="switchVel.state" type="primary" 
on-text="ON" off-text="OFF" style="justify-content:center"></p-switch>
                            <h5 class="card-title" style="margin-left: 
25px;">Velocidad</h5>
                          </div>
                        </div>

                    </div>
              </div>
              <div class="span2">
                    <div class="col-sm-6 col-md-4 col-lg-3">
                        <div class="row">
                          <div >
                            <input type="search" class="form-control input-sm" 
placeholder="km / h" v-model="vmax">
                            <h5 class="card-title">Vel. Max</h5>
                          </div>
                        </div>

                        <div class="row">
                          <div>
                            <input type="search" class="form-control input-sm" 
placeholder="minutos" v-model="tol">
                            <h5 class="card-title">Tolerancia</h5>
                          </div>
                        </div>
                    </div>
              </div>
            </div>
      `,
    data () {
      return {
        switchVel: {
          state: false
        },
        switchEvent: {
          state: false
        },
        switchTrip: {
          state: false
        },
        search: '',
        vmax: '',
        tol: ''
      }
    },
    components: {
        [Button.name]: Button,
        Card,
        PSwitch
    }
  })
  new Vue({
    el: '#modal',
    beforeCreate:  () => {
      swal({
        titleText: "Descarga de Reportes",
        showCancelButton: true,
        cancelButtonText: 'Cancelar',
        confirmButtonText: 'Descargar',
        // confirmButtonAriaLabel: 'glyphicon glyphicon-ok-sign',
        // cancelButtonAriaLabel: 'glyphicon glyphicon-remove-sign',
        confirmButtonColor: '#3085d6',
        cancelButtonColor: '#d33',
        width: 800,
        html: '<div id="modal"><my-comp></my-comp></div>'
      })
    }
  })
}

I hope it helps you

Regards

Ale DC
  • 577
  • 6
  • 11
0

You can render and hide the content inside your app:

<div id="swalHTML" style='display: none'>
  <my-template name="hello"></my-template>
</div>

Then pass the element's innerHTML to the alert:

let el = document.getElementById('swalHTML')
swal({
  html: el.innerHTML
});
opowell
  • 551
  • 4
  • 19