I have a Vue3 component that renders tables based on model data. Then I add DataTables functionality to the table to be able to filter and sorting data. Everything works fine. But if I click on the delete button I need to send a delete ajax request to the server and then on success redraw the whole table with new data. I tried to use draw() method but it does not work. Deleted rows are still in the table. I don't know if I need to remove the whole table and create it again or there is another solution.
This is the Vue template code.
<template>
<div class="table-responsive">
<table :id="id" class="table table-bordered nowrap" width="100%;">
<thead>
<tr>
<th>id</th>
<th>Title</th>
<th>Visible</th>
<th>User</th>
<th></th>
</tr>
</thead>
<tbody>
<tr v-for="article in articles" :key="article.id">
<td>{{article.id}}</td>
<td>{{article.title}}</td>
<td>{{article.visible}}</td>
<td>{{article.user.name}}</td>
<td>
delete
</td>
</tr>
</tbody>
</table>
</div>
</template>
And this is the deleteArticle() code
deleteArticle(id) {
axios.get( apiRoutes.ARTICLE_DELETE_URL + id )
.then( response => {
this.getArticles();
})
.catch( response => {
console.log(response);
});
},
getArticles()
{
axios.get( apiRoutes.ARTICLES_URL )
.then((response) => {
this.articles = response.data.articles;
this.$nextTick(function() {
this.setDataTable();
});
})
.catch((response) => {
console.log(response);
});
},
setDataTable() {
var table = $('#'+this.id);
if( this.dataTableObject )
{
this.dataTableObject.reload().draw(); // This is the code I need to fix
return;
}
...
},
What is the right flow of this component?
Related
i have created vue file to display data in frontend. but i'm unable to print 2 tables on same page at same time. only table 2 is displaying data , in first table it shows data for 2 seconds and than disappears. what i'm doing wrong? please help. i am super new in vuejs and have not much knowledge.
here is my index.vue file,
Table 1
<tbody>
<tr
v-show="items && items.length"
v-for="(data, i) in items"
:key="i">
<td></td>
<td></td>
</tr>
and this is function code,
async fetchData1() {
this.$store.state.operations.loading = true;
let currentPage = this.pagination ? this.pagination.current_page : 1;
await this.$store.dispatch("operations/fetchData", {
path: "/api/calldata?page=",
currentPage: currentPage + "&perPage=" + this.perPage,
});
table 2
<tbody>
<tr
v-show="items && items.length"
v-for="(data, i) in items"
:key="i">
<td></td>
<td></td>
</tr>
and here is the function for table 2
async fetchData2() {
this.Loading2 = true
let currentPage = this.Pagination2 ? this.Pagination2.current_page : 1;
await this.$store.dispatch("operations/fetchData", {
path: "/api/datacall/data2?page=",
currentPage: currentPage + "&perPage=" + this.perPage,
});
this.Loading2 = false;
and this are the controller functions
public function index(Request $request)
{
return DataResource::collection(Datamodl::with('user')->where('type',1)->latest()->paginate($request->perPage));
}
public function index2(Request $request)
{
return DataResource::collection(Datamodl::with('user')->where('type',0)->latest()->paginate($request->perPage));
}
And Route ,
Route::get('/calldata/data2', [DataController::class, 'index2']);
Route::apiResource('calldata', DataController::class);
Observation : You are updating same variable which is items for both the tables. Hence, it is overriding the latest items with the old items array.
Solution : Here is the implementation as per my comment.
new Vue({
el: '#app',
data: {
table1Items: null,
table2Items: null
},
mounted() {
this.fetchData1();
this.fetchData2();
},
methods: {
fetchData1() {
this.table1Items = [{
id: 1,
name: 'table 1 alpha'
}, {
id: 2,
name: 'table 1 beta'
}]
},
fetchData2() {
this.table2Items = [{
id: 1,
name: 'table 2 alpha'
}, {
id: 2,
name: 'table 2 beta'
}]
}
}
})
table, th, td {
border: 1px solid black;
margin: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<table>
<thead>
<th>ID</th>
<th>Name</th>
</thead>
<tbody>
<tr v-show="table1Items" v-for="(data, i) in table1Items" :key="i">
<td>{{ data.id }}</td>
<td>{{ data.name }}</td>
</tr>
</tbody>
</table>
<table>
<thead>
<th>ID</th>
<th>Name</th>
</thead>
<tbody>
<tr v-show="table2Items" v-for="(data, i) in table2Items" :key="i">
<td>{{ data.id }}</td>
<td>{{ data.name }}</td>
</tr>
</tbody>
</table>
</div>
you are using same property which is items for both. so second request will changed first items. so in both table same data will visible. you have to store in different state property for different data rendering.
solution :
make another action fetchData2.
call another mutation setItems2. add state propery item2: []. and setItems2 value from this mutation.
render second table like this.
<tr
v-show="items2.length"
v-for="(data, i) in items2"
:key="i">
<td></td>
<td></td>
</tr>
For code quailty:
give proper and related variable name . don't use items1 and items2 like that.
never used v-if/v-show and v-for in same element.for more info
use template first in this senerio.
use the item's unique id instead of the index in the key.
if you take the items default value as [], instead of null, then you only required to check items.length instead of items && items.length. so always use list default value []
if both requests are not dependent on each other then you should use Promise.all() for fetching data concurrently. which saved tremendous time and also in this case you don't require two loading property.
I am using axios to get data from laravel API. I am getting the API data in my corresponding vue page (in created() hook), but it's not passing into the template. I am sharing the console Image here :
My codes from vue are :
export default {
name : "employee-list",
data() {
return{
employee_list : []
}
},
async created() {
const res = await axios.get('/employees-list')
if(res.status == 200){
console.log(res.data)
this.employee_list = res.data
}else{
console.log('something went wrong')
}
}
}
My Template table :
<table class="employee-list">
<tr>
<td>Full Name</td>
<td>Email</td>
<td>Phone</td>
<td>Designation</td>
<td>Loans</td>
<td>Salary</td>
<td>Joining Date</td>
<td>Address</td>
<td>Photo</td>
<td>Status</td>
<td>Action</td>
</tr>
<tr v-for="(employee, i) in employee_list" :key="i">
<td>{{employee.full_name}}</td>
<td>{{employee.email}}</td>
<td>{{employee.phone}}</td>
<td>{{employee.designation}}</td>
<td>{{employee.loans}}</td>
<td>{{employee.salary}}</td>
<td>{{employee.joining_date}}</td>
<td>{{employee.address}}</td>
<td>{{employee.photo}}</td>
<td>{{employee.status}}</td>
<td><button :id="employee.id">Edit</button></td>
</tr>
</table>
I also tried with callApi() method, But it says "this.callApi is not a function"
It seems there is a double array in res.data, instead of one, thats why you see one row without data
Im using jQuery datatables for displaying my data in my users component in Vue.js, but when I run my code it displays the data but it has some text that says "No data found". Can someone help me with this? I really don't have any idea because I'm new in this Frontend tools.
Users.vue:
<template>
<table id="table" class="table table-striped table-bordered">
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>Email</th>
<th>Type</th>
<th>Created</th>
</tr>
</thead>
<tbody>
<tr v-for="user in users" :key="user.id">
<td>{{ user.id }}</td>
<td>{{ user.name }}</td>
<td>{{ user.email }}</td>
<td>{{ user.type }}</td>
<td>{{ user.created_at }}</td>
</tr>
</tbody>
</table>
</template>
<script>
export default {
data() {
return {
users: [],
form: new Form({
id: "",
name: "",
email: "",
password: "",
type: ""
})
};
},
methods: {
loadUsers() {
axios.get("api/user").then(({ data }) => (this.users = data));
}
},
created() {
console.log("Component mounted.");
this.loadUsers();
}
};
$(document).ready(function() {
$("#table").DataTable({});
});
</script>
Probably, you should init your widget DataTable after receiving data from api, more precisely in then method.
axios.get("api/user").then(({ data }) => {
this.users = data;
this.$nextTick(() => {
$("#table").DataTable({});
});
});
Explanation about Vue.$nextTick:
Defer the callback to be executed after the next DOM update cycle. Use
it immediately after you’ve changed some data to wait for the DOM
update. This is the same as the global Vue.nextTick, except that the
callback’s this context is automatically bound to the instance calling
this method.
axios.get("api/user").then(({ data }) => {
this.users = data;
$(function() {
$("#table").DataTable({});
});
});
I want to send array of id's to backend with one button from vuejs table but i get error 500.
Logic
Check the check boxes
Collect the id's
Send id's to back-end when click on button
update the view
Code
template
<table class="table table-dark table-hover table-bordered table-striped">
<thead>
<tr>
<th class="text-center" width="50">
//the button
<button class="btn btn-outline-danger" #click="withdraw(index)">Withdraw</button>
</th>
<th class="text-center" width="50">#</th>
<th class="text-center">Amount</th>
</tr>
</thead>
<tbody>
<tr v-for="(income,index) in incomes" v-bind:key="index">
<td class="text-center">
//check box input
<input v-if="income.withdraw == '0'" type="checkbox" :id="income.id" :value="income.amount" v-model="checkedNumbers">
</td>
<td class="text-center">{{index+1}}</td>
<td class="text-center">Rp. {{formatPrice(income.amount)}}</td>
</tr>
<tr>
<td colspan="2"></td>
<td>
<span>Withdraw for today, Sum: <br> Rp. {{ formatPrice(sum) }}</span>
</td>
</tr>
</tbody>
</table>
script
export default {
data() {
return {
incomes: [],
checkedNumbers: [],
}
},
computed: {
sum() {
return this.checkedNumbers.reduce(function (a, b) {
return parseInt(a) + parseInt(b);
}, 0);
}
},
methods: {
withdraw(index) {
let checkedids = this.incomes[index]
axios.post(`/api/withdrawbutton/`+checkedids).then(response => {
this.income[index].withdraw = '1'
this.$forceUpdate()
});
}
}
}
route
Route::post('withdrawbutton/{id}', 'IncomeController#withdrawbutton');
controller
public function withdrawbutton($id)
{
$dowithdraw = Income::where('id', $id)->get();
$dowithdraw->withdraw = '1';
$dowithdraw->save();
return response()->json($dowithdraw,200);
}
Any idea where is my mistake and how to fix it?
......................................................................................................................
Don't send the list as a GET parameter, send it as a POST:
let params = {}
params.ids = this.checkedNumbers
axios.post(`/api/withdrawbutton/`, params)
.then(response => {
this.income[index].withdraw = '1'
this.$forceUpdate()
});
Controller
public function withdrawbutton(Request $request)
{
$dowithdraws = Income::whereIn('id', $request->input('ids', []));
$dowithdraws->update(['withdraw' => '1']);
return response()->json($dowithdraws->get(), 200);
}
Route
Route::post('withdrawbutton/', 'IncomeController#withdrawbutton');
And I don't think you need to update anything in the front because you already have them checked (if you want to keep them checked)
I am learning vue js and building a SPA i want to know how do I add filters and search using an input tag. I also want to add a feature that when i click on a particular name on the table it should open the profile of that person also a select all functionality
<template>
<div class="animated fadeIn">
<input type="text" placeholder="Enter Name" v-model="searchText">
<table class="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>College Name</th>
<th>College City</th>
<th>Level Name</th>
<th>Submitted</th>
<th>Pending</th>
<th>Completed</th>
<th>Approved</th>
<th>Rejected</th>
</tr>
</thead>
<tbody>
<tr v-for="profile in profilesdata">
<td>{{profile.first_name}}</td>
<td>{{profile.college_name}}</td>
<td>{{profile.college_city}}</td>
<td>{{profile.level_name}}</td>
<td>{{profile.submitted}}</td>
<td>{{profile.pending}}</td>
<td>{{profile.complete}}</td>
<td>{{profile.approve}}</td>
<td>{{profile.rejected}}</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default{
name: 'profiles',
data () {
return{
profilesdata: []
}
},
created:function(){
this.loadlike()
},
methods:{
loadlike:function(){
this.$http.get('/api/profiles').then(function (res) {
this.profilesdata = res.body
console.log(53+this.profiles)
})}}}
</script>
you could probably return computed list instead of profilesData
<template>
...
<input type="text" placeholder="Enter Name" v-model="searchText">
...
<tr v-for="profile in computedProfilesData">
...
</template>
<script>
export default{
...
data () {
return {
...
// - you need info for searchText
searchText: ''
}
}
...
computed: {
computedProfilesData(){
let searchString = this.searchText;
return this.profilesdata.filter((profile) => {
// example
profile.first_name.indexOf(searchString) !== -1;
})
}
}
</script>
there is a lot of different ways to do this, this is just one of them.
you can pass that searchString to API and return result list, it all comes to that
what do you really need.
You can make a computed for the filtered data:
computed: {
filteredProfiles() {
return this.profilesdata.filter(profile => {
// TODO filter profile with this.searchText
})
}
}
And then change the v-for to loop over the filtered data: <tr v-for="profile in filteredProfiles">