dataTables new row won't add to table - javascript

When I add new data it won't add to table
Code
HTML
<thead>
<tr>
<th class="text-center">ID</th>
<th class="text-center">Number</th>
<th class="text-center">Body</th>
<th class="text-center">Heading</th>
<th class="text-center">Book</th>
<th class="text-center">Chapter</th>
<th class="text-center">Actions</th>
</tr>
</thead>
<tbody>
<template v-for="verse in getVerses">
<tr v-bind:key="verse.id">
<td width="30" class="text-center">{{ verse.id }}</td>
<td class="text-center">{{ verse.number }}</td>
<td>{{ verse.body }}</td>
<td>{{ verse.heading }}</td>
<td class="text-center">{{ verse.book.name }}</td>
<td class="text-center">{{ verse.chapter.name }}</td>
<td class="text-center">
<button class="btn btn-sm btn-danger" type="button" #click="deleteVerse(verse.id)">Delete</button>
</td>
</tr>
</template>
</tbody>
</table>
Script
export default {
name: "adminVerses",
data() {
return {
isLoading: true,
type: '',
books: [],
errors: [],
pageTitle: 'Manage Verses',
getChapters: [],
getVerses: [],
isLoadingChapters: true,
isLoadingVerses: true,
verse: {
number: 1,
heading: '',
body: '',
book_id: '',
chapter_id: '',
}
}
},
methods: {
submit: function(e) {
axios.post('/api/saveVerse', this.verse)
.then(res => {
this.isLoading = false;
$('#exampleModal').modal('toggle');
this.getVerses.push( res.data.verse );
this.verse = {
number: parseInt(this.verse.number) + 1,
heading: '',
body: '',
book_id: this.verse.book_id,
chapter_id: this.verse.chapter_id,
};
})
.catch(error => {
// handle authentication and validation errors here
this.errors = error.response.data.errors
this.isLoading = false
})
},
}
}
Any idea?
Update
by adding
if ($.fn.dataTable.isDataTable('#verses')) {
$('#verses').DataTable().clear().destroy(); //This will destroy datatable
};
this.getVerses.push( res.data.verse );
now i can get my newly added data into my table but i also will lose ability of paging and search, etc.
ideas?

You are using this to refer to an object, so you are limited to Axios scope. Instead you can use a variable to store a reference to main object, in your case adminVerses and then use $data to set or fetch the data attributes.
Example :
let vm = this;
axios({
...
}).then((res) => {
vm.$data.getVerses.push( res.data.verse );
});

open your vue devtools and check if getVerses gets the right data

The push on the array isn't responsive, so the vue doesn't know it needs to update the view. The array setter is responsive, so:
this.getVerses = this.getVerses.concat(res.data.verse)
will do the trick.

Related

Auto play a sound when receive a new row data in Vue.js

