Vue js load modal content with ajax - javascript

I'm using vue js with laravel, In my page i have 13 modals, I thing it's not a good idea to insert 13 modals in the same page so i put this in my blade :
<modal v-if="showModal" #close="showModal = false">
<header slot="header" v-html="modalHeader"></header>
<component slot="body" :is="currentBody"></component>
</modal>
And in my file.js i have this :
Vue.component('modal', require('./components/Modal.vue'));
Vue.component('StatusModal', require('./components/modals/StatusModal.vue'));
Vue.component('UserModal', require('./components/modals/UserModal.vue'));
const profile = new Vue({
el: '#profile',
data: {
showModal: false,
modalHeader: '',
currentBody: '',
},
methods: {
showStatus(){
this.showModal = true
this.modalHeader = 'Confirmation'
this.currentBody = 'StatusModal'
},
showUser(){
this.showModal = true
this.modalHeader = 'Confirmation'
this.currentBody = 'UserModal'
}
}
})
Just for example here i have two modals 'StatusModal' and 'UserModal' and i load them in my js file, so if i have 13 or more modals i will load 13 components in my js file, i need a solution that load only component that i need when i call function, It's possible to load component in function call?
I tried to use ajax and load blade content in ajax call and load it in my modal but i face a problem, i'm using vue validation for inputs so validation don't work (vee-validate) also any vue variable does not compiled and i get {{ variable }} in my modal.
What is the best solution for my case? i appreciate any help

Edit after chat
Solution found:
HTML:
<component slot="body" :is="currentBody"></component>
JS:
showStatus() {
this.dialog = true
System.import('./components/modals/StatusModal.vue').then((response) => {
this.currentBody = response
});
},
Previous answer
If using vue-cli webpack config(modules), I'd go for a solution as follow. The key point is using System.import. It's the same thing as require but asynchronous. It will only resolve one time even if you call the same file multiple times. It'll cache it client side.
I use a render function here, because it's a lot easier to manage vue template replacement.
import Modal from './components/Modal.vue'
const profile = new Vue({
render(h) {
if (this.showModal) {
return h('modal', {
on: {
close() {
this.showModal = false
}
}
}, [
h('header', { slot: 'header' }, this.modalHeader),
h(this.currentBody, { slot: 'body' })
]);
} else {
return null;
}
},
data: {
showModal: false,
modalHeader: '',
currentBody: {},
},
methods: {
showStatus() {
this.showModal = true
this.modalHeader = 'Confirmation'
this.currentBody = System.import('./components/modals/StatusModal.vue')
},
showUser() {
this.showModal = true
this.modalHeader = 'Confirmation'
this.currentBody = System.import('./components/modals/UserModal.vue')
}
},
components: {
Modal
}
})
Note I haven't tested it. Let me know if anything goes wrong or if I misunderstood your case.

Related

Submit a form from a Modal in Vue js

I need to send a form from a modal. Not using a full Vue app, but inserting Vue.js in my HTML page.
I tried a lot of unsuccesful things with my current modal, so I reduced it to the basic modal example I used for the first time https://v2.vuejs.org/v2/examples/modal.html
For the form, I used also the most basic form validation example at https://v2.vuejs.org/v2/cookbook/form-validation.html (I have it working in other places).
And I have created this unsuccessful fiddle:
https://jsfiddle.net/JIBRVI/03qnok9m/53/
Vue.component('modal', {
template: '#modal-template'
})
// start app
// eslint-disable-next-line no-new
new Vue({
el: '#app',
data: {
showModal: false,
errors: [],
name: ''
},
methods: {
checkForm: function (e) {
if (this.name) {
return true
}
this.errors = []
if (!this.name) {
this.errors.push('Name required.')
}
e.preventDefault()
}
}
})
In the basic modal example I added the form with a field, a submit button and a placeholder to show errors. Also the input field «name» and the array «errors» to the data section in the app. I also added the «checkForm» method.
The main error says:
Property or method "checkForm" is not defined on the
instance but referenced during render. Make sure that this property is
reactive, either in the data option, or for class-based components, by
initializing the property
Maybe the main page can communicate with the modal, so data and methods from the main page can’t be used.
I also tried to make a component with the form, but it didn’t work either. I can’t communicate with the modal.
Any help will be aprreciated.
You need to move the name and checkform methods to the modal component. You have currently defined those two in the app component and are trying to access it from the modal which is in the modal component.
Vue.component('modal', {
template: '#modal-template',
data(){
return {
name: '',
errors: [],
}
},
methods: {
checkForm: function (e) {
if (this.name) {
return true
}
this.errors = []
if (!this.name) {
this.errors.push('Name required.')
}
e.preventDefault()
}
}
})

