Is it possible to use Vue-sweetalert2 html as vue component? - vue.js

I use this package https://www.npmjs.com/package/vue-sweetalert2 and I want to past vue component in Vue.swal as html.
<template>
<div class="w-full">
<div class="py-6 w-full">
<button
type="button"
class="btn btn-default btn-primary"
#click="openTeamsModal"
>
Teams
</button>
</div>
</div>
</template>
<script>
import Vue from 'vue';
import TeamTable from "./TeamTable";
import VueSweetalert2 from 'vue-sweetalert2';
Vue.use(VueSweetalert2);
export default {
components: {TeamTable},
props: [
'resourceName',
'resourceId',
'field'
],
data: () => ({
teams: [],
}),
methods: {
openTeamsModal() {
Nova.request().get(`/api/competitors/${this.field.seasonId}/${this.field.championshipId}`).then( responce => {
console.log(responce)
});
Vue.swal({
title: 'Test',
html: '<team-table></team-table>',
});
}
},
}
</script>
but there is nothing. I am new to VueJs and I still don’t fully understand how to insert my components as html.

Yes! is possible! ... after hours of research, I have succeeded.
this is the way:
you need to include all the logic of the template between this characters ` `
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
Greetings from Argentina
https://github.com/aledc7

Related

Cloning div with vue

<template>
<b-form #submit.prevent="Submit" class="mb-5">
<div class="inputArea" v-for="input in inputs" :key="input.id">
<b-form-group label-cols-sm="2" label="Solution (EN)">
<ckeditor :editor="ckeditor" v-model="form.body.en" :config="ckeditorConfig"></ckeditor>
<div v-if="errors['body.en']">
<div v-for="err in errors['body.en']" :key="err">
<small class="text-danger">{{ err }}</small>
</div>
</div>
</b-form-group>
<b-form-group label-cols-sm="2" label="Body (FR)">
<ckeditor :editor="ckeditor" v-model="form.body.np" :config="ckeditorConfig"></ckeditor>
<div v-if="errors['body.np']">
<div v-for="err in errors['body.np']" :key="err">
<small class="text-danger">{{ err }}</small>
</div>
</div>
</b-form-group>
<b-form-group label-cols-sm="2" label="Body (IT)">
<ckeditor :editor="ckeditor" v-model="form.body.in" :config="ckeditorConfig"></ckeditor>
<div v-if="errors['body.in']">
<div v-for="err in errors['body.in']" :key="err">
<small class="text-danger">{{ err }}</small>
</div>
</div>
</b-form-group>
</div>
<b-form-group label="" label-cols-sm="2">
<b-button type="button" class="text-white" variant="dark" #click="addRow">Add row</b-button>
</b-form-group>
<b-form-group label="" label-cols-sm="2">
<b-button type="submit" class="text-white" variant="dark">Submit</b-button>
</b-form-group>
</b-form>
</template>
<style lang="scss">
</style>
<script>
import CKEditor from '#ckeditor/ckeditor5-vue2'
import ClassicEditor from '#ckeditor/ckeditor5-build-classic'
export default {
name: 'Interaction',
components: {
ckeditor: CKEditor.component
},
data(){
return{
counter: 0,
inputs: [
{
en: '',
np: '',
in: '',
}],
form: {
body: [
{
en: '',
np: '',
in: '',
}
],
},
errors: {},
ckeditorData: '<p></p>',
ckeditorConfig: {
// The configuration of the editor
},
ckeditor: ClassicEditor
}
},
methods: {
Submit(){
this.storing = true
this.errors = {}
var self = this
axios.post('/this-is-a-post-url', this.form)
.then(function(response){
console.log(response)
})
},
addRow() {
this.inputs.push({
en: '',
it: '',
fr: '',
id: `${++this.counter}`,
value: '',
});
}
}
}
</script>
I will have array coming in the body name so I am trying to to clone the clone body on a click of a button which has a function AddRow. I want to clone the three fields en,np,in and I want it work like normal html works in this. Example when we clone html form it create input field like so <input name="body['en'][0]"> and when we clone another time it creates something like this <input name="body['en'][1]">.
I have the above code, it clones the body but it also clones the added text before cloning. I want to add an empty field while cloning and also want to update v-model. How can I do that?
Refer below example:
https://codepen.io/telen/pen/OeNZVV
<main class="container">
<form id="app" data-apartments='[{ "price": "23000", "rooms": "12" }, { "price": "42000", "rooms": "32" }]'>
<h1>
Dynamic apartment forms
</h1>
<hr>
<div class="row">
<div class="col-xs-2">
<button type="button" v-on:click="addNewApartment" class="btn btn-block btn-success">
Add +
</button>
</div>
<div class="col-xs-10">
Would you like add more apartments?
</div>
</div>
<div v-for="(apartment, index) in apartments">
<div class="row">
<div class="col-xs-2">
<label> </label>
<button type="button" v-on:click="removeApartment(index)" class="btn btn-block btn-danger">
Rem -
</button>
</div>
<div class="form-group col-xs-5">
<label>Price (HUF)</label>
<input v-model="apartment.price" type="number"
name="apartments[][price]" class="form-control" placeholder="Price">
</div>
<div class="form-group col-xs-5">
<label>Rooms (PCS)</label>
<input v-model="apartment.rooms" type="number"
name="apartments[][rooms]" class="form-control" placeholder="Rooms">
</div>
</div>
</div>
<div class="row">
<div class="col-xs-2">
<button type="submit" v-on:click.prevent="sumbitForm" class="btn btn-block btn-primary">
Submit
</button>
</div>
<div class="col-xs-10">
Open the console (F12) and see the result
</div>
</div>
<hr>
<pre>{{ $data }}</pre>
</form>
JS:
window.app = new Vue({
el: '#app',
data: {
apartment: {
price: '',
rooms: ''
},
apartments: [],
},
mounted: function () {
/*
* The "data-apartments" could come from serverside (already saved apartments)
*/
this.apartments = JSON.parse(this.$el.dataset.apartments)
},
methods: {
addNewApartment: function () {
this.apartments.push(Vue.util.extend({}, this.apartment))
},
removeApartment: function (index) {
Vue.delete(this.apartments, index);
},
sumbitForm: function () {
/*
* You can remove or replace the "submitForm" method.
* Remove: if you handle form sumission on server side.
* Replace: for example you need an AJAX submission.
*/
console.info('<< Form Submitted >>')
console.info('Vue.js apartments object:', this.apartments)
window.testSumbit()
}
}
})
/*
* This is not Vue.js code, just a bit of jQuery to test what data would be submitted.
*/
window.testSumbit = function () {
if (!window.jQuery) {
console.warn('jQuery not present!')
return false
}
console.info('Submitted (serverside) array:', jQuery('form').serializeJSON())
}