I am developing a Vue app to fetch orders from Woocommerce REST API. So i am having a HTML Table to fill the JSON Data. So when each order receive to Woocommerce, I will get it in my dashboard using "setInterval" for 60 seconds through refreshing data. Also the flags are set to notify new order in the HTML table using colors.
Apart from this I need to play a sound in the Vue app whenever the Woocommerce receives an order. How can I archive this as a stable function?
My JS code
var app = new Vue({
el: '#app',
data: {
orders: []
},
mounted: function() {
// Call the API the first time
this.refreshData()
// Then call the API every minute
this.setIntervalId = setInterval(this.refreshData, 60000)
},
beforeDestroy: function() {
// Stop refreshing data after the component is destroyed!
clearInterval(this.setIntervalId)
},
methods: {
refreshData () {
axios.get('https://mysite/wp-json/wc/v3/orders?per_page=20&consumer_key=key1&consumer_secret=key2')
.then(response => {
const previouslyFlaggedIds = this.orders.filter(x => x.is_printed).map(x => x.id);
this.orders = response.data.map(x => ({...x, is_printed: previouslyFlaggedIds.find(y => y === x.id) != null}));
console.log(response);
})
.catch(error => {
console.log(eror);
});
},
printBill(order) {
//change flag
order.is_printed = true;
}
}
})
HTML
<div class ="container mt-5" id="app">
<table class="table table-bordered">
<thead>
<tr>
<th scope="col">Order id</th>
<th scope="col">Name</th>
<th scope="col">Order Date</th>
<th scope="col">Phone</th>
<th scope="col">Address</th>
<th scope="col">Items</th>
<th scope="col">Total</th>
<th scope="col">Print</th>
</tr>
</thead>
<tbody>
<tr
v-for="(order, index) in orders"
:key="order.id"
:class="{highlight: !order.is_printed}"
>
<td>{{ order.id }}</td>
<td>{{ order.billing.first_name + " " +order.billing.last_name }}</td>
<td>{{ order.date_created }}</td>
<td>{{ order.billing.phone}}</td>
<td>{{ order.billing.address_1 + ", " + order.billing.address_2 + ", " + order.billing.city + order.billing.postcode }}</td>
<td>{{ order.line_items[0].name}} </td>
<td>{{ order.total}}</td>
<td><button class="btn btn-primary" #click="printBill(order)">Print</button>
</tr>
</tbody>
</table>
You can use audio tag for this.
<audio
ref="audio"
src="/media/cc0-audio/t-rex-roar.mp3">
</audio>
Then after you receive the new orders just play the audio
this.$refs.audio.play()
JSFiddle example

Make an array from object to iterate over it by *ngFor

