How i can send ajax request after render vue component? - ajax

I have a component
html:
table
tr(is="trcom" v-for="xml in xmls" :xml="xml")
js:
components: {
trcom: {
props: ['xml'],
template: "<tr><td> {{ xml.query }} </td><td> downloading </td><td> downloading </td></tr>",
data: function(){
return {
position: "",
}
}
}
}
can i send ajax request and replace template if ajax is done?
final template:
<tr><td> {{ xml.query }} </td> <td> {{ position }} </td> ...etc... </tr>

Given our discussion in the comments below your question, I have a recommendation:
1) The elements that you're adding and want replaced after individual ajax calls should each be their own component.
2) Because you will be using individual child components, you should use the mounted lifecycle hook to perform your ajax calls.
3) Instead of "replacing" the components' templates, which I'm not sure is even possible, you can instead use conditional rendering to show an initial state vs. a post-ajax state.
Below is a toy example which also uses jQuery (the jQuery itself isn't necessary but is being used for simplicity):
Child Component
Vue.component('my-child-component', {
template: `
<div>
<div v-if="initialized">
I'm initialized! My prop value is: {{my_prop}}
</div>
<div v-else>
I'm not initialized yet!
</div>
</div>
`,
props: ['my_prop'],
data: function() {
return {
initialized: false
};
},
mounted: function() {
//needed because we can't use "this" to reference the vue instance in our ajax call!
var this_vue_instance = this;
//this jQuery ajax call will execute as soon as this component has finished rendering
$.post('/your/target/url', {any_data: 'you might need'}, function(data) {
this_vue.initialized = true;
});
}
});
Root Vue Instance
<script>
$(document).ready(function() {
var root_vue_instance = new Vue({
el: '#app',
data: {
items: [0, 1, 2, 3]
}
});
});
</script>
<div id="app">
<div v-for="item in items">
<my-child-component :my_prop="item"></my-child-component>
</div>
</div>
The above is really bare-bones, but should server as a helpful example for implementing the solution to your current problem.

Related

VueDraggable and Laravel

I'm confused as how to correctly use vueDraggable together with Laravel.
I can drag and sort the elements in the browser but the array is not changing (when I check in the console)/ it seems to me the changes aren't reflected in the array. Shouldn't the array index numbers change after moving items?
In the overview.blade.php I have the component:
<qm-draggable :list="{{ $mylaravelarray }}"></qm-draggable>
In the qm-draggable.vue I have:
<template>
<draggable group="fragenblatt" #start="drag=true" #end="endDrag" handle=".handle">
<li v-for="(item, index) in draggablearray" :key="item.index">
// list items here
</li>
</draggable>
</template>
<script>
data() {
return {
draggablearray:{},
};
},
props: {
list: Array,
},
mounted: function(){
this.draggablearray = this.list; // create a new array so I don't alter the prop directly.
},
[..]
</script>
In the documentation it says, one way to pass the array is:
value
Type: Array
Required: false
Default: null
Input array to draggable component. Typically same array as referenced by inner element v-for directive.
This is the preferred way to use Vue.draggable as it is compatible with Vuex.
It should not be used directly but only though the v-model directive:
<draggable v-model="myArray">
But where do I do that? in overview.blade.php or in the component (.vue), or both?
Try setting v-model on your draggable as that's what will update draggablearray.
Also if draggablearray is supposed to be an array, initialise it as one, so draggablearray:{} should be draggablearray:[].
new Vue({
el: '#app',
data: () => {
return {
drag: false,
draggablearray: [{
id: 1,
name: "1"
}, {
id: 2,
name: "2"
}, {
id: 3,
name: "3"
}]
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js">
</script>
<script src="https://cdn.jsdelivr.net/npm/sortablejs#1.7.0/Sortable.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Vue.Draggable/2.16.0/vuedraggable.min.js"></script>
<div class="container">
<div id="app">
<draggable v-model="draggablearray" group="fragenblatt">
<li v-for="(item, index) in draggablearray">
{{item.name}}
</li>
</draggable>
{{draggablearray}}
</div>
</div>
<script type="text/x-template" id="tree-menu">
<div class="tree-menu">
<div class="label-wrapper">
<div :style="indent" :class="labelClasses" #click.stop="toggleChildren">
<i v-if="nodes" class="fa" :class="iconClasses"></i>
<input type="checkbox" :checked="selected" #input="tickChildren" #click.stop /> {{label}}
</div>
</div>
<draggable v-model="nodes" :options="{group:{ name:'g1'}}">
<tree-menu v-if="showChildren" v-for="node in nodes" :nodes="node.nodes" :label="node.label" :depth="depth + 1" :selected="node.selected" :key="node">
</tree-menu>
</draggable>
</div>
</script>
Ah, I solved it, now I get the altered array back, I achieved it with this:
Had to add v-model="draggablearray" in the component .vue file
Needed to change my 'draggablearray' in data to an Array, instead of
object.
It looks like this now:
In the overview.blade.php I have the component:
<qm-draggable :list="{{ $mylaravelarray }}"></qm-draggable>
In the qm-draggable.vue I have:
<template>
<draggable v-model="draggablearray" group="fragenblatt" #start="drag=true" #end="endDrag" handle=".handle">
<li v-for="(item, index) in draggablearray" :key="item.index">
// list items here
</li>
</draggable>
</template>
<script>
data() {
return {
draggablearray:[], //has to be an Array, was '{}' before
};
},
props: {
list: Array,
},
mounted: function(){
this.draggablearray = this.list; // create a new array so I don't alter the prop directly.
},
[..]
</script>

VueJS dynamically add textareas based on user click (tinymce issue)

I built a dynamic form where the user has the option to add as many HTML textareas as possible. The problem I'm running into is when the user loads the next textarea, I lose tinymce. Same thing happening for any external script (example: datepicker and Select2.js). Any ideas?
Here is my code
<div v-for="index in counter" :key="index">
<email_template_row></email_template_row>
<hr>
</div>
<button class="btn btn-sm btn-success" #click="addRow" type="button">Add Row</button>
// Tinymce code here...
// load vuejs from cdn...
Vue.component('email_template_row', {
template: '<div><textarea name="col_2_content[]" class="form-control tinymce" placeholder="Body"></textarea></div>',
});
var vue = new Vue({
el: '#app',
data:{
counter: 1
},
methods: {
addRow: function () {
this.counter += 1;
}
},
});
Thank you.
You need to re-initialize those components after one is appended to the HTML.
So basically your addRow method should look like this:
addRow: function () {
this.counter += 1;
tinymce.init({
selector: 'textarea', // change this value according to your HTML, you can be more specific
auto_focus: 'element',
// add your configuration
});
}

Laravel router-link works only the first time

I am trying to fetch results from database in News.vue, and display them in Topnews.vue. I have two links fetched. When I click link1, it shows up the Topnews.vue template with everything working as intended, however, if i click link2, nothing happens, except for that the URL changes, but the template does not show up the result. If i refresh the page and click link2 or click on the navbar, then link2, it shows up, and same, clicking then link1, changes the URL, but doesnt show up. I'm really stuck on that and I'd be really glad if you help me out on that issue. Hope you understand.
News.vue
<template id="news">
<div class="col-sm-5">
<div class="cars" v-for="row in filteredNews" >
<div class="name" >
<p class="desc_top_time">{{row.created_at}}</p>
<span class="last_p"> {{row.category}}</span>
<h3 style="margin-bottom:-4px; font-size: 16px;">
<router-link class="btn btn-primary" v-bind:to="{name: 'Topnews', params: {id: row.id} }">{{row.title}}</router-link></h3>
</div></div></div>
</template>
<script>
export default {
data: function() {
return {
news: [],
}
},
created: function() {
let uri = '/news';
Axios.get(uri).then((response) => {
this.news = response.data;
});
},
computed: {
filteredNews: function() {
if (this.news.length) {
return this.news;
}
}
}
}
</script>
Topnews.vue
<template id="topnews1">
<div class="col-sm-7">
<div class="cars">
<img :src="topnews.thumb" class="img-responsive" width=100%/>
<div class="name" ><h3>{{ topnews.title }}</h3>
<p>
<br>{{ topnews.info }}<br/>
</p>
</div></div></div>
</template>
<script>
export default {
data:function(){
return {topnews: {title: '', thumb: '', info: ''}}
},
created:function() {
let uri = '/news/'+this.$route.params.id;
Axios.get(uri).then((response) => {
this.topnews = response.data;
});
}
}
</script>
Like GoogleMac said Vue will reuse the same component whenever possible. Since the route for both IDs use the same component Vue will not recreate it, so the created() method is only being called on the first page. You'll need to use the routers beforeRouteUpdate to capture the route change and update the data.
in TopNews.vue:
export default {
data:function(){
return {topnews: {title: '', thumb: '', info: ''}}
},
beforeRouteEnter:function(to, from, next) {
let uri = '/news/'+ to.params.id;
Axios.get(uri).then((response) => {
next(vm => {
vm.setData(response.data)
})
});
},
beforeRouteUpdate: function(to, from, next) {
let uri = '/news/'+ to.params.id;
Axios.get(uri).then((response) => {
this.setData(response.data);
next();
});
},
methods: {
setData(data) {
this.topnews = data
}
}
}
If you click a link referring to the page you are on, nothing will change. Vue Router is smart enough to not make any changes.
My guess is that the IDs are messed up. If you are using Vue devtools you will be able to easily see what data is in each link. Are they as you expect.

Vue not rendering my data

I have a component that I get data on create with ajax and I show this in a Template. Basically I have this:
my Vue code:
Vue.component('feedback-table', {
template: '#grid-template',
data: function () {
return {
chancesOfApproval:[],
}
},
created:function(){
$.getJSON('/getFeedbackQuestionsAndChances',function(data){
this.chancesOfApproval = data.chancesOfApproval;
}.bind(this));
},
});
new Vue({
el: '#feedback-wrapper',
});
And here is my template:
<template id="grid-template">
<table class="table table-bordered table-responsive table-striped">
<tr v-for="entry in chancesOfApproval">
<td>#{{ entry.position }}</td>
</tr>
</table>
</template>
<div id="feedback-wrapper">
<feedback-table></feedback-table>
</div>
The data is being getted from the jquery because if I do console.log(this.chanceOfApproval) for example, it appears fine in the console. And I do not get any error in my console, so I have no idea why it does not work.
It behaves as expected here, where I've switched out the getJSON for a setTimeout doing the same assignment.
Vue.component('feedback-table', {
template: '#grid-template',
data: function() {
return {
chancesOfApproval: [],
}
},
created: function() {
/*
$.getJSON('/getFeedbackQuestionsAndChances', function(data) {
this.chancesOfApproval = data.chancesOfApproval;
}.bind(this));
*/
setTimeout(() => {
this.chancesOfApproval = [{
position: 1
}, {
position: 2
}];
}, 500);
},
});
new Vue({
el: '#feedback-wrapper',
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.0.3/vue.min.js"></script>
<template id="grid-template">
<table class="table table-bordered table-responsive table-striped">
<tr v-for="entry in chancesOfApproval">
<td>#{{ entry.position }}</td>
</tr>
</table>
</template>
<div id="feedback-wrapper">
<feedback-table></feedback-table>
</div>
You are using function.bind(this) to bring this from outer scope to the function scope, in your $http success handler.
Have you checked if it is not creating any issues? I am not aware of how bind is implemented. Maybe it is not allowing Vue to trigger its Reactivity System.
If you see values in console as a plain JSON object, then it is probably wrong. Vue.js modifies object params to getters / setters / observers so that it can bind to the DOM.
Alternatively, why dont you try the arrow function or using a self variable, just to see if it solves the issue?
Arrow function (ES6 and above):
created:function(){
$.getJSON('/getFeedbackQuestionsAndChances',data => {
this.chancesOfApproval = data.chancesOfApproval;
});
}
Or use a local variable:
created:function(){
var self = this; // self points to this of Vue component
$.getJSON('/getFeedbackQuestionsAndChances',function(data){
self.chancesOfApproval = data.chancesOfApproval;
});
}
Another note: instead of setTimeout(), you also have the option of Vue.nextTick() as follows:
Vue.nextTick(function () {
// DOM updated
})
or in your case:
this.$nextTick(function () {
// DOM updated
})

Sorting and Filtering ajax data using Laravel and VueJs

Current code is sorting and filtering data using vue.js. It is working fine but data is dummy, it is hardcoded. I need to get data dynamically from table using vue js and laravel. How can I get dynamic data in gridData?
JS
Vue.component('demo-grid', {
template: '#grid-template',
props: {
data: Array,
columns: Array,
filterKey: String
},
data: function () {
var sortOrders = {}
this.columns.forEach(function (key) {
sortOrders[key] = 1
})
return {
sortKey: '',
sortOrders: sortOrders
}
},
methods: {
sortBy: function (key) {
this.sortKey = key
this.sortOrders[key] = this.sortOrders[key] * -1
}
}
})
// bootstrap the demo
var demo = new Vue({
el: '#app',
data: {
searchQuery: '',
gridColumns: ['name', 'power'],
gridData: [
{ name: 'Chuck Norris', power: Infinity },
{ name: 'Bruce Lee', power: 9000 },
{ name: 'Jackie Chan', power: 7000 },
{ name: 'Jet Li', power: 8000 }
]
}
})
laravel.blade.php
#extends('layouts.app')
#section('title', 'Customers List')
#section('styles')
#endsection
#section('content')
<div class="container">
<div class="row">
<div class="col-md-10 col-md-offset-1">
<div class="panel panel-default">
<div class="panel-heading">Customers List</div>
<div class="panel-body">
<script type="text/x-template" id="grid-template">
<table class="table table-hover table-bordered">
<thead>
<tr>
<th v-for="key in columns" #click="sortBy(key)" :class="{active: sortKey == key}">#{{key | capitalize}}<span class="arrow" :class="sortOrders[key] > 0 ? 'asc' : 'dsc'"></span>
</th>
</tr>
</thead>
<tbody>
<tr v-for="entry in data | filterBy filterKey | orderBy sortKey sortOrders[sortKey]">
<td v-for="key in columns">
#{{entry[key]}}
</td>
</tr>
</tbody>
</table>
</script>
<div id="app">
<form id="search">
Search <input name="query" v-model="searchQuery">
</form>
<demo-grid :data="gridData" :columns="gridColumns" :filter-key="searchQuery"></demo-grid>
</div>
</div>
</div>
</div>
</div>
</div>
#endsection
#section('scripts')
<script src="/js/vue.js"></script>
<script src="/js/vue-resource.min.js"></script>
<script src="/js/customers.js"></script>
#endsection
You will need to do a few things.
First, in Laravel, create a new route in your routes.php file, for ex.:
Route::get('/api/fighters', 'SomeController#index');
Then in your controller (somecontroller.php), you will have a method index which will query your database table and return it as JSON data.
public function index() {
//query your database any way you like. ex:
$fighters = Fighter::all();
//assuming here that $fighters will be a collection or an array of fighters with their names and power
//when you just return this, Laravel will automatically send it out as JSON.
return $fighters;
}
Now, in Vue, your can call this route and grab the data. Using AJAX. You can use any AJAX library that you like, even jQuery. I currently use Superagent.js. Vue will work just fine with any.
So, in your Vue code, create a new method to get your data.:
methods: {
getDataFromLaravel: function() {
//assign `this` to a new variable. we will use it to access vue's data properties and methods inside our ajax callback
var self = this;
//build your ajax call here. for example with superagent.js
request.get('/api/fighters')
.end(function(err,response) {
if (response.ok) {
self.gridData = response.body;
}
else {
alert('Oh no! We have a problem!');
}
}
}
}
Then you can just call this new method using a button or anyway you like. For example, using a button click event:
<button type="button" #click="getDataFromLaravel">Get Data</button>
Or you can even just have the data loaded automatically using Vue's ready function:
// bootstrap the demo
var demo = new Vue({
el: '#app',
data: {
.... }
ready: function () {
this.getDataFromLaravel();
},
methods: {
.... }
});
Done! You now have your database data assigned to Vue's data property gridData.

Resources