How to export nested json into Excell - node.js

How can I export into Excel the following object:
{
"EAN": "541448820055773131",
"city": "Geel",
"ECAID": [
"4044123",
"9006670"
],
"document_reference": [
"EM-10006129",
"EM-10006134"
]
}
I have this React code that exports only top-level items:
import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import axios from 'axios'
import { ExportToExcel } from "../Excelexport";
const Record = (props) => (
<tr>
<td>{props.record.EAN}</td>
</tr>
);
export default function RecordList() {
const [records, setRecords] = useState([]);
const fileName = "myFile"
useEffect(() => {
async function getRecords() {
const response = await fetch(`http://localhost:5000/records/`);
const records = await response.json();
setRecords(records);
}
getRecords();
return;
}, [records.length]);
function recordList() {
return records.map((record) => {
return (
<Record
record={record}
deleteRecord={() => deleteRecord(record._id)}
key={record._id}
/>
);
});
}
return (
<div>
<h3>Record List</h3>
<ExportToExcel apiData={records} fileName={fileName} />
<table className="table table-striped" style={{ marginTop: 20 }}>
<thead>
<tr>
<th>EAN</th>
</tr>
</thead>
<tbody>{recordList()}</tbody>
</table>
</div>
);
}
ExportJS component looks like this:
import React from 'react'
import * as FileSaver from "file-saver";
import * as XLSX from "xlsx";
export const ExportToExcel = ({ apiData, fileName }) => {
const fileType =
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8";
const fileExtension = ".xlsx";
const exportToCSV = (apiData, fileName) => {
const ws = XLSX.utils.json_to_sheet(apiData);
const wb = { Sheets: { data: ws }, SheetNames: ["data"] };
const excelBuffer = XLSX.write(wb, { bookType: "xlsx", type: "array" });
const data = new Blob([excelBuffer], { type: fileType });
FileSaver.saveAs(data, fileName + fileExtension);
};
return (
<button onClick={(e) => exportToCSV(apiData, fileName)}>Export to Excel</button>
);
};
I want to see nested components like:
"ECAID": [
"4044123",
"9006670"
],
"document_reference": [
"EM-10006129",
"EM-10006134"
]
Also show up in Excel.
Is this even good approach? Possibly the array needs to be flattened somehow but I don't see how this would be done here.

Related

Not able render the images using an API

