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

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 >

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>

dataTables new row won't add to table

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.

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;
},
},
...

AngularJS Component - Function Binding

I wrote a table component:
<table ng-model="$ctrl.model" class="table" md-progress="promise">
<thead class="thead">
<tr>
<th class="theader">ID</th>
<th class="theader">A</th>
<th class="theader">Actions</th>
</tr>
</thead>
<tbody>
<tr md-row ng-repeat="row in $ctrl.data track by $index">
<td width="15%" class="trow">{{row.id}}</td>
<td width="15%" class="trow">{{row.a}}</td>
<td width="15%" class="trow">
<md-button ng-click="$ctrl.edit({x: row.id, y: row.a})"></md-button>
</td>
</tr>
</tbody>
I define the component as:
.component('tablesample', {
require: {},
templateUrl: 'Components/templates/tableSample.html',
bindings: {
data: '=',
model: '=',
edit: '&',
},
controller: function ($log) {
var tbl = this;
tbl.$onInit = function () {
};
tbl.$onChanges = function () {
};
}
})
I call the new component as:
<tablesample data="tableData" model="selectedRow" edit="editFunction()"></tablesample>
Edit Function is defined as:
$scope.editFunction = function(x,y){
console.clear();
$log.info(x);
$log.info(y);
};
The log statements always display 'undefined' and I don't know why, row.id is populated, if I dump it I can see 1,2,3, etc.... Inside editFunction I do an http get so if x is undefined the call will fail.
Can anyone see what I did wrong?
Thanks
D
I had to change:
<tablesample data="tableData" model="selectedRow" edit="editFunction()"></tablesample>
to:
<tablesample data="tableData" model="selectedRow" edit="editFunction(id,a)"></tablesample>
Thanks
Docmur

Categories