Vuejs - Render raw html links to <router-link>

I;m new to VueJS and I'm making some weird experiments. I build a backend service using python/flask and this backend provide me a string of html code with many tags inside, I want to render this inside my Vue app, I have a method for calling the backend that looks like this:
async execute (method, resource, data) {
return client({
method,
url: resource,
data: data
}).then(async req => {
return req.data.html
})
},
callBack (id) {
console.log(id)
return this.execute('post', '/content/', {body: { 'id': id }})
}
And in the .vue file I have:
export default {
data () {
return {
loading: false,
launch: [],
html: 'none',
page: this.$route.params.article
}
},
beforeMount () {
console.log('beforeee')
this.html = api.callBack(this.page)
},
methods: {
async launch () {
this.launch = ''
this.html = await api.callBack(this.page)
}
}
}
so when I call the launch function it populates this.html, and this html variable lives in a v-html.Everything seems to work i get the html and render it in de container but the links are broken, the links should point at the same app something like #/test/linkvalue, but as they are tags, and in vue you have to use they doesn't work.
There is a way to achieve this "dynamic re route" or I'm doing something too weird?
The links are plenty, since they are scraped from the web, so manually parsing is not an option.
JSFiddle
Thanks in advance for your help
Also, you shouldn't return the raw html. Just return the paths for the routes and then loop the paths and create links that way.
You can use the v-html vue directive to output raw html.
https://jsfiddle.net/eywraw8t/66262/
new Vue({
el: "#app",
data: {
rawhtml: "<h1 style='color: red;'>Hi, I am raw html.</h1>"
},
methods: {
}
})
<div id="app">
<div v-html="rawhtml"></div>
</div>

Laravel 5.5 + Vue.js 2.x Proper API requests

I am working locally on a Laravel 5.5 project which uses Vue.js 2.5.9 on with XAMP Server.
I have to load some information to the DOM and refresh it when click "Refresh" button.
Sometimes the information is loaded and well displayed but sometimes they are not (some of the responses are):
Error 429: { "message": "Too Many Attempts." }
Error 500: { "message": "Server Error." }
I managed to "solve" the first issue (error 429) by increasing the Middleware throttle in Kernel.php from 'throttle:60,1', to 100,1)
But the second error I am not sure why I am get it sometimes and sometimes not.
I have this in my APIController (for example):
public function users()
{
$users = User::all();
return response()->json($users);
}
Then in app.js I call the methods in the created hook like this:
const app = new Vue({
el: '#app',
data: {
...
totalUsers: 0,
...
},
created: function() {
...
this.loadUsers();
...
},
methods: {
...
loadUsers: function() {
axios.get('/api/admin/users')
.then(function (response) {
app.totalUsers = response.data.length;
});
},
refreshData: function() {
this.loadUsers():
},
...
}
});
Maybe should I replace $users = User::all() to $users = User::count() to avoid loading "too much data" in API requests?
I think you should be using mounted() instead of created() in your vue.
const app = new Vue({
el: '#app',
data: {
...
totalUsers: 0,
...
},
mounted: function() {
...
this.loadUsers();
...
},
methods: {
...
loadUsers: function() {
axios.get('/api/admin/users')
.then(function (response) {
app.totalUsers = response.data.length;
});
},
refreshData: function() {
this.loadUsers():
},
...
}
});
that's the equivalent of the $(document).on(ready) in jQuery. Thats the method that fires when the window has fully loaded.
On a side note, Laravel knows when it is returning json as an ajax response, so you could probably just amend you controller method to this
public function users()
{
return User::all();
}