I am using multi avatar api to render random images on the UI, but I am getting the below-mentioned error. I also tried using promises to render the UI but failed to get the results.
Uncaught TypeError: The first argument must be one of type string,
Buffer, ArrayBuffer, Array, or Array-like Object. Received type
undefined
import React, { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import { ToastContainer, toast } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import axios from "axios";
import { profilePicRoute } from "../utils/apiRoutes";
import { Buffer } from "buffer";
function ProfilePic() {
const api = "https://api.multiavatar.com";
const navigate = useNavigate();
const [profilePic, setProfilePic] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const [selectedPofilePic, setSelectedPofilePic] = useState(undefined);
const toastStyles = {
position: "top-center",
};
const setProfilePicture = async () => {};
useEffect(() => {
const data = [];
for (let i = 0; i < 4; i++) {
const image = axios.get(`${api}/${Math.round(Math.random() * 1000)}`);
const buffer = Buffer(image.data);
data.push(buffer.toString("base64"));
console.log(data);
}
setProfilePic(data);
setIsLoading(false);
}, []);
return (
<div className="profilePage">
<h1>Pick your favorite profile picture</h1>
<div className="profilePics">
{profilePic.map((pic, index) => {
return (
<div
key={index}
className={`pic ${selectedPofilePic === index ? "selected" : ""}`}
>
<img
src={`data:image/svg+xml;base64,${pic}`}
alt="profile pic"
onClick={() => setSelectedPofilePic(index)}
/>
</div>
);
})}
</div>
<ToastContainer />
</div>
);
}
export default ProfilePic;
Since you were using the async you must have to use await keyword , otherwise it will return promises,and you should use the function inside the useEffect
import React, { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import { ToastContainer, toast } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import axios from "axios";
import { profilePicRoute } from "../utils/apiRoutes";
import { Buffer } from "buffer";
function ProfilePic() {
const api = "https://api.multiavatar.com";
const navigate = useNavigate();
const [profilePic, setProfilePic] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const [selectedPofilePic, setSelectedPofilePic] = useState(undefined);
const toastStyles = {
position: "top-center"
};
useEffect(() => {
const setProfilePicture = async () => {
const data = [];
for (let i = 0; i < 4; i++) {
const image = await axios.get(
`${api}/${Math.round(Math.random() * 1000)}`
);
console.log(image);
const buffer = Buffer(image.data);
data.push(buffer.toString("base64"));
}
setProfilePic(data);
setIsLoading(false);
};
setProfilePicture();
}, []);
return (
<div className="profilePage">
<h1>Pick your favorite profile picture</h1>
<div className="profilePics">
{profilePic.map((pic, index) => {
return (
<div
key={index}
className={`pic ${selectedPofilePic === index ? "selected" : ""}`}
>
<img
src={`data:image/svg+xml;base64,${pic}`}
alt="profile pic"
onClick={() => setSelectedPofilePic(index)}
/>
</div>
);
})}
</div>
<ToastContainer />
</div>
);
}
export default ProfilePic;
Hope this code will help you.
Happy Coding :)

how to pass props into layout compoent in next.js

I have a layout component that needs onAppBarInputChange prop. The onAppBarInputChange prop expected a function that take the input value from the layout component, and filter the todos based on that input value.
How do I pass the props from the todos page to the layout component?
todos.jsx
import {useState} from 'react'
import Layout from './layout'
const Todos = () => {
const [query, setQuery] = useState('')
const todos = [
{
id: 0,
text: 'make some projects'
},
{
id: 1,
text: 'fix some bugs'
},
{
id: 2,
text: 'cook food at home'
}
]
const searchedTodos = todos.filter(todo => todo.toLowerCase().includes(query.toLowerCase()))
return (
<ul>
{searchedTodos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
)
}
Todos.getLayout = function getLayout(page) {
return (
{/* how to set the query like this? */}
<Layout onAppBarInputChage={() => setQuery(e.targe.value)}>
{page}
</Layout>
)
}
export default Todos;
layout.jsx
const Layout = ({children, onAppBarInputChange}) => {
return (
<div>
<header>
<div>Todo page</div>
<input onChange={onAppBarInputChange} />
</header>
<main>{children}</main>
<footer>
some footer here
</footer>
</div>
)
}
export default Layout
Note: I had read the documentation from the next.js website, about how to add layout in next.js, however they don't show any examples on how to pass the props to the layout component
How about passing the input value through Context?
By adopting Context every component can observe the input value easily.
context/app.jsx
const AppContext = createContext(null);
const AppContextProvider = ({ children }) => {
const [query, setQuery] = useState("");
const value = {
query,
setQuery,
};
return <AppContext.Provider value={value}>{children}</AppContext.Provider>;
};
const useAppContext = () => useContext(AppContext);
export { AppContext, AppContextProvider, useAppContext };
pages/_app.jsx
function App({ Component, pageProps }) {
return (
<AppContextProvider>
{Component.getLayout(<Component {...pageProps} />)}
</AppContextProvider>
);
}
component/layout.jsx
const Layout = ({ children }) => {
const { setQuery } = useAppContext();
const onAppBarInputChange = (e) => setQuery(e.target.value);
...(snip)...
todos.jsx
const Todos = () => {
const { query } = useAppContext();
...(snip)...
};
Todos.getLayout = function getLayout(page) {
return <Layout>{page}</Layout>;
};

Firefox: React app not populating root element with weird error

I have a react application built for production and being served by a node/express server.
I have started the server locally and also deployed it to heroku and render.com
On all these systems, Chrome and Edge have no problems and no errors when accessing the site. But: Firefox won't populate the root element and shows a weird error. Only after refreshing, Firefox shows the site perfectly.
This is the error and the corresponding code ... it doesn't make sense
TypeError: wi.get(...) is undefined
ts Header.jsx:36
React 7
C scheduler.production.min.js:13
T scheduler.production.min.js:14
813 scheduler.production.min.js:14
Webpack 12
react-dom.production.min.js:189:29
React 9
C scheduler.production.min.js:13
T scheduler.production.min.js:14
(Async: EventHandlerNonNull)
813 scheduler.production.min.js:14
Webpack 12
const availableLanguages = [
{
code: "he",
name: "עברית",
country_code: "il",
dir: "rtl",
},
{
code: "en",
name: "English",
country_code: "gb",
},
{
code: "de",
name: "Deutsch",
^---- The error is showing for this code position! There is no code!
country_code: "de",
},
];
This happens on all three environments, tested from three different systems.
Does anyone know what is happening?
EDIT: Full Header.jsx
import { useState, useRef, useEffect } from "react";
import i18next from "i18next";
import { useNavigate } from "react-router-dom";
import { useTranslation } from "react-i18next";
import useOutsideClick from "../hooks/useOutsideClick";
import useAuth from "../hooks/useAuth";
import Anchor from "./Anchor";
import "./Header.css";
import claryNextLogo from "../images/ClaryNext2.png";
import cookies from "js-cookie";
import { config } from "../Environment";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { solid } from "#fortawesome/fontawesome-svg-core/import.macro";
import { useContext } from "react";
import { WSContext } from "../App";
import { useEffectOnce } from "../hooks/useEffectOnce";
import { distributionService } from "../services/distributionService";
import MessageBox from "./MessageBox";
import useRetryFetch from "../hooks/useRetryFetch";
const availableLanguages = [
{
code: "he",
name: "עברית",
country_code: "il",
dir: "rtl",
},
{
code: "en",
name: "English",
country_code: "gb",
},
{
code: "de",
name: "Deutsch",
country_code: "de",
},
];
function Header() {
const currentLanguageCode = cookies.get("i18next").split("-")[0] || "en";
const currentLanguage = availableLanguages.find(
(l) => l.code === currentLanguageCode
);
const { auth, setAuth } = useAuth();
const navigate = useNavigate();
const dropdownRef = useRef(null);
const retryFetch = useRetryFetch();
const [showLanguageDropdown, setShowLanguageDropdown] = useState(false);
const [showMessageBox, setShowMessageBox] = useState(false);
const [msgBoxTitle, setMsgBoxTitle] = useState("");
const [msgBoxButtons, setMsgBoxButtons] = useState({});
const [msgBoxInputs, setMsgBoxInputs] = useState([]);
const [msgBoxId, setMsgBoxId] = useState("");
const [msgBoxMoreJSX, setMsgBoxMoreJSX] = useState(null);
const [chatRequestUser, setChatRequestUser] = useState("");
const [chatRequestProcessId, setChatRequestProcessId] = useState("");
const [chatRequestRoomId, setChatRequestRoomId] = useState(0);
const { t } = useTranslation();
const { socket } = useContext(WSContext);
const openMessageBox = (title, id, buttons, inputs, moreJSX) => {
setMsgBoxTitle(title);
setMsgBoxId(id);
setMsgBoxButtons(buttons);
setMsgBoxInputs(inputs);
setMsgBoxMoreJSX(moreJSX);
setShowMessageBox(true);
};
const onButton = async (result) => {
console.log("MessageBox button was clicked");
console.dir(result);
if (result.btnId === "yes") {
// chat was accepted, change to ShowFullLog, join room and notify user
socket.send(
JSON.stringify({
command: "acceptchat",
payload: { email: chatRequestUser, roomId: chatRequestRoomId },
})
);
// collect necessary process log information
let response = await retryFetch(
`${config.API_BASE}/api/processes/${chatRequestProcessId}`
);
let process = await response.json();
let stateToPass = {
processId: chatRequestProcessId,
presName: process.prescriptionName,
presHistoryId: process.fiPrescriptionHistory._id,
autochat: true,
};
if (process.fiExpert?.email === auth.email) {
stateToPass.userEmail = process.fiInitiator?.email;
navigate("/fulllog", { state: stateToPass });
} else {
stateToPass.expertEmail = process.fiExpert?.email;
navigate("/userlog", { state: stateToPass });
}
} else {
// chat was refused, send message to requesting user
socket.send(
JSON.stringify({
command: "refusechat",
payload: {
email: chatRequestUser,
roomId: chatRequestRoomId,
},
})
);
}
};
useEffect(() => {
document.body.dir = currentLanguage?.dir || "ltr";
}, [currentLanguage]);
useEffectOnce(() => {
let messageUnsubscribe = distributionService
.getMessage()
.subscribe((msg) => {
console.log("Header incoming message");
switch (msg.command) {
case "requestchat":
setChatRequestUser(msg.payload.email);
setChatRequestProcessId(msg.payload.processId);
setChatRequestRoomId(msg.payload.roomId);
openMessageBox(
t("msg_chat_requested", {
user: msg.payload.email,
processId: msg.payload.processId,
}),
"requestchat",
[
{ id: "yes", text: t("yes") },
{ id: "no", text: t("no") },
],
[],
""
);
break;
}
});
return () => {
// cleanup subscription
messageUnsubscribe.unsubscribe();
};
}, []);
const login = () => {
navigate("/login");
};
const logout = async () => {
let response = await fetch(config.API_BASE + "/logout", {
credentials: "include",
});
if (!response.ok) alert(t("msg_error_logout"));
let result = await response.json();
console.log(result);
setAuth({});
socket.send(
JSON.stringify({
command: "logout",
payload: undefined,
})
);
navigate("/");
};
// const register = () => {
// navigate("/register");
// };
const showPrescriptions = () => {
//navigate("/design");
navigate("/prescriptionlist");
};
const goHome = () => {
navigate("/");
};
const openMenu = () => {
setShowLanguageDropdown(true);
};
const closeMenu = () => {
setShowLanguageDropdown(false);
};
const selectLanguage = (code) => {
i18next.changeLanguage(code);
setShowLanguageDropdown(false);
};
useOutsideClick(dropdownRef, closeMenu);
return (
<>
<div className="header-menu">
<div className="title" onClick={goHome}>
<img src={claryNextLogo} alt="ClaryNext logo" width="90" />
</div>
{!auth?.email ? (
<>
<div className="notonmobile">
<div>
<Anchor
onClick={showPrescriptions}
text={t("example_prescriptions")}
/>
</div>
</div>
<div className="rightflex notonmobile">
<div className="rightMargin">
<span> </span>
</div>
<button onClick={login}>{t("login")}</button>
<span className="leftMargin bigger">
<FontAwesomeIcon
onClick={() => openMenu()}
icon={solid("globe")}
/>
</span>
</div>
</>
) : (
<>
<div className="rightMargin notonmobile">
<Anchor
onClick={showPrescriptions}
text={t("prescriptions_and_processes")}
/>
</div>
<div className="rightflex notonmobile">
<div className="rightMargin">
<Anchor onClick={logout} text={t("logout")} />
</div>
<span className="smaller">{auth.email}</span>
<span className="leftMargin bigger">
<FontAwesomeIcon
onClick={() => openMenu()}
icon={solid("globe")}
/>
</span>
</div>
</>
)}
</div>
{showMessageBox ? (
<MessageBox
onButton={onButton}
buttons={msgBoxButtons}
text={msgBoxTitle}
inputs={msgBoxInputs}
id={msgBoxId}
moreJSX={msgBoxMoreJSX}
onClose={() => setShowMessageBox(false)}
/>
) : (
""
)}
{showLanguageDropdown ? (
<div className="language_dropdown" ref={dropdownRef}>
{availableLanguages.map((lang, idx) => (
<p key={idx} onClick={() => selectLanguage(lang.code)}>
<span
className={`flag-icon flag-icon-${lang.country_code}`}
></span>
{lang.name}
</p>
))}
</div>
) : (
""
)}
</>
);
}
export default Header;
Originally, I had the code to access the cookie in the main function scope. After I moved it into the useEffect[] callback, it started working also in Firefox.

React Hooks - How to pass props from child to parent component

In the bellow example, how can I pass imageAsUrl.imgUrl from the Child component (ImageUpload) to the parent component (UpdateCard).
CHILD Component
import React, { useState, useEffect } from 'react';
import { storage } from '../firebase';
const ImageUpload = () => {
const allInputs = { imgUrl: '' };
const [imageAsUrl, setImageAsUrl] = useState(allInputs);
const [image, setImage] = useState(null);
const handleChange = (e) => {
if (e.target.files[0]) {
setImage(e.target.files[0]);
}
};
useEffect(() => {
if (image) {
const uploadTask = storage.ref(`images/${image.name}`).put(image);
uploadTask.on(
'state_changed',
(snapshot) => {},
(error) => {
console.log(error);
},
() => {
storage
.ref('images')
.child(image.name)
.getDownloadURL()
.then((fireBaseUrl) => {
setImageAsUrl((prevObject) => ({
...prevObject,
imgUrl: fireBaseUrl,
}));
});
}
);
}
}, [image]);
return (
<>
<label className='custom-file-upload'>
<input type='file' onChange={handleChange} />
</label>
<img src={imageAsUrl.imgUrl} alt='sample' />
</>
);
};
export default ImageUpload;
PARENT Component
import React, { useState } from 'react';
import firebase from '../firebase';
import ImageUpload from './ImageUpload';
const UpdateCard = ({ card }) => {
const [originalText, setOriginalText] = useState(card.originalText);
const [translatedText, setTranslatedText] = useState(card.translatedText);
const onUpdate = () => {
const db = firebase.firestore();
db.collection('FlashCards')
.doc(card.id)
.set({ ...card, originalText, translatedText });
timeOutScroll();
};
return (
<>
<div>
{card.imageURL ? (
<img src={card.imageURL} alt='' className='img' />
) : (
<textarea
className='upload-textarea'
value={originalText}
onChange={(e) => {
setOriginalText(e.target.value);
}}
/>
)}
<ImageUpload />
</div>
<textarea
value={translatedText}
onChange={(e) => {
setTranslatedText(e.target.value);
}}
/>
<button onClick={onUpdate}>Update</button>
</>
);
};
export default UpdateCard;
Inside parent,You can define a callback function as prop ref to be called inside the child.
const ImageUpload = ({getURLtoParent}) =>{ <--------------------
const [imageAsUrl, setImageAsUrl] = useState(allInputs);
useEffect(() => {
uploadTask.on(
..............
...
);
if(imageAsUrl.imgUrl !== '')
getURLtoParent(imageAsUrl.imgUrl) <-----------------------
},[image])
}
const UpdateCart = () => {
const[imgURL,setimgURL] = useState(null)
return (
......
<ImageUpload getURLtoParent={ (url) => setimgURL(url) } /> <----------------
.........
)
}

Why am I receiving the following error when subscribed to the patientAllergies observable?

When I select the add button in the patient-allergies.component.ts, select options for the addition of patient allergies and click save, I receive the following error:
ERROR Error: Error trying to diff '[object Object]'. Only arrays and iterables are allowed.
I cannot understand why I am receiving this error as I cast the object to an array when it is passed from the rest-api.service.ts through _patientAllergies.next().
patient-allergies.component.html
<mat-card class="mat-typography">
<mat-card-title>Allergies</mat-card-title>
<mat-card-content>
<hr>
<div *ngFor="let patientAllergy of this.patientAllergies">
<h3 *ngIf="!patientAllergy.nickname"> {{ patientAllergy.fullname }} </h3>
<h3 *ngIf="patientAllergy.nickname"> {{ patientAllergy.fullname }} ({{ patientAllergy.nickname }}) </h3>
</div>
</mat-card-content>
<mat-card-actions *ngIf="isEdit">
<button mat-button class="dark" (click)="onAdd()">ADD</button>
<button mat-button class="dark" (click)="onRemove()">REMOVE</button>
</mat-card-actions>
</mat-card>
<mat-card *ngIf="isLoading" style="display: flex; justify-content: center; align-items: center">
<mat-progress-spinner class="mat-spinner-color" mode="indeterminate"></mat-progress-spinner>
</mat-card>
patient-allergies.component.ts
import {Component, OnInit, Input, Pipe, PipeTransform} from '#angular/core';
import {RestAPIService} from 'src/app/rest-api.service';
import {ActivatedRoute} from '#angular/router';
import {MatDialog} from '#angular/material';
#Component({selector: 'app-patient-allergies', templateUrl: './patient-allergies.component.html', styleUrls: ['./patient-allergies.component.css']})
export class PatientAllergiesComponent implements OnInit {
#Input()isEdit : boolean = false;
constructor(private restAPIService : RestAPIService, private route : ActivatedRoute, public dialog : MatDialog) {}
isLoading : boolean;
patientAllergies;
subscription = null;
ngOnInit() {
this.isLoading = true;
this
.restAPIService
.getPatientAllergies(this.route.snapshot.params.id);
this.subscription = this
.restAPIService
.patientAllergies
.subscribe((patAllergies) => {
this.patientAllergies = patAllergies as [];
this.isLoading = false;
});
}
onAdd() {
let dRef = this
.dialog
.open(AllergiesDialogComponent, {
disableClose: true,
height: '800px',
width: '600px',
data: {
isAdding: true,
patientAllergies: this.patientAllergies
}
});
dRef
.afterClosed()
.subscribe((res) => {
res.forEach((ele) => {
this
.restAPIService
.addPatientAllergies(this.route.snapshot.params.id, ele.value.allergyID);
});
});
}
onRemove() {
let dRef = this
.dialog
.open(AllergiesDialogComponent, {
disableClose: true,
height: '800px',
width: '600px',
data: {
isAdding: false,
patientAllergies: this.patientAllergies
}
});
dRef
.afterClosed()
.subscribe((res) => {
res.forEach((ele) => {
this
.restAPIService
.deletePatientAllergies(this.route.snapshot.params.id, ele.value.allergyID);
});
})
}
}
import {Inject} from '#angular/core';
import {MatDialogRef, MAT_DIALOG_DATA} from '#angular/material';
import {DialogData} from 'src/app/patient/patient.component';
#Component({selector: 'app-allergies-dialog', templateUrl: './allergies-dialog.component.html', styleUrls: ['./allergies-dialog.component.css']})
export class AllergiesDialogComponent implements OnInit {
allergies = [];
filterName : string;
subscription;
constructor(public dialogRef : MatDialogRef < AllergiesDialogComponent >, #Inject(MAT_DIALOG_DATA)public data : DialogData, private restAPIService : RestAPIService) {}
ngOnInit() {
this
.restAPIService
.getAllergies();
if (this.data['isAdding']) {
this.subscription = this
.restAPIService
.allergies
.subscribe((allergies) => {
let allergiesArr = allergies as [];
this.allergies = [];
let patientAllergyNames = [];
this
.data['patientAllergies']
.forEach(patientAllergy => {
patientAllergyNames.push(patientAllergy['fullname'])
});
allergiesArr.forEach(allergy => {
if (!patientAllergyNames.includes(allergy['fullname']))
this.allergies.push(allergy);
}
);
})
} else {
this.allergies = this.data['patientAllergies'];
}
}
onClose(selectedOptions) {
if (this.data['isAdding'])
this.subscription.unsubscribe();
// either [] or the IDs of the objects to add/remove
this
.dialogRef
.close(selectedOptions);
}
}
#Pipe({name: 'filterOnName'})
export class filterNames implements PipeTransform {
transform(listOfObjects : any, nameToFilter : string) : any {
let allergyArr = listOfObjects as[];
let matchedObjects = [];
if (!listOfObjects)
return null;
if (!nameToFilter)
return listOfObjects;
allergyArr.forEach(allergyObj => {
let fullname : string = allergyObj['fullname'];
let nickname : string = allergyObj['nickname'];
let fullnameLower = fullname.toLowerCase();
let nicknameLower = fullname.toLowerCase();
let filter = nameToFilter.toLowerCase();
if (nickname) {
if ((fullnameLower.includes(filter) || nicknameLower.includes(filter)))
matchedObjects.push(allergyObj);
}
else {
if (fullnameLower.includes(filter))
matchedObjects.push(allergyObj);
}
});
return matchedObjects;
}
}
rest-api.service.ts
private _allergies;
private allergiesSubject = new Subject();
allergies = this
.allergiesSubject
.asObservable();
private _patientAllergies;
private patientAllergiesSubject = new Subject();
patientAllergies = this
.patientAllergiesSubject
.asObservable();
getPatientAllergies(patientID) {
const request = {
headers: {},
response: true
};
API
.get('DiagnetAPI', '/v1/patients/' + patientID + '/allergies', request)
.then(resp => {
this._patientAllergies = resp.data;
this
.patientAllergiesSubject
.next(this._patientAllergies);
})
.catch((err) => console.log(err))
}
addPatientAllergies(patientID, allergyID) {
const request = {
headers: {},
response: true,
body: allergyID
};
API
.post('DiagnetAPI', '/v1/patients/' + patientID + '/allergies', request)
.then(resp => {
console.log(resp.data);
this._patientAllergies = resp.data;
this
.patientAllergiesSubject
.next(this._patientAllergies);
})
.then(() => {
this.getPatientAllergies(patientID);
})
.catch((err) => console.log(err))
}
deletePatientAllergies(patientID, allergyID) {
const request = {
headers: {},
response: true
};
API
.del('DiagnetAPI', '/v1/patients/' + patientID + '/allergies/' + allergyID, request)
.then((res) => console.log(res))
.then(() => {
this.getPatientAllergies(patientID);
})
.catch((err) => console.log(err))
}
If your response from the api is not an array but an object, you can use the keyvalue pipe in your *ngFor loop.
In your case patientAllergy would be an object with two keys, the key and the value so you can access the nickname like this: patientAllergy.value.nickname
your template:
<div *ngFor="let patientAllergy of this.patientAllergies | keyvalue">
<h3 *ngIf="!patientAllergy.value.nickname"> {{ patientAllergy.value.fullname }} </h3>
<h3 *ngIf="patientAllergy.value.nickname"> {{ patientAllergy.value.fullname }} ({{ patientAllergy.value.nickname }}) </h3>
</div>

Resources