Sending Enum Value with spaces using Prisma and MySQL - mysql

I am going to try my best to explain this issue...
I am working on building a request tracker App in NextJS using Prisma as the ORM and MySQL as the Database.
I am wanting to be able to send the Status to the Database without having to add the Underscores into it.
Is this possible?
Here is my Prisma Schema
generator client {
provider = "prisma-client-js"
binaryTargets = ["native", "darwin"]
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
model requests {
id Int #id #default(autoincrement())
project_id String #db.VarChar(255)
request_type requests_request_type?
name String? #db.VarChar(255)
account_name String? #db.VarChar(255)
legacy_org requests_legacy_org?
total_hours_spent Int?
status requests_status?
updated_on DateTime? #default(now()) #db.DateTime(0)
comment String? #db.Text
}
enum requests_request_type {
Rem
Add_on #map("Add on")
New_Logo #map("New Logo")
Migration
}
enum requests_legacy_org {
CSC
ES
}
enum requests_status {
To_be_Started #map("To be Started")
Work_in_Progress #map("Work in Progress")
Awaiting_Customer_Confirmation #map("Awaiting Customer Confirmation")
Completed
}
This function creates the entry in the Database
import type {NextApiRequest, NextApiResponse} from 'next';
import prisma from '../../../../lib/prisma';
export default async function handle(
req: NextApiRequest,
res: NextApiResponse
) {
const {name, projectID, accountName, status, requestType, totalHours} =
req.body;
const result = await prisma.requests.create({
data: {
name: name,
project_id: projectID,
account_name: accountName,
status: status,
request_type: requestType,
total_hours_spent: totalHours,
},
});
res.json(result);
}
This is my page to add the request
import React, {useState} from 'react';
import Router from 'next/router';
const AddRequest = () => {
const [name, setName] = useState('');
const [projectID, setProjectID] = useState('');
const [accountName, setAccountName] = useState('');
const [status, setStatus] = useState('To_be_Started');
const [requestType, setRequestType] = useState('');
const [totalHours, setTotalHours] = useState(0);
const submitData = async (e: React.SyntheticEvent) => {
e.preventDefault();
try {
const data = {
name,
projectID,
accountName,
status,
requestType,
totalHours,
};
await fetch('/api/requests/add', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(data),
});
await Router.push('/requests');
} catch (error) {
console.log(error);
}
};
return (
<>
<div className="container flex-auto">
<form onSubmit={submitData}>
<h1 className="text-3xl">New Request</h1>
<div className="text-center grid grid-cols-2 gap-3">
<input
className="border border-black"
type="text"
placeholder="Name"
value={name}
onChange={(e) => {
setName(e.target.value);
}}
/>
<input
className="border border-black"
type="text"
placeholder="Project ID"
onChange={(e) => {
setProjectID(e.target.value);
}}
value={projectID}
/>
<input
className="border border-black"
type="text"
placeholder="Account Name"
onChange={(e) => {
setAccountName(e.target.value);
}}
value={accountName}
/>
<input
className="border border-black"
type="text"
placeholder="Request Type"
onChange={(e) => {
setRequestType(e.target.value);
}}
value={requestType}
/>
<button
className="border border-black bg-red-100"
disabled={!name || !projectID}
type="submit">
Create
</button>
</div>
</form>
</div>
</>
);
};
export default AddRequest;

Enum Identifiers having embedded spaces are not supported yet.
We have a Feature Request for adding support to allow arbitrary enum values here: #4954. Please feel free to add a comment to the Feature Request so that we can prioritise it.
For this enum:
enum requests_status {
To_be_Started #map("To be Started")
Work_in_Progress #map("Work in Progress")
Awaiting_Customer_Confirmation #map("Awaiting Customer Confirmation")
Completed
}
The generated types for now would be:
export const requests_status: {
To_be_Started: 'To_be_Started',
Work_in_Progress: 'Work_in_Progress',
Awaiting_Customer_Confirmation: 'Awaiting_Customer_Confirmation',
Completed: 'Completed'
};
The #map only applies to the schema and right at query time.

Related

