I've got a fragment where I make an API call using Async Http library.
This is the code:
private void getPeers() {
Log.i("REST", "getPeers");
RestClient.get(RestClient.PEERS_URL, null, new TextHttpResponseHandler() {
#Override
public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
Log.i("REST", "onFailure");
stopRefreshing();
if (statusCode == 0) {
showNoConnectionError();
}
}
#Override
public void onSuccess(int statusCode, Header[] headers, String responseString) {
Log.i("REST", "onSuccess");
stopRefreshing();
// Create arraylist from json
peerArrayList = new Gson().fromJson(responseString, new TypeToken<List<Peer>>() {
}.getType());
// Show ListView
populateListView(peerArrayList);
}
});
}
When I call getPeers() the first time, it all works well.
After it is called the second time, it loads for 5-10seconds and then times out.
This is the log after it is called 2 times:
07-22 18:33:08.909 30671-30671/com.challenge.challengeapp I/REST﹕ getPeers
07-22 18:33:09.044 30671-30671/com.challenge.challengeapp I/REST﹕ onSuccess
07-22 18:33:11.707 30671-30671/com.challenge.challengeapp I/REST﹕ getPeers
07-22 18:33:19.589 30671-6125/com.challenge.challengeapp W/System.err﹕ org.apache.http.client.ClientProtocolException
07-22 18:33:19.593 30671-6125/com.challenge.challengeapp W/System.err﹕ at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:562)
07-22 18:33:19.594 30671-6125/com.challenge.challengeapp W/System.err﹕ at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:492)
07-22 18:33:19.594 30671-6125/com.challenge.challengeapp W/System.err﹕ at com.loopj.android.http.AsyncHttpRequest.makeRequest(AsyncHttpRequest.java:148)
07-22 18:33:19.594 30671-6125/com.challenge.challengeapp W/System.err﹕ at com.loopj.android.http.AsyncHttpRequest.makeRequestWithRetries(AsyncHttpRequest.java:179)
07-22 18:33:19.594 30671-6125/com.challenge.challengeapp W/System.err﹕ at com.loopj.android.http.AsyncHttpRequest.run(AsyncHttpRequest.java:108)
07-22 18:33:19.595 30671-6125/com.challenge.challengeapp W/System.err﹕ at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
07-22 18:33:19.595 30671-6125/com.challenge.challengeapp W/System.err﹕ at java.util.concurrent.FutureTask.run(FutureTask.java:237)
07-22 18:33:19.595 30671-6125/com.challenge.challengeapp W/System.err﹕ at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
07-22 18:33:19.595 30671-6125/com.challenge.challengeapp W/System.err﹕ at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
07-22 18:33:19.597 30671-6125/com.challenge.challengeapp W/System.err﹕ at java.lang.Thread.run(Thread.java:818)
07-22 18:33:19.597 30671-6125/com.challenge.challengeapp W/System.err﹕ Caused by: org.apache.http.client.CircularRedirectException: Circular redirect to 'http://52.17.110.217/api/friends/'
07-22 18:33:19.597 30671-6125/com.challenge.challengeapp W/System.err﹕ at org.apache.http.impl.client.DefaultRedirectHandler.getLocationURI(DefaultRedirectHandler.java:178)
07-22 18:33:19.597 30671-6125/com.challenge.challengeapp W/System.err﹕ at org.apache.http.impl.client.DefaultRequestDirector.handleResponse(DefaultRequestDirector.java:928)
07-22 18:33:19.597 30671-6125/com.challenge.challengeapp W/System.err﹕ at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:480)
07-22 18:33:19.597 30671-6125/com.challenge.challengeapp W/System.err﹕ at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:560)
07-22 18:33:19.597 30671-6125/com.challenge.challengeapp W/System.err﹕ ... 9 more
07-22 18:33:19.606 30671-30671/com.challenge.challengeapp I/REST﹕ onFailure
If I call it one time, then restart app, it works. This just happens when I call it 2 times in a row.
Use this method instead of RestClient.
public static AsyncHttpClient getAsyncHttpClient() {
return new AsyncHttpClient();
}
Related
I am testing my vue component that uses vuejs-smart-table
by rendering the headers and rows from head and body slot that is in my vue component.
this is my vue component (it is just a wrapper that wraps vuejs-smart-table and a pagination component):
<template>
<div class="custom-table-wrapper">
<div class="table-container" :style="tableContainerStyle">
<v-table
:id="id"
:class="tableClass"
:data="data"
:hideSortIcons="true"
:pageSize="pageSize"
:filters="filters"
:currentPage="currentPage"
:selectionMode="selectionMode"
:selectedClass="selectedClass"
#update:currentPage="currentPageChanged"
#totalPagesChanged="totalPagesChanged"
#selectionChanged="selectionChanged">
<template slot="head">
<slot name="head">
</slot>
</template>
<template slot="body" slot-scope="{displayData}">
<slot name="body" :displayData="displayData">
</slot>
</template>
</v-table>
</div>
<div v-show="showTableFooter" class="paging" id="pagination">
<smart-pagination
v-show="totalPages>1"
:currentPage="currentPage"
:totalPages="totalPages"
:maxPageLinks="5"
#update:currentPage="currentPageChanged"/>
<div class="footer-action-btns-container">
<slot name="footer-action-btns">
</slot>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
id: {
type: String,
required: true,
},
data: {
type: Array | null,
default() {
return [];
},
},
filters: {
type: Object,
default() {
return {};
},
},
tableClass: {
type: [String, Object],
default: 'table table-bordered',
},
hideSortIcons: {
type: Boolean,
default: false,
},
pageSize: {
type: Number,
default: 20,
},
selectionMode: {
type: String,
default: 'single',
},
selectedClass: {
type: String,
default: 'vt-selected',
},
tableContainerStyle: {
type: [String, Object],
value: undefined,
},
},
data() {
return {
totalPages: 0,
currentPage: 1,
};
},
methods: {
totalPagesChanged(page) {
this.totalPages = page;
},
currentPageChanged(page) {
this.currentPage = page;
},
selectionChanged($event) {
this.$emit('selectionChanged', $event);
},
},
computed: {
showTableFooter() {
return this.data.length > this.pageSize || this.$slots['footer-action-btns']?.[0];
},
},
};
</script>
this is my spec file for testing my vue component:
import { createLocalVue, mount, $createElement } from '#vue/test-utils';
import SmartTable from 'vuejs-smart-table';
import CustomTable from '#/components/custom-table.vue';
const localVue = createLocalVue();
localVue.use(SmartTable);
describe('CustomTable.vue Test', () => {
let wrapper;
let data = [
{
id: '1',
first_name: 'user1',
last_name: 'user1',
},
{
id: '2',
first_name: 'user2',
last_name: 'user2',
},
{
id: '3',
first_name: 'user3',
last_name: 'user3',
},
];
beforeEach(() => {
wrapper = mount(CustomTable, {localVue,
propsData: {
data: data,
id: 'custom-table',
},
slots: {
head: `
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
</tr>
</thead>
`,
body: `
<tbody #body="{displayData}">
<tr v-for="(row, index) in displayData" :key="index">
<td>{{row.first_name}}</td>
<td>{{row.last_name}}</td>
</tr>
</tbody>
`
},
});
});
it('renders items from data as rows', () => {
expect(wrapper).toMatchSnapshot();
});
});
this is the output of the snapshot:
exports[`CustomTable.vue Test renders items from data as rows 1`] = `
<div class="custom-table-wrapper">
<div class="table-container">
<table id="custom-table" class="table table-bordered">
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<div id="pagination" class="paging" style="display: none;">
<nav class="smart-pagination" style="display: none;">
<ul class="pagination">
<!---->
<li class="page-item disabled"><a href="javascript:void(0)" aria-label="Previous" class="page-link"><svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512">
<path fill="currentColor" d="M34.52 239.03L228.87 44.69c9.37-9.37 24.57-9.37 33.94 0l22.67 22.67c9.36 9.36 9.37 24.52.04 33.9L131.49 256l154.02 154.75c9.34 9.38 9.32 24.54-.04 33.9l-22.67 22.67c-9.37 9.37-24.57 9.37-33.94 0L34.52 272.97c-9.37-9.37-9.37-24.57 0-33.94z"></path>
</svg></a></li>
<li class="page-item active">1</li>
<li class="page-item disabled"><a href="javascript:void(0)" aria-label="Next" class="page-link"><svg width="16" height="16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512">
<path fill="currentColor" d="M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z"></path>
</svg></a></li>
<!---->
</ul>
</nav>
<div class="footer-action-btns-container"></div>
</div>
</div>
`;
as you can see the tbody doesnt render any rows here.
i have tried using scopedSlots but it just results to more errors.
One strange fact was that the first time I saved the code, the map appeared perfectly on the screen. But it was only this one time.
I don't know what is going on. The code looks perfect, and I don't know what this error in the console means.
I am unable to fix these errors:
runtime-core.esm-bundler.js:38 [Vue warn]: Unhandled error during execution of mounted hook
at <LeafletMap lat=-20.3612397 lng=-40.2958806 >
at <[id] onVnodeUnmounted=fn<onVnodeUnmounted> ref=Ref< Proxy {__v_skip: true} > >
at <Anonymous key="/anuncio/kqnceP5qQ3QTl0d9wrFM" routeProps= {Component: {…}, route: {…}} pageKey="/anuncio/kqnceP5qQ3QTl0d9wrFM" ... >
at <BaseTransition mode="out-in" appear=false persisted=false ... >
at <Transition name="page" mode="out-in" >
at <RouterView name=undefined route=undefined >
at <NuxtPage>
at <Default >
at <Anonymous key="default" name="default" hasTransition=true >
at <BaseTransition mode="out-in" appear=false persisted=false ... >
at <Transition name="layout" mode="out-in" >
at <Anonymous>
at <App key=1 >
at <NuxtRoot>
and:
Uncaught (in promise) ReferenceError: L is not defined
at LeafletMap.vue:24:1
at runtime-core.esm-bundler.js:2723:40
at callWithErrorHandling (runtime-core.esm-bundler.js:155:22)
at callWithAsyncErrorHandling (runtime-core.esm-bundler.js:164:21)
at hook.__weh.hook.__weh (runtime-core.esm-bundler.js:2697:29)
at flushPostFlushCbs (runtime-core.esm-bundler.js:341:32)
at flushJobs (runtime-core.esm-bundler.js:395:9)
My LeafletMap component:
<script setup>
const props = defineProps({
// props go here
lat: {
type: Number,
required: true
},
lng: {
type: Number,
required: true
}
})
// leaflet map 1.9.2
const map = ref(null)
const marker = ref(null)
const mapContainer = ref(null)
const mapOptions = {
center: [props.lat, props.lng],
zoom: 13,
zoomControl: false
}
// load map
onMounted(async () => {
map.value = await L.map(mapContainer.value, mapOptions)
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution:
'© OpenStreetMap contributors'
}).addTo(map.value)
marker.value = await L.marker([props.lat, props.lng]).addTo(map.value)
})
// update marker position
watch(
() => props.lat,
() => {
marker.value.setLatLng([props.lat, props.lng])
map.value.setView([props.lat, props.lng], 13)
}
)
watch(
() => props.lng,
() => {
marker.value.setLatLng([props.lat, props.lng])
map.value.setView([props.lat, props.lng], 13)
}
)
// destroy map
onUnmounted(() => {
map.value.remove()
})
</script>
<template>
<div class="relative h-96 w-full" ref="mapContainer"></div>
</template>
<style scoped>
#import 'https://unpkg.com/leaflet#1.9.2/dist/leaflet.css';
</style>
The props are going to the component:
<LeafletMap :lat="ad.geolocation.lat" :lng="ad.geolocation.lng" />
OP solved the issue by importing Leaflet
const L = await import('leaflet')
I am working on a Nuxt app. For one of my dynamic pages I want to show data with infinite scrolling. For that purpose I decided to use Vue-infinite-loading. I read this article and also skimmed the documentation of Vue-infinite-loading. In both of them they were using axios module to load data step by step to show in the page when the scroll reaches to specific point in that page. But in my page the data is already present in the page according to page-id with the help of $strapi module and Nuxt fetch hook. The whole code of my page is like below:
<template>
<v-container fluid>
<v-row>
<v-col cols="10" class="mx-auto">
<p v-if="$fetchState.pending">
در حال بارگذاری اطلاعات
</p>
<p v-else-if="$fetchState.error">
مشکلی در نمایش اطلاعات به وجود آمده است.
</p>
<div v-else>
<h1 v-html="this.fourType[this.nameRoute]['title']">
</h1>
<section>
<BaseCard
v-for="item in listShow"
:key="item.id"
:imgId = "item.id"
:sizeCard = "270"
>
<template v-slot:tozih>
{{ item.tozih }}
</template>
<template v-slot:aout>
{{ item.author }}
</template>
</BaseCard>
</section>
<infinite-loading
spinner="spiral"
#infinite="infiniteScroll"
>
</infinite-loading>
</div>
</v-col>
</v-row>
</v-container>
</template>
<script>
export default {
data() {
return {
listBooks: [],
fourType:{
short: {
title: "در این قسمت میتوانید کتابها را به ترتیب از کوچک (کمترین تعداد صفحه) به بزرگ (بیشترین تعداد صفحه) مشاهده نمایید:",
request: 1
},
programming: {
title: "در این قسمت میتوانید کتب مرتبط با برنامهنویسی (دارای برچسب برنامهنویسی) را به ترتیب به روزرسانی (از جدیدترین به قدیمیترین) مشاهده نمایید:",
request: 2
},
new: {
title: "در این قسمت میتوانید کتب موجود در سایت را به ترتیب به روز رسانی (از جدید به قدیم) مشاهده نمایید:",
request: 3
},
random: {
title: "در این قسمت میتوانید به صورت تصادفی به مشاهده تمامی کتب موجود در سایت بپردازید:",
request: 4
}
},
nameRoute: this.$route.params.type
}
}, // end of data
computed: {
listShow: function() {
return [ this.listBooks[0], this.listBooks[1] ]
}
}, // end of computed
methods: {
infiniteScroll($state) {
if (this.listBooks.length > 1) {
this.listBooks.forEach((item) => this.listShow.push(item))
$state.loaded()
}
else {
$state.complete()
}
}
}, // end of methods
async fetch() {
switch (this.nameRoute) {
case "short":
this.listBooks = await this.$strapi.$books.find("_sort=pageQuantity:ASC");
break;
case "programming":
this.listBooks = await this.$strapi.$books.find({ 'tags.name': ['برنامه نویسی'], _sort: 'updated_at:DESC' });
break;
case "new":
this.listBooks = await this.$strapi.$books.find("_sort=updated_at:DESC");
break;
default:
this.listBooks = this.$store.getters["books/randomBook"](this.$store.state.books.list2.length);
break;
}
}
}
</script>
<style scoped>
</style>
In the code I get the data in fetch hook (that is different according to page id) and then put it in listBooks Vue data. What I want to do is that show for example 2 data in listBooks first and when the scroll reached to the end of the second data (second card here), I show the rest of data step by step with infinite scrolling method. So I used a Computed data called listShow and used it in v-for. Then I put the code I thought that maybe it might work inside infiniteScroll method. But obviously that does not work, because I just guess that. If anyone know that how change that code to work and show data in infinite scrolling please help me to solve this issue.
Usually, an infinite loader is used to have a small subset of data that you then expand for performance reasons: not having to load 100 elements at once but 10, then 10 more etc...
If you already have the data locally at once and like the behavior of it, without any "pagination" needed from your Strapi backend, you can always watch for the #infinite event and increase the size of your initial array of elements with the next one.
Saying that if you display 10 of them, want to scroll down and show 10 more: display the first 10, then when the infinite event is triggered, append 10 more items to your initial array and so on.
My previous answer may help understand it a bit more.
PS: beware of some reactivity issues that you may face one day when dealing with arrays.
I finally with the help of kissu answer and with the inspiration of the code of the article mentioned in my question found the solution. Here I will post the code of my whole Nuxt page to show the answer for using other developers:
<template>
<v-container fluid>
<v-row>
<v-col cols="10" class="mx-auto">
<p v-if="$fetchState.pending">
در حال بارگذاری اطلاعات
</p>
<p v-else-if="$fetchState.error">
مشکلی در نمایش اطلاعات به وجود آمده است.
</p>
<div v-else>
<h1 v-html="this.fourType[this.nameRoute]['title']">
</h1>
<section>
<BaseCard
v-for="item in list2"
:key="item.id"
:imgId = "item.id"
:sizeCard = "270"
>
<template v-slot:tozih>
{{ item.tozih }}
</template>
<template v-slot:aout>
{{ item.author }}
</template>
</BaseCard>
</section>
<infinite-loading
spinner="spiral"
#infinite="infiniteScroll"
>
</infinite-loading>
</div>
</v-col>
</v-row>
</v-container>
</template>
<script>
export default {
data() {
return {
listBooks: [],
page: 0,
list2: [],
fourType:{
short: {
title: "در این قسمت میتوانید کتابها را به ترتیب از کوچک (کمترین تعداد صفحه) به بزرگ (بیشترین تعداد صفحه) مشاهده نمایید:",
request: 1
},
programming: {
title: "در این قسمت میتوانید کتب مرتبط با برنامهنویسی (دارای برچسب برنامهنویسی) را به ترتیب به روزرسانی (از جدیدترین به قدیمیترین) مشاهده نمایید:",
request: 2
},
new: {
title: "در این قسمت میتوانید کتب موجود در سایت را به ترتیب به روز رسانی (از جدید به قدیم) مشاهده نمایید:",
request: 3
},
random: {
title: "در این قسمت میتوانید به صورت تصادفی به مشاهده تمامی کتب موجود در سایت بپردازید:",
request: 4
}
},
nameRoute: this.$route.params.type
}
}, // end of data
methods: {
infiniteScroll($state) {
setTimeout(() => {
let emptyArr = [];
for (let index = this.page*10; index < this.page*10+10; index++) {
if (this.listBooks[index]) {
emptyArr.push(this.listBooks[index])
}
}
if (emptyArr.length > 0) {
emptyArr.forEach(
(item) => this.list2.push(item)
)
$state.loaded();
} else {
$state.complete()
}
this.page++
}, 500)
}
}, // end of methods
async fetch() {
switch (this.nameRoute) {
case "short":
this.listBooks = await this.$strapi.$books.find("_sort=pageQuantity:ASC");
break;
case "programming":
this.listBooks = await this.$strapi.$books.find({ 'tags.name': ['برنامه نویسی'], _sort: 'updated_at:DESC' });
break;
case "new":
this.listBooks = await this.$strapi.$books.find("_sort=updated_at:DESC");
break;
default:
this.listBooks = this.$store.getters["books/randomBook"](this.$store.state.books.list2.length);
break;
}
}
}
</script>
<style scoped>
</style>
the two important changes were:
1- Using an empty array called list2 in my Vue data instead of Vue computed data.
2- Using a variable called emptyArr in my infiniteScroll method that holds only for example 10 items of the original data (Vue listBooks data) and is showing them with scrolling the page each time that 10 data passed from the user view.
Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 2 years ago.
Improve this question
so i rendering succusfully products , now i want to add the product id to the cart array however once i clicked on the add button it gives me this error
[Vue warn]: Error in v-on handler: "ReferenceError: cartItems is not defined"
hopefully u can give me a hint on how to fix the problem
thnak you so much
this is the code
<template>
<main>
<div class="margin"></div>
<div class="shopTitle">
<h1>Shop</h1>
</div>
<section>
<div v-if="show" class="products">
<div class="product" v-for="product in filteredProducts" :key="product.productId">
<div class="imgproduct"></div>
<div class="productDetails">
<div>
<h1>{{product.productTitle}}</h1>
</div>
<div>
<i class="fa fa-heart"></i>
</div>
<div>
<p>{{product.productPrice}}</p>
</div>
<div>
<i #click="addToCart(product)" class="fa fa-shopping-cart"></i>
</div>
</div>
</div>
</div>
</section>
</main>
</template>
<script>
export default {
data() {
return {
products: [
{
id: 1,
productTitle: "Shoes",
productImg: "../assets/ProductOne.png",
productPrice: "246$",
category: "Shoes"
},
{
id: 2,
productTitle: "Suits",
productImg: "../assets/ProductOne.png",
productPrice: "246$",
category: "Suits"
},
{
id: 3,
productTitle: "Bags",
productImg: "../assets/ProductOne.png",
productPrice: "246$",
category: "Bags"
}
],
selectedCategory: "All",
show: true,
cartItems: []
};
},
methods: {
addToCart(itemToAdd) {
// Add the item or increase qty
let itemInCart = this.cartItems.filter(item => item.id === itemToAdd.id);
let isItemInCart = itemInCart.length > 0;
if (isItemInCart === false) {
this.cartItems.push(itemToAdd);
} else {
}
console.log(cartItems)
}
}
};
</script>
console.log(cartItems)
should be
console.log(this.cartItems)
I'm developing a WebApp with MEANStack, using Sequelize to access SQL Databases. Unfortunately I get the following error on the client's side: core.js:1673 ERROR TypeError: Cannot read property 'map' of undefined
at MapSubscriber.project (tables.service.ts:39)
"Line 39" of the error is applicationsTables: tableData.applicationsTables.map(table => {
Here's how the DT on the server side looks like:
Data Table on the Browser - Server Side
And here's how the error on the client's side looks like:
Error Messages on the Chrome developers' tools view
Here's my code
tables-list.component.html
<mat-spinner *ngIf="isLoading"></mat-spinner>
<h1 class="mat-body-2">Process List </h1>
<mat-accordion multi="true" *ngIf="userIsAuthenticated && !isLoading">
<mat-expansion-panel>
<mat-expansion-panel-header>
Process List
</mat-expansion-panel-header>
<table mat-table [dataSource]="processTables" matSort class="mat-elevation-z8" *ngIf="userIsAuthenticated">
<!-- ProcessName Column -->
<ng-container matColumnDef="ProcessName">
<th mat-header-cell *matHeaderCellDef mat-sort-header> ProcessName </th>
<td mat-cell *matCellDef="let element"> {{element.ProcessName}} </td>
</ng-container>
<!-- PackageVersion Column -->
<ng-container matColumnDef="PackageVersion">
<th mat-header-cell *matHeaderCellDef mat-sort-header> PackageVersion </th>
<td mat-cell *matCellDef="let element"> {{element.PackageVersion}} </td>
</ng-container>
<!-- RobotType Column -->
<ng-container matColumnDef="RobotType">
<th mat-header-cell *matHeaderCellDef mat-sort-header> RobotType </th>
<td mat-cell *matCellDef="let element"> {{element.RobotType}} </td>
</ng-container>
<!-- PackagePath Column -->
<ng-container matColumnDef="PackagePath">
<th mat-header-cell *matHeaderCellDef mat-sort-header> PackagePath </th>
<td mat-cell *matCellDef="let element"> {{element.PackagePath}} </td>
</ng-container>
<!-- CreationTime Column -->
<ng-container matColumnDef="CreationTime">
<th mat-header-cell *matHeaderCellDef mat-sort-header> CreationTime </th>
<td mat-cell *matCellDef="let element"> {{element.CreationTime}} </td>
</ng-container>
<!-- Status Column -->
<ng-container matColumnDef="Status">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Status </th>
<td mat-cell *matCellDef="let element"> {{element.Status}} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedprocessTablesColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedprocessTablesColumns;"></tr>
</table>
</mat-expansion-panel>
</mat-accordion>
<br> <h1 class="mat-body-2">Applications List </h1>
tables-list.component.ts:
import { Component, OnInit, OnDestroy } from "#angular/core";
import { Table, ApplicationsTable } from "./tables.model";
import { PageEvent } from "#angular/material";
import { Subscription } from "rxjs";
import { TablesService } from "./tables.service";
import { AuthService } from "../auth/auth.service";
#Component({
// We load the component via routing and therefore we do not need a selector
selector: "app-tables",
templateUrl: "./tables-list.component.html",
styleUrls: ["./tables-list.component.css"]
}) // Turn class into component by adding #Component Decorator
export class TableListComponent implements OnInit, OnDestroy {
processTables: Table[] = [];
applicationsTables: ApplicationsTable[] = [];
isLoading = false;
totalTables = 0;
tablesPerPage = 5;
currentPage = 1;
pageSizeOptions = [1, 2, 5, 10];
displayedprocessTablesColumns: string[] = ["ProcessName", "PackageVersion", "RobotType", "PackagePath", "CreationTime", "Status" ];
userIsAuthenticated = false;
userId: string;
isAdmin: boolean;
private tablesSub: Subscription;
private authStatusSub: Subscription;
constructor(
public tablesService: TablesService,
private authService: AuthService
) {}
ngOnInit() {
this.isLoading = true;
this.tablesService.getTables(this.tablesPerPage, this.currentPage);
this.userId = this.authService.getUserId();
this.tablesSub = this.tablesService
.getTableUpdateListener()
.subscribe((tableData: { processTables: Table[]; applicationsTables: ApplicationsTable[]; tableCount: number }) => {
this.isLoading = false;
this.totalTables = tableData.tableCount;
this.processTables = tableData.processTables;
this.applicationsTables = tableData.applicationsTables;
console.log(tableData.applicationsTables);
});
this.userIsAuthenticated = this.authService.getIsAuth();
// console.log("Is authenticated: " + this.userIsAuthenticated);
this.authStatusSub = this.authService
.getAuthStatusListener()
.subscribe(isAuthenticated => {
this.userIsAuthenticated = isAuthenticated;
});
}
onChangedPage(pageData: PageEvent) {
this.isLoading = true;
this.currentPage = pageData.pageIndex + 1;
this.tablesPerPage = pageData.pageSize;
this.tablesService.getTables(this.tablesPerPage, this.currentPage);
}
onLogout() {
this.authService.logout();
}
ngOnDestroy() {
this.tablesSub.unsubscribe();
this.authStatusSub.unsubscribe();
}
}
Tables.service.ts:
import { Injectable } from "#angular/core";
import { HttpClient } from "#angular/common/http";
import { Subject } from "rxjs";
import { map } from "rxjs/operators";
import { Router } from "#angular/router";
import { environment } from "../../environments/environment";
import { Table, ApplicationsTable } from "./tables.model";
const BACKEND_URL = environment.apiUrl + "/tables/";
#Injectable({ providedIn: "root" })
export class TablesService {
private processTables: Table[] = [];
private applicationsTables: ApplicationsTable[] = [];
private tablesUpdated = new Subject<{ processTables: Table[]; applicationsTables: ApplicationsTable[]; tableCount: number }>();
constructor(private http: HttpClient, private router: Router) {}
getTables(tablesPerPage: number, currentPage: number) {
const queryParams = `?pagesize=${tablesPerPage}&page=${currentPage}`;
this.http
.get<{ processTables: Table[]; applicationsTables: ApplicationsTable[]; maxTables: number }>(
BACKEND_URL + queryParams
)
.pipe(
map((tableData: { processTables: Table[]; applicationsTables: ApplicationsTable[]; maxTables: number }) => {
return {
processTables: tableData.processTables.map(table => {
return {
ProcessName: table.ProcessName,
PackageVersion: table.PackageVersion,
RobotType: table.RobotType,
PackagePath: table.PackagePath,
CreationTime: table.CreationTime,
Status: table.Status
};
}),
applicationsTables: tableData.applicationsTables.map(table => {
return {
ProcessName: table.ProcessName,
PackageVersion: table.PackageVersion,
WorkflowsBelongingToProcess: table.WorkflowsBelongingToProcess,
ApplicationsBelongingToWorkflow: table.ApplicationsBelongingToWorkflow
};
}),
maxTables: tableData.maxTables
};
})
)
.subscribe(transformedTablesData => {
this.processTables = transformedTablesData.processTables;
this.tablesUpdated.next({
processTables: [...this.processTables],
applicationsTables: [...this.applicationsTables],
tableCount: transformedTablesData.maxTables
});
});
}
getTableUpdateListener() {
return this.tablesUpdated.asObservable();
}
getTable(id: string) {
return this.http.get<{
ProcessName: string;
PackageVersion: string;
RobotType: string;
PackagePath: string;
CreationTime: string;
Status: string;
}>(BACKEND_URL + id);
}
}
Tables\model.ts:
export interface Table {
ProcessName: string;
PackageVersion: string;
RobotType: string;
PackagePath: string;
CreationTime: string;
Status: string;
}
export interface ApplicationsTable {
ProcessName: string;
PackageVersion: string;
WorkflowsBelongingToProcess: string;
ApplicationsBelongingToWorkflow: string;
}
Backend\models\tables.js:
Backend\controllers\tables.js:
const sequelize = require("../sequelize");
exports.getProcessTables = (req, res) => {
sequelize.query("SELECT * FROM dbo.Process", { type: sequelize.QueryTypes.SELECT})
.then(fetchedtables => {
res.status(200).json({
message: "Process table fetched from the server",
processTables: fetchedtables,
maxProcessTables: fetchedtables.length
});
});
};
exports.getApplicationsTables = (req, res) => {
sequelize.query("SELECT * FROM dbo.Applications", { type: sequelize.QueryTypes.SELECT})
.then(fetchedtables => {
res.status(200).json({
message: "Applications Table fetched from the server",
applicationTables: fetchedtables,
maxApplicationsTables: fetchedtables.length
});
});
};
Backend\routes\tables.js:
const express = require("express");
const TableController = require("../controllers/tables")
const router = express.Router({ mergeParams: true });
router.get("", TableController.getProcessTables);
router.get("", TableController.getApplicationsTables);
module.exports = router;
How can I fix it?
Many Thanks
Gennaro
You're returning an object which has a property 'retrievedTables' from the server but on the client you are trying to access 'tables' which doesn't exist.
You can fix this in either Backend\controllers\tables.js or Tables.service.ts. To fix it on the server, just change retrievedTables: tables to tables: tables so the client is getting the fields it expects. If you were to fix it on the client instead you would need to reference retrievedTables rather than tables and update your types accordingly. You're also not sending a maxTables from the server so you will want to add that. Perhaps, maxTables: tables.length.
You also need to make sure you correctly reference the property names. On the server you are sending table properties with an uppercase first letter and on the client you are reading properties with a lowercase first letter which leads to it being undefined.
Be careful with type definitions too. In this case, you are saying that the tables property of the return object of the this.http.get is of type any and then try to call its map method. Only certain types have a map method so be more specific about what you are expecting. Even specifying the type as an array of any types is better as you then guarantee the map method:
this.http
.get<{ tables: any[]; maxTables: number }>(
BACKEND_URL + queryParams
)
That could still be improved further by specifying a specific type rather than any, especially as you go on to use its properties. The better type would be the same one used for the return type of getTable which can be defined as an interface for reuse.
interface TableInstance {
processName: string;
packageVersion: string;
robotType: string;
packagePath: string;
creationTime: string;
status: string;
}
Then, you would define the type returned from get as below.
.get<{ tables: TableInstance[]; maxTables: number }>(
You can set the type of the map function too, which will work instead of doing the above, which directly addresses the problematic line from your error message.
.pipe(
map((tableData: { tables: TableInstance[]; maxTables: number }) => {
return {