MEAN-Stack - Cannot read second SQL Table from server - sql

I'm developing a WebApp with MEANStack, using Sequelize to access SQL Databases. I managed to read, with the code below, an SQL Table. Unfortunately I get the following error on the client's side when trying to read a second SQL (Edit after first Answer)
Table: core.js:1673 ERROR TypeError: tableData.processTables.map is not a function at MapSubscriber.project (tables.service.ts:32)
tables.service.ts:32 of the error is :
processTables: tableData.processTables.map(table => {
And here's how the error on the client's side looks like:
Table: core.js:1673 ERROR TypeError: tableData.processTables.map is not a function at MapSubscriber.project
Here's my code (edit)
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 { ProcessTable, 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: ProcessTable[] = [];
applicationsTables: ApplicationsTable[] = [];
isLoading = false;
totalTables = 0;
tablesPerPage = 5;
currentPage = 1;
pageSizeOptions = [1, 2, 5, 10];
displayedprocessTablesColumns: string[] = ["ProcessName", "PackageVersion", "RobotType", "PackagePath", "CreationTime", "Status" ];
displayedApplicationsTablesColumns: string[] = ["ProcessName", "PackageVersion" ];
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.getProcessTables(this.tablesPerPage, this.currentPage);
this.userId = this.authService.getUserId();
this.tablesSub = this.tablesService
.getTableUpdateListener()
.subscribe((tableData: { processTables: ProcessTable[]; applicationsTables: ApplicationsTable[]; tableCount: number }) => {
this.isLoading = false;
this.totalTables = tableData.tableCount;
this.processTables = tableData.processTables;
this.applicationsTables = tableData.applicationsTables;
});
this.userIsAuthenticated = this.authService.getIsAuth();
// console.log("Is authenticated: " + this.userIsAuthenticated);
this.authStatusSub = this.authService
.getAuthStatusListener()
.subscribe(isAuthenticated => {
this.userIsAuthenticated = isAuthenticated;
});
}
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 { ProcessTable, ApplicationsTable } from "./tables.model";
const BACKEND_URL = environment.apiUrl + "/tables/";
#Injectable({ providedIn: "root" })
export class TablesService {
private processTables: ProcessTable[] = [];
private applicationsTables: ApplicationsTable[] = [];
private tablesUpdated = new Subject<{ processTables: ProcessTable[]; applicationsTables: ApplicationsTable[]; tableCount: number }>();
constructor(private http: HttpClient, private router: Router) {}
getProcessTables(tablesPerPage: number, currentPage: number) {
const queryParams = `?pagesize=${tablesPerPage}&page=${currentPage}`;
this.http
.get<{ processTables: ProcessTable[]; applicationsTables: ApplicationsTable[]; maxTables: number }>(
BACKEND_URL + queryParams
)
.pipe(
map((tableData: { processTables: ProcessTable[]; applicationsTables: ApplicationsTable[]; maxTables: number }) => {
console.log(tableData);
console.log(tableData.processTables);
console.log(tableData.applicationsTables);
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();
}
}
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\controllers\tables.js:
const sequelize = require("../sequelize");
const getProcessTables = (req, res) => {
return sequelize
.query("SELECT * FROM dbo.Process", { type: sequelize.QueryTypes.SELECT })
.then(fetchedtables => {
return {
message: "Process table fetched from the server",
processTables: fetchedtables,
maxProcessTables: fetchedtables.length
};
});
};
const getApplicationsTables = (req, res) => {
return sequelize
.query("SELECT * FROM dbo.Applications", {
type: sequelize.QueryTypes.SELECT
})
.then(fetchedtables => {
return {
message: "Applications Table fetched from the server",
applicationsTables: fetchedtables,
maxApplicationsTables: fetchedtables.length
};
});
};
exports.getAllTables = (req, res) => {
return Promise.all([
getApplicationsTables(req, res),
getProcessTables(req, res)
]).then(tables => {
res.status(200).json({
applicationsTables: tables[0],
processTables: tables[1]
});
});
};
Backend\routes\tables.js:
const express = require("express");
const TableController = require("../controllers/tables")
const router = express.Router({ mergeParams: true });
router.get("", TableController.getAllTables);
module.exports = router;
How can I fix it?
Many Thanks
Gennaro

There are two errors that I can see.
You are querying the /tables/ route but not defining the route.
You Created two request handler and used the same route. What is does is the request goes to the first app.get block. Not the second one.
So, first you need to Merge the getProcessTable and getApplicationTable into one consolidated function.
const sequelize = require("../sequelize");
const getProcessTables = (req, res) => {
return sequelize.query("SELECT * FROM dbo.Process", { type: sequelize.QueryTypes.SELECT})
.then(fetchedtables => {
return {
message: "Process table fetched from the server",
processTables: fetchedtables,
maxProcessTables: fetchedtables.length
};
});
};
const getApplicationsTables = (req, res) => {
return sequelize.query("SELECT * FROM dbo.Applications", { type: sequelize.QueryTypes.SELECT})
.then(fetchedtables => {
return {
message: "Applications Table fetched from the server",
applicationsTables: fetchedtables,
maxApplicationsTables: fetchedtables.length
};
});
};
exports.getAllTables = (req, res) =>{
return Promise.all([getApplicationsTables(req,res), getProcessTables(req,res)])
.then(tables =>{
res.status(200).json({
applicationsTables: tables[0],
processTables: tables[1]
});
})
}
and then maybe route it to tables
router.get("/tables/", TableController.getAllTables);
Now, you need to change the map line.
processTables: tableData.processTables.map will be,
processTables: tableData.processTables.processTables.map(table => {

Related

Getters in store with pinia don't loading

With pinia on VueJS, i use a store for my licences and a licences have a number who references to a project. The projects list with informations are in an other store.
so i make getters in the licence store to get the informations of the projet (name, entreprise...).
But, when the page loading, the value of getters don't appears, and when i go in the extension on Vuejs Web browser for seeing my stores, the values appears. And I don't understand how to use the getters in my template ... I tried but no results....
I make a video to demonstrate my problem :
https://www.youtube.com/watch?v=Er4xcQ-Mq2Y
Thanks for helping !
My viewpage :
<h1>Licences actives (de type "DEV")</h1>
<table>
<tr>
<th>Numero/Clé</th>
<th>Fin d'activation</th>
<th>type</th>
<th>Entreprise</th>
<th>N° d'Affaire<br />(Projet)</th>
<th>Projet</th>
<th>Responsable</th>
<th>Version</th>
<th>Version Soft</th>
<th>Durée restante <br />(jours)</th>
</tr>
<tr v-for="article in currentList" :key="article.numero">
<td style="color: red">{{ article.numero }}</td>
<td>{{ Date_formate(new Date(article.fin_activation)) }}</td>
<td>{{ article.type }}</td>
<td style="color: red">{{ article.entreprise }}</td>
<td>{{ article.affaire }}</td>
<td>{{ article.name }}</td>
<td>{{ article.responsable }}</td>
<td>{{ article.version }}</td>
<td>{{ article.version_soft }}</td>
<td>
{{
Math.round((new Date(article.fin_activation) - Date.now()) / 86400000)
}}
</td>
</tr>
<br />
</table>
</template>
<script setup>
import { computed, onMounted, ref } from "#vue/runtime-core";
import { useListLicences } from "../stores/licence";
import { Date_formate } from "../plugin/functions";
const useListLicences2 = useListLicences();
const currentList = computed(() => {
return useListLicences2.$state.list;
</script>
The licence store in src/stores :
import { defineStore } from "pinia";
import { useListProjets } from "./projets";
const entreprises = useListEntreprises();
const projets = useListProjets();
export const useListLicences = defineStore({
id: "licences",
state: () => ({
list: [],
}),
persist: true,
getters: {
getList: (state) => state.list,
getName: (state) => //pour afficher Projet dans le tableau
state.list.map((licence) => {
projets.list.map((projet) => {
if (licence.projet_affaire == projet.affaire) {
return licence.name = projet.projetNom;
}
});
}),
getResponsable: (state) => //pour afficher Responsable
state.list.map((licence) => {
projets.list.map((projet) => {
if (licence.projet_affaire == projet.affaire) {
return licence.responsable = projet.userPseudo;
}
});
}),
getEntreprise: (state) => //pour afficher Entreprise
state.list.map((licence) => {
projets.list.map((projet) => {
if (licence.projet_affaire == projet.affaire) {
return licence.entreprise = projet.entrepriseNom;
}
});
}),
getAffaire: (state) => //pour afficher le Num d'affaire
state.list.map((licence) => {
projets.list.map((projet) => {
if (licence.projet_affaire == projet.affaire) {
return licence.affaire = projet.affaire;
}
});
}),
getID_Entreprise: (state) =>
state.list.map((licence) => {
entreprises.list.map((entreprise) => {
if (licence.entreprise == entreprise.entreprise_nom) {
return licence.ID_entreprise = entreprise.id;
}
});
}),
getContacts: (state) =>
state.list.map((licence) => {
projets.list.map((projet) => {
if (licence.projet_affaire == projet.affaire) {
return licence.contact1 = projet.email1;
}
});
}),
},
});
use this:
const currentList = computed(() => {
// removed: return useListLicenses2.$state.list
return useListLicences2.list;
})
$state is an unwrap object so there will be no response

issues getting data to show on screen from SQL

This is a React Js project that is using Axios, Cors, Express, and Node JS connecting to an SQL database.
I am trying to get the data from an SQL table and have it show on the screen either in a div or p tag each row on its own line. At this time I am able to get it to console.log inside my VS Code terminal from my server.js side as well as console log the data inside my browser console from my frontend of ProductList.js. I do not get any errors in any of my consoles just the data that I would like displayed on the screen.
The below is my server.js
const bodyParser = require('body-parser');
const express = require('express');
cors = require('cors');
const app = express();
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }))
app.use(bodyParser.urlencoded({ extended: true }));
app.get('/collectors', function (req, res) {
var sql = require("mssql");
const config = {
user: 'XXXXXXX',
password: 'XXXXXXX',
server: 'XXXXXXX',
database: 'XXXXXXX',
options: {
trustServerCertificate: true,
trustedConnection: false,
enableArithAbort: true
},
}
sql.connect(config).then(pool => {
return pool.request()
.query('select * from CollectorAssignment.tCollectors ').then(result => {
console.dir(result)
res.send(result)
})
}).catch(err => {
console.log("error at line24: ", err)
})
sql.on('error', err => {
console.log("error at line28: ", err)
})
});
app.listen(5000, () => {
console.log('listening on port 5000')
});
The below is my ProductList.js
import React from "react";
import axios from 'axios';
class ProductList extends React.Component {
state = {
loading: true,
error: "",
data: []
};
componentDidMount() {
this.getCollectorList();
}
getCollectorList = () => {
this.setState({ loading: true });
return axios
.get(
'http://localhost:5000/collectors'
)
.then(result => {
console.log(result);
this.setState({
CollectorList: result.data.items,
loading: false,
error: false
});
})
.catch(error => {
console.error("error: ", error);
this.setState({
error: `${error}`,
loading: false
});
});
};
render() {
const { loading, error, data } = this.state;
if (loading) {
return <p className="productList">Loading ...</p>;
}
if (error) {
return (
<p className="productList">
There was an error loading the collectors.{" "}
<button onClick={this.loadData}>Try again</button>
</p>
);
}
return (
<div className="productList">
<h1>Collector List</h1>
{data.map(result => <p className="productList">{result.CollectorList}</p>)}
</div>
);
}
}
export default ProductList;
Screenshot of my VS Code console data I get the same info in my browser console which is a total of 16 rows of data that I need displayed on the screen
I have gotten this to start working for me here are the changes I have made to the two files I provided. I was calling a few areas improperly and found that I was looking at some ways for SQL and I am using SQL so some connections to the DB were different which caused some issues small syntax things mainly.
Server.js
const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
const config = require('./src/dbfiles/dbConfig')
const app = express();
app.use(cors());
app.use(bodyParser.json({ extended: true }));
var sql = require("mssql");
app.get('/getCollectors', (req, res) => {
sql.connect(config).then(pool => {
return pool.request()
.query('SELECT * FROM CollectorAssignment.tCollectorsTest').then(result => {
res.send(result.recordset)
})
})
})
app.post('/addCollector', function (req, res) {
sql.connect(config).then(pool => {
return pool.request()
.query(`INSERT INTO CollectorAssignment.tCollectorsTest
(
Active,
FirstName,
MiddleInitial,
LastName,
CollectorCode,
CreationDate,
CollectionTeamID
) VALUES (
${req.body.Active},
'${req.body.FirstName}',
'${req.body.MiddleInitial}',
'${req.body.LastName}',
'${req.body.CollectorCode}',
'${req.body.CreationDate}',
1
)`)
.then(result => {
res.send(result)
})
})
});
app.post('/updateCollector', function (req, res) {
sql.connect(config).then(pool => {
return pool.request()
.query(`UPDATE CollectorAssignment.tCollectorsTest
SET ${req.body} = ${req.body}
WHERE ${req.body} = ${req.body}
`)
.then(result => {
res.send(result)
})
})
});
app.delete('/deleteCollector/:CollectorID', (req, res) => {
sql.connect(config).then(pool => {
return pool.request()
.query(`DELETE FROM CollectorAssignment.tCollectorsTest WHERE CollectorID = ${req.params.CollectorID}`).then(result => {
res.send(result.recordset)
})
})
})
app.listen(5000, () => {
console.log('running on port 5000');
})
ProductList.js
import "./userList.css";
import React from "react";
import axios from 'axios';
import { Link } from "react-router-dom";
import { DeleteOutline, Edit } from "#material-ui/icons";
class UserList extends React.Component {
state = {
Collectors: '',
collectorList: []
}
componentDidMount() {
this.getCollectors()
}
getCollectors = () => {
axios.get('http://localhost:5000/getCollectors')
.then((result) => result.data)
.then((result) => {
this.setState({collectorList: result});
});
};
render() {
return (
<div className="userList">
<h3>Collectors</h3>
<table className="blueTableHeaders">
<thead>
<tr>
<th>Active</th>
<td>Collectors</td>
<td>Aging Bucket</td>
<td>Program Code</td>
<td>Finance Company</td>
<td></td>
</tr>
</thead>
</table>
{this.state.collectorList.map((Collectors) => (
<div>
<table className="blueTableData">
<thead>
<tr>
<th><input type="checkbox" name="Active" defaultChecked={Collectors.Active === false ? false : true}/></th>
<td>{Collectors.FirstName} {Collectors.LastName} | {Collectors.CollectorCode}</td>
<td>
<input type="checkbox" />
<input type="checkbox" />
<input type="checkbox" />
<input type="checkbox" />
</td>
<td>
<input type="checkbox" />
<input type="checkbox" />
<input type="checkbox" />
<input type="checkbox" />
</td>
<td>
<input type="checkbox" />
<input type="checkbox" />
<input type="checkbox" />
</td>
<td>
<Link to="/updateUser:CollectorID">
<Edit className="editCollector" />
</Link>
<Link to="/deleteUser:CollectorID">
<DeleteOutline className="deleteCollector"/>
</Link>
</td>
</tr>
</thead>
</table>
</div>
))}
<Link to="/newUser">
<button className="userListAddButton">Add Collector</button>
</Link>
<Link to="/deleteUser">
<button className="userListDeleteButton">Delete Collector</button>
</Link>
</div>
);
}
}
export default UserList;

Nuxt:Vuex update data from button click not working. error "Cannot read property 'results' of undefined"

I've using the store to update data in a pages file. The really strange thing is the data sometimes appears if i add/remove "results" from state.mbbalance.results
If I add .results I get an error.
Cannot read property 'results' of undefined
If I remove the .results and leave as state.mbbalance ,click the button
I can see the data networks tab in browser as a valid response. But the data won't display on webpage.
results: [{id: 7, balance: 30, exposure: 0, free_funds: 30},…]
I'm not sure why I get the error as the code should work as expected and display a list of balances from the backend. Can anyone tell me why the data won't update?
versions
"nuxt": "2.15.7",
"#nuxtjs/axios": "^5.13.6",
This is my store.
store/index.js
export default {
state: () => ({
loggedIn: false,
user: null
}),
actions: {
async nuxtServerInit ({ commit }, { req, app }) {
console.log('nuxtServerInit', req.session.authToken)
if (req.session.authToken) {
const data = await app.$axios.$get('/api/auth/me/')
commit('SET_USER', data)
} else {
commit('SET_USER', null)
}
},
async login ({ commit }, creds) {
await this.$axios.$post('/auth/login/', creds)
const data = await this.$axios.$get('/api/auth/me/')
commit('SET_USER', data)
},
logout ({ commit }) {
this.$axios.$post('/auth/logout/')
commit('SET_USER', null)
},
async getMBalance(context) {
const res = await this.$axios.get('api/balance/')
if (!res.error) {
context.commit("SET_MB_BALANCE", res.data)
} else {
context.commit("SET_MB_BALANCE", [])
}
}
}
}
pages/balance.vue
<template>
<div id="app">
<button class="btn btn-primary" #click.prevent="updateItem">get</button>
<table>
<thead class="thead-dark">
<tr>
<th>Balance</th>
</tr>
</thead>
<tbody>
<tr v-for="o in mbbaldata" :key="o.id">
<td>{{o.balance}}</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
import { mapMutations, mapState } from 'vuex';
export default {
computed: {
...mapState({
mbbaldata: (state) => state.mbbalance.results,
})
},
methods: {
updateItem() {
this.mb_balance = this.$store.dispatch('getMBalance')
}
}
}
</script>

How to hook multiple times in component in Vue 3

I saw in one lesson that we can create with composition api hook usePromise but the problem that I have simple crud app with to-do list, where I have create, delete, get API calls and I don't understand how I can use this hook for all api in one component. All call works correct but the loading is not, it works only at first call PostService.getAll() and then loader isn't triggered. Thanks for response.
usePromise.js
import { ref } from 'vue';
export default function usePromise(fn) {
const results = ref(null);
const error = ref(null);
const loading = ref(false);
const createPromise = async (...args) => {
loading.value = true;
error.value = null;
results.value = null;
try {
results.value = await fn(...args);
} catch (err) {
error.value = err;
} finally {
loading.value = false;
}
};
return { results, loading, error, createPromise };
}
apiClient.js
import axios from 'axios';
export default axios.create({
baseURL: 'https://jsonplaceholder.typicode.com/',
withCredentials: false,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
});
PostService.js
import apiClient from './apiClient';
const urlPath = '/posts';
export default {
getAll() {
return apiClient.get(urlPath);
},
add(post) {
return apiClient.post(urlPath, post);
},
delete(id) {
return apiClient.delete(`${urlPath}/${id}`);
},
};
List.vue
<template>
<div>
<VLoader v-if="loading" />
<template v-else>
<table class="table">
<thead>
<tr>
<th>Id</th>
<th>Title</th>
<th></th>
</tr>
</thead>
<tbody>
<tr v-for="post in posts" :key="post.id">
<td>{{ post.id }}</td>
<td>{{ post.title }}</td>
<td>
<button class="btn btn-danger ml-1" #click="deletePost(post.id)">Delete</button>
</td>
</tr>
</tbody>
</table>
</template>
</div>
</template>
<script>
import { ref, computed, watch, unref } from 'vue';
import PostService from '#/services/PostService';
import usePromise from '#/use/usePromise';
export default {
setup() {
const posts = ref([]);
const post = ref({
title: '',
body: '',
});
const {
results: postsResultRef,
loading: postsLoadingRef,
createPromise: getAllPosts,
} = usePromise(() => PostService.getAll());
getAllPosts(); //get all posts by initialize component
const {
results: postDeleteResultRef,
loading: postDeleteLoadingRef,
createPromise: deletePost,
} = usePromise((id) => PostService.delete(id).then((result) => ({ ...result, removedId: id })));
watch(postsResultRef, (postsResult) => {
posts.value = postsResult.data;
});
watch(postDeleteResultRef, (postDeleteResult) => {
if (postDeleteResult.status === 200) {
posts.value = posts.value.filter((item) => item.id != postDeleteResult.removeId);
// unref(posts).splice(/* remove postDeleteResult.removedId */);
}
});
const loading = computed(() => [postsLoadingRef, postDeleteLoadingRef].map(unref).some(Boolean));
return { posts, post, loading };
},
};
</script>
A ref keeps reactive reference to a value that is supposed to exist through the entire component lifecycle. It stays reactive on other places of a component - a template, computed properties, watchers, etc.
Hooks like usePromise are supposed to be set up inside setup function (hence the name):
const { results, loading, createPromise } = usePromise(() => PostService.getAll()
For multiple requests, multiple hook results can be composed:
const posts = ref([]);
const { results: postsResultRef, loading: postsLoadingRef, createPromise: getAllPosts } = usePromise(() =>
PostService.getAll()
);
const { results: postDeleteResultRef, loading: postDeleteLoadingRef, createPromise: deletePost } = usePromise(id =>
PostService.delete(id).then(result => ({...result, removedId: id }))
);
...
watch(postsResultRef, postsResult => {
posts.value = postsResult.data
});
watch(postDeleteResultRef, postDeleteResult => {
if (postDeleteResult.status === 200)
unref(posts).splice(/* remove postDeleteResult.removedId */)
});
...
const loading = computed(() => [postsLoadingRef, postDeleteLoadingRef, ...].map(unref).some(Boolean))
getAllPosts, etc are supposed to be used as a callback, e.g. in a template, a promise it returns doesn't need to be handled explicitly and chained in general, as its current state is already reflected in hook results. This indicates a potential flaw in the hook, as createPromise arguments are unknown at the time when a result is available, this requires to provide a parameter explicitly for delete result.
The problem is only the first loading ref is returned from setup(). The others are hidden and unused inside each method.
One solution is to track the active loading ref in state, returned from setup():
Declare state.loading.
export default {
setup() {
const state = reactive({
//...
loading: null,
})
//...
}
}
Set state.loading to the loading ref within each method.
const fetchPosts = () => {
const { results, loading, createPromise } = usePromise(/*...*/)
state.loading = loading
//...
}
const deletePost = (id) => {
const { results, loading, createPromise } = usePromise(/*...*/)
state.loading = loading;
//...
}
const onSubmit = () => {
const { results, loading, createPromise } = usePromise(/*...*/)
state.loading = loading
//...
}
Remove the loading ref that was originally returned from setup(), since we already have state.loading, and toRefs(state) would expose loading to the template already:
export default {
setup() {
//...
//return { toRefs(state), loading }
// ^^^^^^^
return { toRefs(state) }
}
}
demo

Displaying a specific data in html ionic

I'm trying to display a specific username using the user uid in firebase, but i can't display it in the html. I try to get the username from the userColl by the Uid. For the html, i tried using *ngFor, but there's problem indicating that it can only be used for array
Here's is the code:
read_Username() {
return this.firestore.collection('userColl',ref => ref
.where('userUid', '==', this.userUid)).snapshotChanges();
//return this.firestore.collection('/userColl/userUid' + this.userUid).snapshotChanges();
}
ngOnInit() {
this.user = firebase.auth().currentUser;
//this.userUid = firebase.auth().currentUser.uid
this.read_Username().subscribe(data => {
this.user = data.map(e => {
if(this.user != null){
return {
id: e.payload.doc.id,
userUid: e.payload.doc.data()['userUid'],
username: e.payload.doc.data()['username'],
};
}
})
console.log(this.user);
});
}
The html:
<ion-card class="welcome-card">
<img src="/assets/shapes.svg" alt=""/>
<div>
<ion-card-header >
<ion-card-subtitle></ion-card-subtitle>
<ion-card-title> {{userUid}}</ion-card-title>
</ion-card-header>
<ion-card-content>
<p>{{username?.username}}</p>
</ion-card-content>
Try changing your codes to this:
read_Username() {
return this.firestore.collection('userColl',ref => ref
.where('userUid', '==', this.userUid)).snapshotChanges();
//return this.firestore.collection('/userColl/userUid' + this.userUid).snapshotChanges();
}
userUid:string;
username:string;
ngOnInit() {
this.user = firebase.auth().currentUser;
//this.userUid = firebase.auth().currentUser.uid
this.read_Username().subscribe(data => {
this.user = data.map(e => {
if(this.user != null){
return {
id: e.payload.doc.id,
userUid: e.payload.doc.data().userUid,
username: e.payload.doc.data().username,
};
}
})
console.log(this.user);
});
}
The html:
<ion-card class="welcome-card">
<img src="/assets/shapes.svg" alt=""/>
<div>
<ion-card-header >
<ion-card-subtitle></ion-card-subtitle>
<ion-card-title>{{userUid}}</ion-card-title>
</ion-card-header>
<ion-card-content>
<p>{{username}}</p>
</ion-card-content>
If it doesn't work, change the
userUid: e.payload.doc.data().userUid,
username: e.payload.doc.data().username,
to:
userUid: e.payload.doc.data()['userUid'],
username: e.payload.doc.data()['username'],