I got response from my api it simple one record from my database.
getFilledForm() {
const reservation = this.hotels[0].reservation;
this.accidentFormService.getAccidentForm(reservation).subscribe(
res => {
this.form = res;
console.log('this.form: '+JSON.stringify(this.form));
this.test = Object.keys(this.form).map(key => ({ type: key, value: this.form[key] }));
console.log('this.test: ' +this.test);
},
error => {
console.log(error);
}
)
}
this.form is a object and output looks like:
this.form: {"id":1,reservation":1234,"hotel_name":"BB Hotels"}
I tried to make an array but my array of objects looks like ( this.test )
this.test: [object Object],[object Object],[object Object]
after stringify
this.test: [{"type":"id","value":1},{"type":"reservation","value":null},{"type":"hotel_name","value":"BB Hotels"}]
I want output like: "id":1 no "type":"id","value":1
How sould it be ? I want to make a table from output using *ngFor
-Edit
Added HTML
<table *ngIf="checkForm" class="table table-sm align-items-center table-hover table-flush responsive">
<thead class="thead-light">
<tr>
<th scope="col" class="lPart">ID</th>
<th scope="col" class="lPart">Hotel Name</th>
</tr>
</thead>
<tbody>
<tr
*ngFor="let form of form | paginate: { itemsPerPage: 10, currentPage: p }; let i = index">
<td id="lPart">{{ form.id }}</td>
<td id="lPart">{{ form.hotel_name }}</td>
</tr>
</tbody>
</table>

why my code make an infinite loop when I call the api?

I'm trying to call my api to get the name of a tp but in the view I only see a [object promise] and in the browser console it looks like it's doing an infinite loop
html:
<table [dtOptions]="dtOptions" class="row-border hover" datatable>
<thead>
<tr>
<th>Date</th>
<th>Intitulé</th>
<th>Résultat</th>
<th>Coefficiant</th>
</tr>
</thead>
<tbody *ngIf="grades?.length != 0">
<tr *ngFor="let grade of grades">
<td>{{ formatDate(grade.Date) | date : 'MM/dd/yyyy hh:mm'}}</td>
<td>{{ getTpName(grade.tp_id) }}</td>
<td><b>{{ grade.Grade | number:'2.1-2' }}/20</b></td>
<td>{{ grade.coeff | number:'1.1'}}</td>
</tr>
</tbody>
<tbody *ngIf="grades?.length == 0">
<tr>
<td class="no-data-available" colspan="4">Aucune note enregistées</td>
</tr>
<tbody>
</table>
TS:
async getTpName(tp_id: any) {
return await this.apiService.getTpName(tp_id);
}
apiservice>getTpName
getTpName(tp_id) {
let url = `${this._tpUrl}/readName/${tp_id}`;
return new Promise((resolve, reject) => {
this.http.get(url, {headers: this.headers}).subscribe(
res => resolve(res),
error => reject(error)
)
});
}
Do you know why it happen and how to correct this ?
EDIT:
It will be good to move the api call from html to component controller.
ngOnInit() {
grades.forEach(grade => {
this.getTpName(grade.tp_id)
.pipe(take(1))
.subscribe(tpName => {
grade.tpName = tpName;
});
});
}
< td > {{ grade.tpName | json }}</td >

Vue, multifilter for html table

I'm using vue to take a returned object from an axios call and fill in an html table with it. I have the table exactly as I want but I'm wondering if there's a way (don't want to convert this whole thing to datatables) to make each header a filter for the table so that multiple columns can be filtered for the whole table. Basically, say the rows have items like 'Truck', 'Trailer' and 'Container' for the 'Resources' column. I'm thinking of a dropdown filter on the header of that column that would show rows for all resources or I could select 'Truck' so that only rows with 'Truck' show on the table.
Does that make sense? Is there an inherent way to do this with Vue?
<table style="width:100%; text-align:center;">
<thead>
<tr>
<th>Title</th>
<th>Resource</th>
<th>Location</th>
<th>Status</th>
</tr>
</thead>
<tbody v-for="dateEvent in dateEvents">
<tr>
<td v-if="dateEvent.id === '2'">{{ dateEvent.title }}</td>
<td v-if="dateEvent.id === '2'">{{ dateEvent.resource }}</td>
<td v-if="dateEvent.id === '2'">{{ dateEvent.location }}</td>
<td v-if="dateEvent.id === '2'">{{ dateEvent.status }}</td>
</tr>
</tbody>
</table>
data () {
return {
dateEvents: []
},
created() {
this.fetchItems();
},
methods: {
fetchItems() {
axios.get('/home/resource_items')
.then(response => {
// handle success
console.log(response.data)
this.dateEvents = response.data
})
.catch(function(error) {
console.log(error)
})
.finally(function() {})
}
}
You can use computed functionality to automatically calculate:
a filtered array of dateEvents to display in the table
an array of titles to display in the filter
an array of resources to display in the filter
an array of locations to display in the filter
an array of statuses to display in the filter
Here is an example:
...
<select v-model="filters.title">
<option v-for="title in titles" :value="title">{{ title }}</option>
</select>
<select v-model="filters.resource">
<option v-for="resource in resources" :value="resource">{{ resource }}</option>
</select>
<select v-model="filters.location">
<option v-for="location in locations" :value="location">{{ location }}</option>
</select>
<select v-model="filters.status">
<option v-for="status in statuses" :value="status">{{ status }}</option>
</select>
<button #click="reset">Reset</button>
...
<tbody v-for="dateEvent in filtered">
<tr>
<td>{{ dateEvent.title }}</td>
<td>{{ dateEvent.resource }}</td>
<td>{{ dateEvent.location }}</td>
<td>{{ dateEvent.status }}</td>
</tr>
</tbody>
...
...
data() {
return {
dateEvents: [],
filters: {
title: null,
resource: null,
location: null,
status: null,
},
};
},
computed() {
filtered() {
return this.dataEvents
.filter(dataEvent => !this.filters.title || dataEvent.title === this.filters.title),
.filter(dataEvent => !this.filters.resource || dataEvent.resource === this.filters.resource),
.filter(dataEvent => !this.filters.location || dataEvent.location === this.filters.location),
.filter(dataEvent => !this.filters.status || dataEvent.status === this.filters.status);
},
titles() {
return this.dataEvents
.map(dataEvent => dataEvent.title)
.filter((title, index, self) => self.indexOf(title) === index);
},
resources() {
return this.dataEvents
.map(dataEvent => dataEvent.resource)
.filter((resource, index, self) => self.indexOf(resource) === index);
},
locations() {
return this.dataEvents
.map(dataEvent => dataEvent.location)
.filter((location, index, self) => self.indexOf(location) === index);
},
statuses() {
return this.dataEvents
.map(dataEvent => dataEvent.status)
.filter((status, index, self) => self.indexOf(status) === index);
},
},
methods: {
reset() {
this.filters.title = null;
this.filters.resource = null;
this.filters.location = null;
this.filters.status = null;
},
},
...

Getting related array element AngularJS

I have a php api that returns a list of companies and a list of users,
Each user has a company_id assigned.
(Users : CustomerList) (Companies : CompanyList)
The companies have a company_id and company_name value.
I'm using resolve to get both these arrays.
When displaying the CustomerList, the company_id of the customer is displayed and everything is working fine.
But what i require is instead of the company_id being shown, I need the company_name being displayed in the CustomerList.
The company_id of the CustomerList is related to the id in the CompanyList.
Just that the company_name is contained in the companyList and not in the CustomerList. But the ID's are related and contained in the userList.
I need to get the company_name of the id that's in CustomerList and display it in the view.
resolve: { userList:function($http,$stateParams){
return $http.post('/api/get_users.php',{role:$stateParams.role},{headers: { 'Content-Type': 'application/x-www-form-urlencoded' }})
.then(function(response){
console.log(response);
return response.data.data;
})
},
companyList:function($http,$stateParams){
return $http.get('/api/get_companies.php')
.then(function(response){
console.log(response);
return response.data.data;
})
}
}
controller("CustomerList", function($scope,$location,$http,$stateParams,userList,companyList){
$scope.customers = userList;
$scope.companies = companyList;
})
VIEW
<table>
<thead>
<tr>
<th>Username</th>
<th>Company Name</th>
<th>Contact Name</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="customer in customers">
<td>{{ customer.username }}</td>
<td>{{ customer.company_id }}</td>
<td>{{ customer.contact_name }}</td>
</tr>
</tbody>
</table>
the customer.company_id is related to the ID in the companyList , i need to return the companyList.company_name instead of showing the company ID in the customers.
Thanks in advance for any help.
You need to join them.
So you need to create a new object with will contain the username, contact_name, company_name and other properties you need. You need to use the map() which will return an array of new objects. With username and contract_name it is easy, just assign the properties to the new created object. With company_name, we need to find the appropriete company and assign it's name to the newly created object.
Working Example with simple data, which joins two arrays based on the id.
var app = angular.module('app', []);
app.controller('ctrl', ['$scope', function($scope){
var userList = [
{username: 'A user', contact_name: 'A contract', company_id: 1},
{username: 'B user', contact_name: 'B contract', company_id: 2},
{username: 'C user', contact_name: 'C contract', company_id: 3},
];
var companyList = [
{id: 1, name: 'A Company'},
{id: 2, name: 'B Company'},
{id: 3, name: 'C Company'},
];
$scope.customers = userList.map(item => {
var foundCompany = companyList.find(company => company.id === item.company_id);
return {
username: item.username,
contact_name: item.contact_name,
company_name: foundCompany ? foundCompany.name : ''
};
});
$scope.companies = companyList;
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="app" ng-controller="ctrl">
<table>
<thead>
<tr>
<th>Username</th>
<th>Company Name</th>
<th>Contact Name</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="customer in customers">
<td>{{ customer.username }}</td>
<td>{{ customer.company_name }}</td>
<td>{{ customer.contact_name }}</td>
</tr>
</tbody>
</table>
</body>
You can structure your companies list in such a way that each object in list is key value pair with key being id of company and value being complete object of company details and then access it easily.
eg
$scope.companies = {
{
"companyId1" : {
"name" : "companyName",
"field2" : "vlue2"
}
},
{
"companyId2" : {
"name" : "companyName2",
"field2" : "vlue2"
}
}
}
and then in your html access it using
{{companies[customer.company_id].name}}
This will work for you
map your userList so that it will contain companyName
var mappedCustomerList= userList.map(function(user){
var user=userWithCompanyName;
userWithCompanyName['companyName']=
companyList[companyList.findIndex(function(company){return company['id']==user['company_id']})].companyName;
return userWithCompanyName;
});
$scope.customerList=mappedCustomerList;
so then from your view you can access companyName
<td>{{customer.companyName}}</td>

Categories