I made the form a component, it disappeared with Vue.js

overview
Currently, the created new.vue and edit.vue have similar form parts, so I would like to make them common and componentized.
Therefore, I would like to create a new form.vue and display the page in the form of calling it.
However, when I made it into a component, the page disappeared.
(There is no such description in the log, and there is no error display in Console)
I think the data transfer isn't working, but I'm not sure where to fix it.
I would appreciate it if you could tell me how to fix it.
Original code before componentization
New.vue
<template>
<main>
<form>
<section>
<div>
<div>
<fieldset>
<div class="form-row">
<div class="form-group col-3">
<label>タイトル</label>
<input v-model="latest_information.title" type="text">
</div>
<div class="form-group col-3">
<label>詳細</label>
<input v-model="latest_information.detail" type="text">
</div>
</div>
</fieldset>
</div>
</div>
</section>
<div class="btn-container d-flex justify-content-center">
<button class="button-square btn-send" type="button" #click="createLatestInformation">保存する</button>
</div>
</form>
</main>
</template>
<script>
export default {
data() {
return {
latest_information: {
title: '',
detail: '',
},
}
},
methods: {
createLatestInformation() {
this.$loading.load(this.$auth.api.post('admin/latest_informations/', {
latest_information: this.latest_information
})
.then(response => {
this.$router.push({name: 'AdminLatestInformationIndex'})
}))}
},
}
</script>
Code after componentization (not behaving well)
New.vue
<template>
<form :latest_information="latest_information" #click="createLatestInformation"></form>
</template>
<script>
import Form from './Form.vue';
export default {
components:{
Form
},
data() {
return {
latest_information: {
title: '',
detail: '',
},
}
},
methods: {
createLatestInformation() {
this.$loading.load(this.$auth.api.post('admin/latest_informations/', {
latest_information: this.latest_information
})
.then(response => {
this.$router.push({name: 'AdminLatestInformationIndex'})
}))}
},
}
</script>
Form.vue
<template>
<main>
<form>
<section>
<div>
<div>
<fieldset>
<div class="form-row">
<div class="form-group col-3">
<label>タイトル</label>
<input v-model="latest_information.title" type="text">
</div>
<div class="form-group col-3">
<label>詳細</label>
<input v-model="latest_information.detail" type="text">
</div>
</div>
</fieldset>
</div>
</div>
</section>
<div class="btn-container d-flex justify-content-center">
<button class="button-square btn-send" type="button" #click="$emit('click')">保存する</button>
</div>
</form>
</main>
</template>
<script>
export default {
props: {
latest_information: {
title: '',
detail: '',
},
}
}
</script>
<style>
</style>
Environment
rails 6
vue#2.6.10
form was a reserved word.Changed to tform.
<template>
<tform :latest_information="latest_information" #click="createLatestInformation"></form>
</template>
<script>
import Form from './Form.vue';
export default {
components:{
Tform: Form
},

Vue.js: How to change booleanfield in specific object from false to true #click in state with mutations and server with actions axios?

I want to change the boolean from false to true from one object(todo), when I do #click="updateTodoItem(todo.id, todo.title, todo.body)
thats how 1 object looks: {id: 1, title: "", body: "", done: false}
UPDATE (finished):
I figured it out and updated code here. I can only pass done one argument as payload to the store actions.
Home.vue
<template>
<div class="container">
<section class="hero is-link">
<div class="hero-body">
<div class="container">
<h1 class="title">
Hello Dudes
</h1>
<h2 class="subtitle">
Hamster ToDo List
</h2>
</div>
</div>
</section>
<section class="section">
<nav class="level">
<div class="level-item has-text-centered">
<div>
<p class="heading">All Tasks</p>
<p class="title">{{getTotalTodos}}</p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="heading">Tasks has to be done</p>
<p class="title">{{getTodosFalse}}</p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="heading">Tasks finished</p>
<p class="title">{{getTodosTrue}}</p>
</div>
</div>
<!-- <div class="level-item has-text-centered">
<div>
<p class="heading">Likes</p>
<p class="title">789</p>
</div>
</div> -->
</nav>
</section>
<section class="section">
<div class="columns">
<div class="column is-half">
<strong>Add ToDo</strong>
<div class="control">
<input class="input is-info" v-model="todoTitle" type="text" placeholder="Add Task Title">
<input class="input is-info" v-model="todoBody" type="text" placeholder="Add Task Body">
<button class="button is-link" #click="addTodoItem({title: todoTitle, body: todoBody, done: false}); empyInput()">Add</button>
</div>
</div>
</div>
<div class="tabs is-small">
<ul>
<li class="is-active"><a>All Tasks </a></li> <span class="tag is-link">{{getTotalTodos}}</span>
<li><a>Tasks still not done</a></li><span class="tag is-link">{{getTodosFalse}}</span>
<li><a>Finished Tasks</a></li><span class="tag is-link">{{getTodosTrue}}</span>
<!-- <li><a>Documents</a></li> -->
</ul>
</div>
<article class="media" v-for="todo in todoItems" :key="todo.id">
<figure class="media-left">
<!-- <p class="image is-64x64">
<img src="https://bulma.io/images/placeholders/128x128.png">
</p> -->
</figure>
<div class="media-content">
<div class="content">
<p>
<strong>{{todo.title}}</strong>
<br>
{{todo.body}}
</p>
</div>
<nav class="level is-mobile">
<div class="level-left">
<a class="level-item">
<span class="icon is-small"><i class="fas fa-edit"></i></span>
</a>
<a class="level-item" v-if="todo.done==false" #click="updateTodoToTrue(todo.id)">
<span class="icon is-small"><i class="fas fa-check"></i></span>
</a>
<a class="level-item" v-if="todo.done==true" #click="updateTodoToFalse(todo.id)">
<span class="icon is-small"><i class="fas fa-undo"></i></span>
</a>
<a class="level-item" #click="deleteTodoItem(todo.id)">
<span class="icon is-small"><i class="fas fa-trash"></i></span>
</a>
</div>
</nav>
</div>
<div class="media-right">
<button class="delete" #click="deleteTodoItem(todo.id)"></button>
</div>
</article>
</section>
</div>
</template>
<script>
// # is an alias to /src
import {mapGetters} from 'vuex';
import {mapActions} from 'vuex';
export default {
name: 'Home',
data() {
return{
todoTitle: '',
todoBody: '',
}
},
// updated() {
// this.$store.dispatch('getTodoItems');
// },
computed: {
...mapGetters(['todoItems', 'getTotalTodos', 'getTodosTrue', 'getTodosFalse'])
},
methods: {
...mapActions(['addTodoItem', 'deleteTodoItem', 'updateTodoToTrue', 'updateTodoToFalse']),
// addTodo() {
// return this.$store.actions.addTodoItem;
empyInput() {
this.todoTitle = '';
this.todoBody = '';
}
}
}
</script>
store
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios';
Vue.use(Vuex)
export default new Vuex.Store({
state: {
todoItems: []
},
// Keep in mind that response is an HTTP response
// returned by the Promise.
// The mutations are in charge of updating the client state.
mutations: {
UPDATE_TODO_ITEMS (state, payload) {
state.todoItems = payload;
},
ADD_TODO_ITEMS (state, payload) {
state.todoItems.push(payload)
},
DELETE_TODO_ITEMS (state, payload) {
state.todoItems.splice(payload, 1)
},
changeTodoStatus: (state, payload) => {
let index = state.todoItems.findIndex(el => {
return el.id == payload.id
})
state.todoItems[index].done = payload.done
}
},
actions: {
getTodoItems ({ commit }) {
axios.get('http://127.0.0.1:8000/api/').then((response) => {
commit('UPDATE_TODO_ITEMS', response.data)
});
},
addTodoItem ({ commit }, todoItem) {
axios.post('http://127.0.0.1:8000/api/', todoItem).then((response) => {
commit('ADD_TODO_ITEMS', response.data)
});
},
deleteTodoItem ({ commit }, todoItemId) {
axios.delete('http://127.0.0.1:8000/api/' + todoItemId + '/').then((response) => {
commit('DELETE_TODO_ITEMS', response.data)
});
},
updateTodoToTrue ({ commit }, todoItemId) {
var payload = {
done: true
};
axios.patch('http://127.0.0.1:8000/api/' + todoItemId + '/', payload).then((response) => {
commit('changeTodoStatus', response.data)
});
},
updateTodoToFalse ({ commit }, todoItemId) {
var payload = {
done: false
};
axios.patch('http://127.0.0.1:8000/api/' + todoItemId + '/', payload).then((response) => {
commit('changeTodoStatus', response.data)
});
},
},
getters: {
todoItems: state => state.todoItems,
getTotalTodos: state => state.todoItems.length,
getTodosTrue: state => state.todoItems.filter(todo => todo.done).length,
getTodosFalse: state => state.todoItems.filter(todo => !todo.done).length
},
modules: {
}
})
My problem was that I tried to pass down 3 arguments to the actions payload in the store. The action accepts accepts only 1 argument which is the payload. I changed the axios method from PUT to PATCH.
Home.vue
<template>
<div class="container">
<section class="hero is-link">
<div class="hero-body">
<div class="container">
<h1 class="title">
Hello Dudes
</h1>
<h2 class="subtitle">
Hamster ToDo List
</h2>
</div>
</div>
</section>
<section class="section">
<nav class="level">
<div class="level-item has-text-centered">
<div>
<p class="heading">All Tasks</p>
<p class="title">{{getTotalTodos}}</p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="heading">Tasks has to be done</p>
<p class="title">{{getTodosFalse}}</p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="heading">Tasks finished</p>
<p class="title">{{getTodosTrue}}</p>
</div>
</div>
<!-- <div class="level-item has-text-centered">
<div>
<p class="heading">Likes</p>
<p class="title">789</p>
</div>
</div> -->
</nav>
</section>
<section class="section">
<div class="columns">
<div class="column is-half">
<strong>Add ToDo</strong>
<div class="control">
<input class="input is-info" v-model="todoTitle" type="text" placeholder="Add Task Title">
<input class="input is-info" v-model="todoBody" type="text" placeholder="Add Task Body">
<button class="button is-link" #click="addTodoItem({title: todoTitle, body: todoBody, done: false}); empyInput()">Add</button>
</div>
</div>
</div>
<div class="tabs is-small">
<ul>
<li class="is-active"><a>All Tasks </a></li> <span class="tag is-link">{{getTotalTodos}}</span>
<li><a>Tasks still not done</a></li><span class="tag is-link">{{getTodosFalse}}</span>
<li><a>Finished Tasks</a></li><span class="tag is-link">{{getTodosTrue}}</span>
<!-- <li><a>Documents</a></li> -->
</ul>
</div>
<article class="media" v-for="todo in todoItems" :key="todo.id">
<figure class="media-left">
<!-- <p class="image is-64x64">
<img src="https://bulma.io/images/placeholders/128x128.png">
</p> -->
</figure>
<div class="media-content">
<div class="content">
<p>
<strong>{{todo.title}}</strong>
<br>
{{todo.body}}
</p>
</div>
<nav class="level is-mobile">
<div class="level-left">
<a class="level-item">
<span class="icon is-small"><i class="fas fa-edit"></i></span>
</a>
<a class="level-item" v-if="todo.done==false" #click="updateTodoToTrue(todo.id)">
<span class="icon is-small"><i class="fas fa-check"></i></span>
</a>
<a class="level-item" v-if="todo.done==true" #click="updateTodoToFalse(todo.id)">
<span class="icon is-small"><i class="fas fa-undo"></i></span>
</a>
<a class="level-item" #click="deleteTodoItem(todo.id)">
<span class="icon is-small"><i class="fas fa-trash"></i></span>
</a>
</div>
</nav>
</div>
<div class="media-right">
<button class="delete" #click="deleteTodoItem(todo.id)"></button>
</div>
</article>
</section>
</div>
</template>
<script>
// # is an alias to /src
import {mapGetters} from 'vuex';
import {mapActions} from 'vuex';
export default {
name: 'Home',
data() {
return{
todoTitle: '',
todoBody: '',
}
},
// updated() {
// this.$store.dispatch('getTodoItems');
// },
computed: {
...mapGetters(['todoItems', 'getTotalTodos', 'getTodosTrue', 'getTodosFalse'])
},
methods: {
...mapActions(['addTodoItem', 'deleteTodoItem', 'updateTodoToTrue', 'updateTodoToFalse']),
// addTodo() {
// return this.$store.actions.addTodoItem;
empyInput() {
this.todoTitle = '';
this.todoBody = '';
}
}
}
</script>
STORE
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios';
Vue.use(Vuex)
export default new Vuex.Store({
state: {
todoItems: []
},
// Keep in mind that response is an HTTP response
// returned by the Promise.
// The mutations are in charge of updating the client state.
mutations: {
UPDATE_TODO_ITEMS (state, payload) {
state.todoItems = payload;
},
ADD_TODO_ITEMS (state, payload) {
state.todoItems.push(payload)
},
DELETE_TODO_ITEMS (state, payload) {
state.todoItems.splice(payload, 1)
},
changeTodoStatus: (state, payload) => {
let index = state.todoItems.findIndex(el => {
return el.id == payload.id
})
state.todoItems[index].done = payload.done
}
},
actions: {
getTodoItems ({ commit }) {
axios.get('http://127.0.0.1:8000/api/').then((response) => {
commit('UPDATE_TODO_ITEMS', response.data)
});
},
addTodoItem ({ commit }, todoItem) {
axios.post('http://127.0.0.1:8000/api/', todoItem).then((response) => {
commit('ADD_TODO_ITEMS', response.data)
});
},
deleteTodoItem ({ commit }, todoItemId) {
axios.delete('http://127.0.0.1:8000/api/' + todoItemId + '/').then((response) => {
commit('DELETE_TODO_ITEMS', response.data)
});
},
updateTodoToTrue ({ commit }, todoItemId) {
var payload = {
done: true
};
axios.patch('http://127.0.0.1:8000/api/' + todoItemId + '/', payload).then((response) => {
commit('changeTodoStatus', response.data)
});
},
updateTodoToFalse ({ commit }, todoItemId) {
var payload = {
done: false
};
axios.patch('http://127.0.0.1:8000/api/' + todoItemId + '/', payload).then((response) => {
commit('changeTodoStatus', response.data)
});
},
},
getters: {
todoItems: state => state.todoItems,
getTotalTodos: state => state.todoItems.length,
getTodosTrue: state => state.todoItems.filter(todo => todo.done).length,
getTodosFalse: state => state.todoItems.filter(todo => !todo.done).length
},
modules: {
}
})

Cannot use 'in' operator to search for 'xxxxx' in undefined in vue.js

I'm trying to create a modal form that will add a record. I am able to display the default values from data but as soon as I try to modify the field, I get the following error whenever I try to type changes to the input box.
*vue.min.js:6 TypeError: Cannot use 'in' operator to search for 'fullname' in undefined
at a.ke [as $set] (vue.min.js:6)
at input (eval at Ya (vue.min.js:1), <anonymous>:3:2182)
at He (vue.min.js:6)
at HTMLInputElement.n (vue.min.js:6)
at HTMLInputElement.Yr.o._wrapper (vue.min.js:6)*
In given Below I added the code of the component I'm trying to create:
Any help, please.
var bus = new Vue();
Vue.component('leagues_add', {
props: {
show: Boolean,
is_admin: Boolean,
},
data: function () {
return {
newLeague: {"fullname":"a", "notes":"b", "group_image_path": "c"} // remember to always enclose the fieldnames in doublequotes
}
},
methods: {
closeModal() {
this.show = false;
},
showModal() {
this.show = true;
},
addLeague() {
event.preventDefault();
var formData = this.toFormData(this.newLeague);
axios.get("http://"+ window.location.hostname +"/db/leagues/index.php?action=create").then(function(response){
if (response.data.error) {
app.errorMessage = response.data.message;
} else {
app.leagues = response.data.leagues;
}
});
},
toFormData(obj) {
var fd = new FormData();
for (var i in obj) {
fd.append(i, obj[i]);
}
return fd;
},
}
,
template:
`
<!-- Add new Leage -->
<div>
<div class="text-center pb-3" >
<button type="button" class="btn btn-outline-primary bg-success text-white" #click="showModal"><b class="" style="">Add League</b></button>
</div>
<transition name="modal">
<div id="overlay" v-show="this.show">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Add League</h5>
<button type="button" class="close" #click="closeModal"><span aria-hidden="true">×</span></button>
</div>
<div class="modal-body">
<form action="#" method="POST">
<div class="form-group">
<input type="text" v-model="this.newLeague.fullname" class="form-control form-control-md" placeholder="Name of League">
</div>
<div class="form-group">
<textarea v-model="this.newLeague.notes" rows="3" cols="100%" name="notes" class="form-control form-control-md" placeholder="Describe this league">
</textarea>
</div>
<div class="form-group form-inline ">
<div class="col-12 p-0">
<input type="url" v-model="this.newLeague.group_image_path" class="col-5 pull-left form-control form-control-md" placeholder="Image URL">
<button class="col-4 btn btn-primary btn-md">Image</button>
</div>
</div>
<div class="form-group">
<button class="btn btn-info btn-block btn-md" #click="closeModal();addLeague();">Add this league</button>
</div>
</form>
</div>
</div>
</div>
</div>
</transition>
<div>
`
});
<div class="row">
<div class="col-md-12">LEAGUES SECTION</div>
</div>
<div class="row mt-2">
<div class="col-md-12">
<leagues_add :show="true" />
</div>
</div>
The problem is here:
v-model="this.newLeague.fullname"
You cannot use this. with v-model. Instead it should be:
v-model="newLeague.fullname"
You should also remove all other references to this within your template. In many cases they are harmless but sometimes, such as with v-model, they will cause problems.
Complete examples below. Note how the first input does not function correctly when editing the text.
new Vue({
el: '#app1',
data () {
return { newLeague: { fullname: 'League 1' } }
}
})
new Vue({
el: '#app2',
data () {
return { newLeague: { fullname: 'League 1' } }
}
})
<script src="https://unpkg.com/vue#2.6.11/dist/vue.js"></script>
<div id="app1">
<input type="text" v-model="this.newLeague.fullname">
{{ newLeague.fullname }}
</div>
<div id="app2">
<input type="text" v-model="newLeague.fullname">
{{ newLeague.fullname }}
</div>
I followed the way #skirtle wrote the data section and it worked.
My original syntax was:
data: function () {
return {
newLeague: {"fullname":"a", "notes":"b", "group_image_path": "c"} }
}
to
data () {
return { newLeague: { fullname: 'League 1' } }
}

Bootstrap Modal Hide from VUE method

I have a vuejs component that displays a modal dialog with a small form inside. When the form is submitted I would like to hide the Modal but cannot figure out how to do it. When submitted the form calls a method in the parent.
Here is the component code
<template>
<div>
<div id="todoModal" class="modal fade" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{{ title }}</h4>
<button type="button" class="close" data-dismiss="modal">
×
</button>
</div>
<div class="modal-body">
<form id="todoForm" #submit.prevent="$emit('save')">
<div class="form-group">
<label for="name">Todo name</label>
<input
id="name"
v-model="todo.name"
type="text"
class="form-control"
aria-describedby="nameHelp"
placeholder="Enter Todo Name"
/>
<small id="nameHelp" class="form-text text-muted"
>Enter your todo's name</small
>
</div>
</form>
</div>
<div class="modal-footer">
<button class="btn btn-primary mr-4" type="submit" form="todoForm">
<span v-if="mode == 'create'">Create</span>
<span v-if="mode == 'update'">Update</span>
</button>
<button
type="button"
class="btn btn-danger mr-auto"
data-dismiss="modal"
#click="
$parent.showModal = false;
$parent.getTodos();
"
>
Cancel
</button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "CreateTodo",
props: ["mode", "title", "todo", "showModal"],
ref: "createTodoModal",
mounted() {
if (this.mode == "create") {
this.title = "Create Todo";
}
},
methods: {}
};
</script>
<style scoped></style>
And here is my APP.js file
<template>
<div id="app" class="container mt-5">
<router-view
ref="createtodo"
class="CreateTodo"
name="a"
:todo="todo"
:title="title"
:mode="mode"
:show-modal="showModal"
#save="saveTodo"
></router-view>
</div>
</template>
<script>
import { APIService } from "./APIServices";
const apiService = new APIService();
export default {
name: "App",
data: function() {
return {
mode: "create",
title: "Create Todo",
todo: {},
todos: [],
numberOfTodos: 0,
showModal: false
};
},
mounted: function() {
this.getTodos();
},
methods: {
saveTodo: function() {
if (this.mode == "create") {
apiService.createTodo(this.todo).then(
result => {
if (result.status === 200) {
this.todo = result.data;
this.getTodos();
}
},
error => {}
);
this.showModal = false;
// this.$refs.createtodo.$refs.todoModal
} else if (this.mode == "update") {
apiService.updateTodo(this.todo).then(
result => {
this.getTodos();
},
error => {}
);
this.showModal = false;
} else if (this.mode == "update") {
apiService.updateTodo(this.todo).then(
result => {
this.showModal = false;
this.getTodos();
},
error => {}
);
}
},
}
};
</script>
<style lang="scss">
</style>
I tried using the ref to reference the Modal from APP.js but it does not work.
Add an id to the close X button"
<button type="button" class="close" data-dismiss="modal" aria-label="Close" id="close">
<span aria-hidden="true">×</span>
</button>
Then create a method to close the modal:
closeModal() {
document.getElementById('close').click();
},
Like #Dan Stoian reply, you can use ref in vue.js :
<button ref="Close" type="button" data-dismiss="modal" ...>
...
</button>
And in your handler
this.$refs.Close.click();
If you are using boostrap, you need to call the hide an show methods from it, because modal api create html elements on the fly (as the dark background)
I recommend to create a save method instead of call the $emit, where you can call the modal hide method before emit the save signal.
<script>
import $ from 'jquery'
export default {
name: "CreateTodo",
props: ["mode", "title", "todo"],
ref: "createTodoModal",
mounted() {
if (this.mode == "create") {
this.title = "Create Todo";
}
},
methods: {
save() {
$('#ModalId').modal('hide')
this.$emit('save')
}
}
};
</script>
showModal is not needed in this case.
You might use v-if to show/hide modal
In your component:
<div v-if="showModal">
<div id="todoModal" class="modal fade" role="dialog">
...
</div>
</div>
and set showModal to true/false to show/hide component respectively.
Child
<template>
<div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby=""
aria-hidden="true" ref="modalEle">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-primary text-white">
<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 ref="Close" type="button" class="btn btn-secondary" data-bs-dismiss="modal">
Close
</button>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { onMounted, ref, defineProps, defineExpose } 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();
}
function _close(){
thisModalObj.hide()
}
defineExpose({ show: _show, close: _close });
</script>
Parent
<template>
<Modal title="OMG ITS A MODAL" ref="thisModal">
<template #body>
<div class="col-md-12 mx-auto">
yay modal text
</div>
</template>
<template #footer>
<!-- extra footer stuff !-->
</template>
</Modal>
</template>
<script setup>
import { ref } from 'vue';
import Modal from "../components/ModalCard.vue";
let showModal = ()=>{
thisModal.value.show();
}
let closeModal = ()=>{
thisModal.value.close();
}
</script>
Explanation
So basically what we are doing is exposing a child function to the parent via refs. In the _show and _close functions we are triggering the .show() and .hide() they are bootstrap modal functions we have access too via the thisModalObj. I hope this helps!
Now you can progmatically call thisModal.value.show and thisModal.value.close and itll show and close the modal.
you can use this npm package
npm i vue-js-modal
https://www.npmjs.com/package/vue-js-modal
Your code should then be modified in this way:
<template>
<modal :name="'edit-modal'+id" height="auto">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Edit {{ mName }}</h5>
<button type="button" class="close" #click="hideEditModal(id)">
<span aria-hidden="true">×</span>
</button>
</div>
<form action="/user/update" method="patch" #submit.prevent="updateAssistant">
<div class="modal-body">
<div class="position-relative form-group">
<label for="exampleAddress" class="">Full name</label><input name="name" v-model="editName" id="exampleAddress" placeholder="FullName" type="text" class="form-control">
<div v-if="errors.has('editName')" class="alert alert-danger">
{{ errors.first('editName') }}
</div>
</div>
<div class="position-relative form-group">
<label for="exampleEmail11" class="">Email</label><input name="email" v-model="editEmail" id="exampleEmail11" placeholder="email" type="email" class="form-control">
<div v-if="errors.has('editEmail')" class="alert alert-danger">
{{ errors.first('editEmail') }}
</div>
</div>
<div class="position-relative form-group">
<label for="examplePassword11" class="">Change Password</label><input name="password" v-model="editPassword" id="examplePassword11" placeholder="password" type="password" class="form-control">
<div v-if="errors.has('password')" class="alert alert-danger">
{{ errors.first('password') }}
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" #click="hideEditModal(id)">Close</button>
<button type="submit" class="btn btn-primary">Save changes</button>
</div>
<span class="loader" v-if="this.loading" style="position: absolute; bottom: 0.515rem; left: 20px;">
<span class="ball-clip-rotate">
<div style=" border:1px solid #000;"></div>
</span>
</span>
</form>
</modal>
import Errors from '../../Errors.js'
export default {
name: 'AssistantEditModalComponent',
props: [
'mEmail',
'mName',
'id'
],
data () {
return {
editPassword: null,
disabled: false,
errors: Errors,
loading: false
}
},
methods: {
hideEditModal(id) {
this.$modal.hide('edit-modal'+id);
},
setData() {
this.editName = this.mName
this.editEmail = this.mEmail
},
updateAssistant() {
this.disabled = true;
this.loading = true;
const form = {
editName: this.mName,
editEmail: this.mEmail,
password: this.editPassword
}
axios.patch('/user/update/'+this.id, form)
.then((response) => {
this.disabled = false
this.loading = false
this.hideEditModal(this.id)
this.alert(response)
})
.catch((err) => {
this.disabled = false
this.loading = false
Errors.fill(err.response.data.errors)
})
},
alert(response) {
swal(response.data.username, response.data.message, 'success')
},
},
computed: {
editName: {
get: function() {
return this.mName
},
set: function(value) {
this.$emit('update:mName', value);
}
},
editEmail: {
get: function() {
return this.mEmail
},
set: function(value) {
this.$emit('update:mEmail', value);
}
}
}}
If you don't want to add any extra Close button rather than default X button of modal header you could do something like this:
<b-modal
id="user-role"
ref="role-modal"
hide-footer
>
...
</b-modal>
then in your method:
hideModal() {
this.$refs["role-modal"].$refs['close-button'].click()
}