How to trigger transition when data is loaded in component in vue.js?

i'm currently working on a project with vue.js and vue-router. I got a vue in which I display some news, and I got thoses news from an API (it's kind of a blog).
I'm currently loading thoses news inside the router.data to set the data for the component, as said here. It's working great, no problems.
But my problem is that I want to animate the apparition of the news when I go to this view. I've tried using the ready property from the component, but it's called before the router.data has finished getting the news, which result in errors in animation, because there aren't any elements.
How can i trigger the animations once the news I fetch are fully rendered inside the DOM ?
Here is the code of my component:
export default {
name: 'News',
data: function () {
return {
news: []
}
},
route: {
data: function (transition) {
console.log('data hook')
return api
.getPostsByLimit(4, 1)
.then(function (posts) {
for (var i = 0; i < posts.length; i++) {
var post = posts[i]
post.formatedDate = moment(post.date).format("D MMM. YYYY")
post.dateTime = moment(post.date).format("YYYY-MM-DD")
if(post.news_artist_related) {
post.news_artist_related = JSON.parse(post.news_artist_related)
post.news_artist_related.type = slugify(post.news_artist_related.type)
post.news_artist_related.slug = slugify(post.news_artist_related.slug)
}
}
return posts
})
.then(news => ({news}))
}
},
ready: function () {
console.log('Ready hook')
animateNewsApparition()
}
}
From the docs:
When resolved, the component will also emit a 'route-data-loaded' event.
So:
events: {
'route-data-loaded': function() {
animateNewsApparition()
}
}

How to set a timer with a Vue.js class

im just using Vue.js to updates posts on a site im messing around with, this is what ive got so far (im still learning javascript, and not too great at it)
[app.js]
var Vue = require('vue');
Vue.use(require('vue-resource'));
var app = new Vue({
el: '#app',
components: {
'postlist' : require('./components/postlist/postlist.js')
}
});
[postlist.js]
module.exports = {
template: require('./postlist.template.html'),
data: function () {
return {
'search': '',
'posts' : {}
}
},
methods: {
'updatePosts' : function()
{
this.$http.get('api/posts', function(responce, status, request)
{
this.$set('posts', responce.data);
});
}
}
};
What I'm looking for is to have updatePosts fire off every x seconds, how do I do this?
ive tried doing this in the app.js
setInterval(function()
{
app.components.postlist.methods.updatePosts(); // doesnt work
app.postlist.updatePosts(); //doesnt work either
}, 500);
and tried putting the setInterval into the component itself
im pretty lost with this, whats the best way to achieve this?
updatePosts running every x seconds?
I have also trouble with scopes in Vue.
this should work
module.exports = {
template: require('./postlist.template.html'),
data: function () {
return {
'search': '',
posts: {}
}
},
methods: {
updatePosts: function () {
var self = this;
self.$http.get('api/posts', function(responce, status, request) {
self.posts = responce.data;
setTimeout(function(){ self.updatePosts() }, 2000);
});
}
},
created: function () {
this.updatePosts();
}
}
Functions in Vue works kinda different way, because your method updatePosts is not regular function. It is function defined in $vm.methods object. so It can't be called regularly like setTimeout($vm.updatePosts). Actually $vm.updatePosts doesn't exists. if you called it like $vm.updatePosts() it is different story. $vm instance automatically calls its method... So correct way is setTimeout(function(){ self.updatePosts() },2000)
You could start the request cycle in created or somewhere else in the lifecycle. It's also probably better to use recursion here so you can wait for the response to come back before you send off another one. I didn't test this code fully but it should work.
module.exports = {
template: require('./postlist.template.html'),
data: function () {
return {
'search': '',
posts: {}
}
},
methods: {
updatePosts: function () {
this.$http.get('api/posts', function(responce, status, request) {
this.posts = responce.data;
setTimeout(this.updatePosts, 2000);
});
}
},
created: function () {
this.updatePosts();
}
}

Categories

Resources