TypeError: allOrg.map is not a function [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed last year.
Improve this question
I am trying to loop through all the objects in a array state in react, hence I used map function. Here is the block of code where I used the map function:
return(
<div>
<Navbar/><br/>
{
allOrg.map((data: orgType, index: number) => {
/*<Org key={index} userId = {UserId} orgName = {data.orgName} /> */
<h1>{index} {UserId} {data.orgName}</h1>
})
}
<div className = "OrgRow">
<button className = "OrgTeams" onClick={createOrg}>Add Org</button>
{createOrgForm}
</div>
</div>
)
But it is showing me "TypeError: allOrg.map is not a function" error. picture of the error I looked for similar errors on stackoverflow, but only suggestions were that map can only be used with arrays. And here my state is an array only, still this problem is persisting. Here is my declaration of the state named "allOrg":
import React,{useState, useEffect} from "react";
import { useForm } from "react-hook-form";
import Navbar from "./navBar";
import Org from "./org";
import "../../style/auth.css";
import "../../style/home.css";
interface orgType{
orgId: string;
orgName: string;
}
function Home(): JSX.Element{
//let UserId: string = "Ronak";
const initialOrg = {
orgId: "",
orgName: ""
}
const [UserId, setUserId] = useState<string>("userId");
const [createOrgForm, setForm] = useState(<div></div>);
const [allOrg, setAllOrg] = useState<orgType[]>([initialOrg]);
const [orgAdded, changeState] = useState(true);
const {register, handleSubmit} = useForm();
I am also pasting images containing my entire code for that component:
import React,{useState, useEffect} from "react";
import { useForm } from "react-hook-form";
import Navbar from "./navBar";
import Org from "./org";
import "../../style/auth.css";
import "../../style/home.css";
interface orgType{
orgId: string;
orgName: string;
}
function Home(): JSX.Element{
//let UserId: string = "Ronak";
const initialOrg = {
orgId: "",
orgName: ""
}
const [UserId, setUserId] = useState<string>("userId");
const [createOrgForm, setForm] = useState(<div></div>);
const [allOrg, setAllOrg] = useState<orgType[]>([initialOrg]);
const [orgAdded, changeState] = useState(true);
const {register, handleSubmit} = useForm();
const submitButton = {
margin: "auto",
marginTop: 30,
display: "block"
}
useEffect(() => {
fetch('/api/v1/auth/verifyJWT', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
})
.then(res => res.json())
.then(data => {
console.log(data.serviceResponse.userId);
setUserId(data.serviceResponse.userId);
console.log(UserId);
}
)
}, [] )
useEffect( () => {
console.log(UserId);
fetch('/api/v1/org/all/' + UserId)
.then(res => res.json())
.then(data => {
setAllOrg(data);
console.log("Hi");
console.log(data);
console.log(allOrg);
console.log("bye");
}
)}, [UserId]);
function onSubmit(data: any){
fetch('/api/v1/org/create', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(res => res.json())
.then(data => {
console.log(data);
if(data.message == "Created!"){
console.log("successful");
setForm(()=><div></div>);
changeState(!orgAdded);
}
else{
console.log("failed");
}
})
}
function createOrg(){
console.log(UserId);
setForm(()=>
<form className = "auth_form" onSubmit = {handleSubmit(onSubmit)}>
<br/><br/>
<input className = "auth_input" {...register("userId", {required: true})} name="userId" value={UserId}/>
<br/>
<input className = "auth_input" {...register("orgName", {required: true})} name="orgName" placeholder="Organization Name"/>
<br/>
<button className = "auth_button" style={submitButton} type="submit">Create</button>
</form>
)
}
return(
<div>
<Navbar/><br/>
{
allOrg.map((data: orgType, index: number) => {
/*<Org key={index} userId = {UserId} orgName = {data.orgName} /> */
<h1>{index} {UserId} {data.orgName}</h1>
})
}
<div className = "OrgRow">
<button className = "OrgTeams" onClick={createOrg}>Add Org</button>
{createOrgForm}
</div>
</div>
)
}
export default Home;
Line 103 is where I used allOrg.map() and the declaration of allOrg state is at the start of the function.
Any help would be welcome.
P.S. Incase anyone thinks that the allOrg state might be empty, it is not so. I checked using console.log..
Edit: I am adding the ss of console.log of allOrg, console.log(allOrg).
Even if you checked that allOrg is state is not empty it might be possible that component is rendered multiple times where first time allOrg is at initial state for second rendering it might be empty or null or undefined and at last when API call is completed it fills allOrg.
So you have to handle case for when allOrg is null or something.
let orgList;
if(Array.isArray(allOrg)){
orgList = allOrg.map(
...
);
}
render (
...
{orgList}
...
);

Netflify Serverless Function JSON parse error Sendgrid API

My contact form is not sending the frontend user to my thank you page neither is it sending any information to me via the Sendgrid APi. The issue comes from the fact that I'm not sure how to turn the JSON object into a string and then straight back to an Object..... Or as you can see I have a hard time even framing my question. The goal would be to send the information to my email account using Sendgrid API.
The form is live here:
https://www.metsanotus.fi/yhteydenotto
The code is based 99% off these two tutorials:
https://oliverschmidt.dev/blog/adding-a-contact-form-to-a-static-site-with-netlify-functions/
https://dev.to/char_bone/using-netlify-lambda-functions-to-send-emails-from-a-gatsbyjs-site-3pnb
The code for the contact-page:
https://gist.github.com/otsolap/f05cd4e3a1a08794f61a6d5730abc695
import React, { useState } from "react";
import { graphql } from "gatsby"
import { RiSendPlane2Line } from "react-icons/ri";
import Layout from "../components/layout"
import SEO from "../components/seo"
export const pageQuery = graphql`
query ContactQuery($id: String!){
markdownRemark(id: { eq: $id }) {
id
html
excerpt(pruneLength: 140)
frontmatter {
title
}
}
site {
siteMetadata {
title
}
}
}
`
const Contact = ({ data }) => {
const { markdownRemark, site } = data // data.markdownRemark holds your post data
const { frontmatter, html } = markdownRemark
// input type hidden on netlifytä varten, jotta netlify tietää mikä lomake kyseessä.
// contact on meidän lomake, niin kaikki viestit löytyy contact-lomakkeen alta.
// honeypot=bot-field on botteja varten.
// p hidden pitää kohdan piilossa, mutta console.logilla sen löytää. ;-)
const [formState, setFormState] = useState({
name: '',
email: '',
phone: '',
subject: '',
message: '',
})
const handleChange = (e) => {
setFormState({
...formState,
[e.target.name]: e.target.value,
});
}
const handleSendEmail = async (event) => {
event.preventDefault();
try {
const response = await fetch("/.netlify/functions/contact-form-email", {
method: "POST",
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formState),
})
if (!response.ok) {
//EI 200 response
return
}
//all OK
} catch (event) {
//error
}
}
return (
<Layout className="contact-page">
<SEO
title={frontmatter.title}
description={frontmatter.title + " " + site.siteMetadata.title}
/>
<div className="wrapper">
<h1>{frontmatter.title}</h1>
<div className="description" dangerouslySetInnerHTML={{ __html: html }} />
<form className="contact-form"
action="/kiitos"
name="contact"
method="POST"
data-netlify="true"
value="contact"
data-netlify-honeypot="bot-field"
onSubmit={handleSendEmail}
>
<input type="hidden" name="form-name" value="contact" />
<p hidden><input name="bot-field" /></p>
<p>
<label><input
required
placeholder="Nimi *"
type="text"
name="name"
onChange={handleChange}
/>
</label>
</p>
<p>
<label><input required
placeholder="Sähköposti *"
type="email"
name="email"
onChange={handleChange}
/>
</label>
</p>
<p>
<label><input required
placeholder="Puhelin *"
type="number"
name="phone"
onChange={handleChange}
/>
</label>
</p>
<p>
<label><input placeholder="Aihe"
type="text"
name="subject"
onChange={handleChange}
/>
</label>
</p>
<p>
<label><textarea
placeholder="Viesti"
name="message"
onChange={handleChange}
></textarea></label>
</p>
<p className="text-align-center">
<button className="button"
type="submit">
Lähetä<span className="icon -right"><RiSendPlane2Line />
</span>
</button>
</p>
</form>
</div>
</Layout>
)
}
export default Contact
The code for the serverless function:
https://gist.github.com/otsolap/e157b136aee040281f20ba87131014eb
require('dotenv').config();
const sgMail = require('#sendgrid/mail')
const {
SENDGRID_API_KEY,
METSAN_OTUS_NAME,
METSAN_OTUS_ADDRESS }
= process.env
sgMail.setApiKey(SENDGRID_API_KEY)
exports.handler = async (event, context, callback) => {
const payload = JSON.parse(event.body)
const { email, subject, message } = payload
const msg = {
to: METSAN_OTUS_ADDRESS,
name: METSAN_OTUS_NAME,
from: email,
subject: subject ? subject : 'Yhteydenotto lomakkeesta',
text: message,
};
try {
await sgMail.send(msg)
return {
statusCode: 200,
body: "Viesti lähetetty"
}
} catch (e) {
return {
body: e.message,
statusCode: 500,
}
}
};
When I keep the JSON.parse(body.event) this is the error it displays:
https://gist.github.com/otsolap/79830f6cf1e9b247c63c1f3f49c5286b
SyntaxError: Unexpected token u in JSON at position 0
If I change the line 13 of serverless-function.js from JSON.parse(event.body) to (for example) JSON.stringify(event.body) the error becomes this:
TypeError: Cannot destructure property 'email' of 'payload' as it is undefined.
So I guess my question is how should I formulate my serverless function so that the React object from UseState can become readable for my function?

HttpErrorResponse {headers: HttpHeaders, status: 415, statusText: "Unsupported Media Type"

Hi I tried to upload a csv file and convert it to json array and pass to the web api. But when I click the submit button I am getting this error. Anyone who can help to fix this?
Thank you :)
This is my .ts file in angular Here I tried to upload a csv file and convert it to json array.
changeListener(event: any) {
if (event.target.files.length > 0) {
const file = event.target.files[0];
this.myForm.patchValue({
fileSource: file
});
//File reader method
let reader: FileReader = new FileReader();
reader.readAsText(file);
reader.onload = (e) => {
let csv: any = reader.result;
let allTextLines = [];
allTextLines = csv.split(/\r|\n|\r/);
console.log('CSV: ', csv?.toString());
}
//JSON.stringify(file);
}
}
submit() {
const formData = new FormData();
formData.append('file', this.myForm.get('fileSource')?.value);
this.http.post('http://localhost:64233/api/employee', formData)
.subscribe(res => {
console.log(res);
alert('Upload Sussessful');
})
}
This is my .html file in angular
<form [formGroup]="myForm" (ngSubmit)="submit()">
<h1 style="text-align: center">File Upload</h1>
<br /><br />
<div class="form-group">
<label for="file">File</label>
<input class="form-control" formControlName="file" id="file" type="file" class="upload"
(change)="changeListener($event)" />
</div>
<button id="btnSave" class="btn btn-primary" type="submit">Submit</button>
</form>
This is the error I get when I click on submit button
I checked my web api in postman and it is working fine for json array. Really appreciate if you can help. Thank you
Ok I am talking to myself. But this is to help others.
Here in my .ts file, I have uploaded the file and read the data in the csv file as a string. But I have not converted it to json array correctly and push it to go to the web api.
So below is the working code. This may not be the perfect one but it works fine for me.
This is my .ts file in angular
export class FileUploadComponent implements OnInit {
myForm = new FormGroup({
file: new FormControl('', [Validators.required])
});
ngOnInit(): void {
this.resetForm();
}
constructor(private http: HttpClient, public fileUploadService: FileUploadService,
private toastr: ToastrService, private router: Router) { }
obj: any;
unique: any;
removeHeader: any;
// Maximum file size allowed to be uploaded = 1MB
maxSize: number = 1048576;
//upload file
fileUpload(event: any) {
if (event.target.files && event.target.files.length > 0) {
// Don't allow file sizes over 1MB
if (event.target.files[0].size < this.maxSize) {
const file = event.target.files[0];
console.log(file);
//File reader method
let reader: FileReader = new FileReader();
reader.readAsText(file);
reader.onload = (e) => {
let csv: any = reader.result;
let res: any[] = csv.split("\n");
//remove first element of the array
res.shift();
let jsonArray: any = [];
res.forEach(item => {
let singlePerson = item.split(",");
let singleObject = { employeeid: singlePerson[0], firstname: singlePerson[1], lastname: singlePerson[2], address: singlePerson[3] }
jsonArray.push(singleObject);
})
this.obj = jsonArray;
//check duplicates in csv file, remove, and return unique records
let unique = this.obj
.map((e: { [x: string]: any; }) => e['employeeid'])
.map((e: any, i: any, final: string | any[]) => final.indexOf(e) === i && i)
.filter((obje: string | number) => this.obj[obje])
.map((e: string | number) => this.obj[e]);
this.obj = unique;
}
}
else {
// Display error message
this.toastr.error("File is too large to upload");
}
}
}
resetForm() {
this.myForm.reset();
}
submit() {
this.fileUploadService.postFileUpload(this.obj);
this.resetForm();
}
}
This is my html file
<br /><br />
<form [formGroup]="myForm">
<h1 style="text-align: center">File Upload</h1>
<br /><br />
<div class="form-group">
<label for="file" style="font-size: 25px">File</label>
<input
class="form-control"
formControlName="file"
type="file"
accept=".csv"
class="upload"
(change)="fileUpload($event)"
/>
</div>
<div class="form-group">
<label> Please Upload a CSV or Text file of size less than 1MB </label>
</div>
<button class="btn btn-primary" type="submit" (click)="submit()">
Submit
</button>
</form>
This is my service class
export class FileUploadService {
messages: string[] = [];
constructor(private http: HttpClient, private toastr: ToastrService) { }
readonly baseURL = 'http://localhost:64233/api/employee';
myForm = new FormGroup({
file: new FormControl('', [Validators.required])
});
formData: FileUpload = new FileUpload();
//method for post request
postFileUpload(body: any) {
const requestOptions = { headers: new HttpHeaders({ 'content-type': "application/json" }) };
return this.http.post(this.baseURL, body, requestOptions)
.subscribe(
observer => {
this.toastr.success("File Uploaded Succesfully");
this.resetForm();
},
err => {
if (err.status == 500)
this.toastr.error("Empty File");
else
this.toastr.error("Please upload a file");
//console.log(err);
/* (error) => {
console.log(error); */
//throw new Error(error);
});
}
resetForm() {
this.myForm.reset();
this.formData = new FileUpload();
}
}
Here to display alerts I have used a toaster

React: Passing a Prop in w/ event for setState

I'm working with a nested state object that I have been updating with onChange functions, like so:
const [someState, setSomeState] = useState({
customer: [
{
name: "Bob",
address: "1234 Main Street",
email: "bob#mail.com",
phone: [
{
mobile: "555-5555",
home: "555-5555"
}
]
}
]
});
const updateSomeStatePhone = e => {
e.persist();
setSomeState(prevState => {
prevState.customer[0].phone[0].mobile = e.target.value;
return {
...prevState
};
});
};
<p>Update Mobile Number<p>
<select
value={someState.customer[0].phone[0].mobile}
onChange={updateSomeStatePhone}
>
<option value="123-4567">"123-4567"</option>
</select>
This gets the trick done. Currently however, if I want to update multiple state properties via a large form with dropdowns/input fields etc, I have to hard code 6 different onChange handlers for those fields.
Instead, I would prefer to have only one onChange handler, and pass in the state from the form field for the state property that I am changing, but I can't figure out the syntax:
const updateSomeState = (e, prop) => {
e.persist();
setSomeState(prevState => {
prevState.prop = e.target.value;
return {
...prevState
};
});
};
<p>Update Mobile Number<p>
<select
value={someState.customer[0].phone[0].mobile}
onChange={updateSomeState(e, prop)}
>
<option value="123-4567">"123-4567"</option>
</select>
I've tried using different types of syntax to chain the passed in 'prop' value to prevState:
prevState.prop = e.target.value;
prevState.(prop) = e.target.value;
${prevState} + '.' + ${prop} = e.target.value; // Dumb, I know
But the function never recognizes the "prop" that I pass in from the function. I'm sure there must be a simple way to do this. Any help would be greatly appreciated.
Does it have to be a single useState hook? I would recommend using useReducer or simplifying it a bit with multiple useState hooks.
Multiple useState hooks
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
const [name, setName] = React.useState("");
const [address, setAddress] = React.useState("");
const [email, setEmail] = React.useState("");
const [mobile, setMobile] = React.useState("");
const [home, setHome] = React.useState("");
const getResult = () => ({
customer: [
{
name,
address,
email,
phone: [
{
mobile,
home
}
]
}
]
});
// Do whatever you need to do with this
console.log(getResult());
return (
<>
<input
value={name}
placeholder="name"
onChange={e => setName(e.target.value)}
/>
<br />
<input
value={address}
placeholder="address"
onChange={e => setAddress(e.target.value)}
/>
<br />
<input
value={email}
placeholder="email"
onChange={e => setEmail(e.target.value)}
/>
<br />
<input
value={mobile}
placeholder="mobile"
onChange={e => setMobile(e.target.value)}
/>
<br />
<input
value={home}
placeholder="home"
onChange={e => setHome(e.target.value)}
/>
</>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Single useReducer (with simplified state)
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
const reducer = (state, action) => {
const { type, value } = action;
switch (type) {
case "SET_NAME":
return { ...state, name: value };
case "SET_ADDRESS":
return { ...state, address: value };
case "SET_EMAIL":
return { ...state, email: value };
case "SET_MOBILE":
return { ...state, phone: [{ ...state.phone[0], mobile: value }] };
case "SET_HOME":
return { ...state, phone: [{ ...state.phone[0], home: value }] };
default:
throw Error(`Unexpected action: ${action.type}`);
}
};
const initialState = {
name: "",
address: "",
email: "",
phone: [
{
mobile: "",
home: ""
}
]
};
function App() {
const [state, dispatch] = React.useReducer(reducer, initialState);
// Do what you need with state
console.log(state);
return (
<>
<input
value={state.name}
placeholder="name"
onChange={({ target: { value } }) =>
dispatch({ type: "SET_NAME", value })
}
/>
<br />
<input
value={state.address}
placeholder="address"
onChange={({ target: { value } }) =>
dispatch({ type: "SET_ADDRESS", value })
}
/>
<br />
<input
value={state.email}
placeholder="email"
onChange={({ target: { value } }) =>
dispatch({ type: "SET_EMAIL", value })
}
/>
<br />
<input
value={state.phone.mobile}
placeholder="mobile"
onChange={({ target: { value } }) =>
dispatch({ type: "SET_MOBILE", value })
}
/>
<br />
<input
value={state.phone.home}
placeholder="home"
onChange={({ target: { value } }) =>
dispatch({ type: "SET_HOME", value })
}
/>
</>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
useReducer is a better choice for doing this. Examples all over the internet.
Why you shouldn't use useState to pass an object is because it doesn't act like setState. The underlying object reference is the same. Therefore, react will never trigger a state change. In case you want to use the same useState for objects. You may have to implement your own version to extend that (example below ) or you can directly use useReducer hook to achieve the same.
Here's an example with useState for you to notice the state update on every change.
const [form, setValues] = useState({
username: "",
password: ""
});
const updateField = e => {
setValues({
...form,
[e.target.name]: e.target.value
});
};
Notice the ...form in there. You can do it this in every update you want or you can use your own utility or useReducer as I mentioned.
Now coming to your code, there are other concerns.
You are using your phone as an array which can be an object. Or better yet separate properties will do as well. No harm.
If you have customers as an array, you have to loop through the records. Not just update the index by hardcoding. If there's only one customer better not keep the array but just an object. Assuming it is an array of customers, and you are looping through it, here's how to update mobile.
const updatedCustomers = state.customers.map(item => {
const { phone } = item;
return { ...item, phone: { mobile: e.target.value }};
// returns newCustomer object with updated mobile property
});
// Then go ahead and call `setSomeState ` from `useState`
setSomeState(...someState, { customer: updatedCustomers });// newState in your case is
Instead, I would prefer to have only one onChange handler, and pass in
the state from the form field for the state property that I am
changing, but I can't figure out the syntax
If you haven't figured that out from the first example. Here's how in short steps.
Give your HTML element a name attribute.
Then instead use the [e.target.name]
return { ...item, phone: { [e.target.name]: e.target.value }};
Use lodash's _.set helper.
const updateSomeState = (e, prop) => {
e.persist();
setSomeState(prevState => {
let customers = [...prevState.customers] // make a copy of array
let customer = {...customers[0]} // make a copy of customer object
_.set(customer, prop, e.target.value)
customers[0] = customer;
return {
...prevState, customers
};
});
};
BTW, in your existing updateSomeStatePhone you are modifying prevState object which is supposed to be immutable.

Angular 6 error doesn't fire

I am learning angular and for my example using Firebase createUserWithEmailAndPassword for sign-up. This returns a promise which i have changed to observable using from.
In firebase minimum password length is 6 characters. When i provide 5 characters, in the console i see the error message but in my sign-up event, success message shows rather than error. What am i missing here?
AuthService
import * as firebase from 'firebase';
import { throwError, from } from 'rxjs';
export class AuthService{
//user sign up, its a promise so listen for errors and log
signUpUser(email: string, password: string){
//return an observable using from
return from(
firebase.auth().createUserWithEmailAndPassword(email, password)
.then(
(authData) => {
//good
console.log("User created successfully with payload-", authData);
return authData;
}
)
.catch(
(error) => {
//error
console.log(error);
return throwError(error);;
}
)
);
}
}
Sign-up component
onSignup(form: NgForm){
const email = form.value.email;
const password = form.value.password;
this.authService.signUpUser(email, password).subscribe(
(authData) => {
alert("Signup successful");
this.router.navigate(['/sign-in']);
},
(error) => {
alert(error.message);
}
);
}
Also i am using then in the authService method. How can i do .pipe(map(return authData.json()))?
Update 1:
Following helped and i am getting my error, on successful registration i am getting redirected to the sign-in view.
Convert promise to observable
AuthService
import { from } from 'rxjs';
signUpUserNew(email: string, password: string){
var subscription = from(firebase.auth().createUserWithEmailAndPassword(email, password));
return subscription;
}
Sign-up Component
//property to hold result of sign-up error
error = '';
onSignup(form: NgForm){
const email = form.value.email;
const password = form.value.password;
//this.authService.signUpUser(email, password);
this.authService.signUpUserNew(email, password)
.subscribe(
(firebaseUser) => {
console.log(firebaseUser);
this.router.navigate(['/sign-in']);
},
(error) => {
this.error = error.message;
}
);
}
View
<h2>Register</h2>
<div class="row">
<div class="col-xs-12 col-sm-10 col-md-8 col-sm-offset-1 col-md-offset-2">
<form (ngSubmit)="onSignup(f)" #f="ngForm">
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" name="email" ngModel class="form-control" #email="ngModel" required email>
<span class="help-block" *ngIf="!email.valid && email.touched">Please enter a valid email!</span>
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" name="password" ngModel class="form-control" #password="ngModel" required minlength="6">
<span class="help-block" *ngIf="!password.valid && password.touched && !password.errors?.minlength">Please enter a valid password!</span>
<span class="help-block" *ngIf="!password.valid && password.touched && password.errors?.minlength">Password must be at least 6 characters long</span>
</div>
<p class="error" *ngIf="error">{{ error }}</p>
<button class="btn btn-primary" type="submit" [disabled]="!f.valid">Sign Up</button>
</form>
</div>
</div>
Result
Pending
Now i still need help implementing pipe and map operators.
I am getting the following error on .json:
[ts] Property 'json' does not exists on type 'UserCredential'
onSignup(form: NgForm){
const email = form.value.email;
const password = form.value.password;
//this.authService.signUpUser(email, password);
this.authService.signUpUserNew(email, password)
.pipe(
map(
(firebaseUser) => {
return firebaseUser.json();
}
)
)
.subscribe(
(firebaseUser) => {
console.log(firebaseUser);
this.router.navigate(['/sign-in']);
},
(error) => {
this.error = error.message;
}
);
}
Firstly, I guess you should call fromPromise instead of from, so try the following:
import 'rxjs/add/observable/fromPromise';
import { Observable } from "rxjs/Observable";
signUpUser(email: string, password: string){
//return an observable using fromPromise
const obs$ = fromPromise(
firebase.auth().createUserWithEmailAndPassword(email, password)
);
// you can call .pipe() here, and it will return an observable
return obs$.pipe(
map(x => console.log('PUT YOUR MAP FUNCTION HERE.')),
filter(x => console.log('Call filter() if you want'))
);
}
And you can subscribe to this observable
const subscription = this.authService.signUpUser(email, password).subscribe(
(firebaseUser) => {
console.log('firebase user: ', firebaseUser);
alert("Signup successful");
this.router.navigate(['/sign-in']);
},
(error) => {
alert(error.message);
}
);