I am using Cloudinary with NextJS. When I am trying to specify the folder name in sign.js, which will create a signature for my request, I am getting an "Unauthorized" error as a response. But without a folder name, it works fine.
sign.js - This is the file responsible for creation of signature.
const cloudinary = require("cloudinary").v2;
const sign = async (req, res) => {
const timestamp = Math.round((new Date()).getTime() / 1000);
const signature = cloudinary.utils.api_sign_request({
timestamp: timestamp,
folder: 'product'
}, process.env.NEXT_PUBLIC_CLOUDINARY_SECRET);
res.statusCode = 200;
res.json({ signature, timestamp });
};
export default sign;
index.js - This is the main file.
import Head from 'next/head'
import Image from 'next/image'
import styles from '../styles/Home.module.css'
import { useState } from 'react';
export default function Home() {
const [selectedFile, setSelectedFile] = useState();
const [isFilePicked, setIsFilePicked] = useState(false);
const [file, setFile] = useState();
const changeHandler = (event) => {
console.log(event.target.files[0]);
setSelectedFile(event.target.files[0]);
setFile(URL.createObjectURL(event.target.files[0]))
setIsFilePicked(true);
};
const handleSubmission = async () => {
const { signature, timestamp } = await getSignature();
const formData = new FormData();
const url = `https://api.cloudinary.com/v1_1/${process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME}/upload`
formData.append('file', selectedFile)
formData.append("signature", signature);
formData.append("timestamp", timestamp);
formData.append("api_key", process.env.NEXT_PUBLIC_CLOUDINARY_KEY);
fetch(url, {
method: 'POST',
body: formData
}).then((response) => response.json())
.then((result) => {
console.log(result);
})
.catch((error) => {
console.error('Error:', error);
});
}
return (
<div className={styles.container}>
<Head>
<title>Tailwind-Next</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<div className={styles.main}>
<div className="flex items-center justify-center w-[50%] mb-5">
{selectedFile == null ?
<label htmlFor="dropzone-file" className={`flex flex-col items-center justify-center w-full h-64 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer bg-gray-50 dark:hover:bg-bray-800 hover:bg-gray-100 ${styles.dropzone}`}>
<div className="flex flex-col items-center justify-center pt-5 pb-6">
<svg aria-hidden="true" className="w-10 h-10 mb-3 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"></path></svg>
<p className="mb-2 text-sm text-gray-500 dark:text-gray-400"><span className="font-semibold">Click to upload</span> or drag and drop</p>
<p className="text-xs text-gray-500 dark:text-gray-400">SVG, PNG, JPG or GIF (MAX. 800x400px)</p>
</div>
<input id="dropzone-file" type="file" className="hidden" accept="image/*" onChange={changeHandler} />
</label> :
<div className='flex flex-col items-center justify-center w-full h-64 relative'>
<div className={styles.image_wrapper}>
<img src={file} className={styles.img_preview} alt="" />
</div>
</div>}
</div>
<button className="w-[50%] bg-blue-600 hover:bg-blue-500 text-white font-bold py-2 px-4 border-b-4 border-blue-700 hover:border-blue-600 rounded" onClick={handleSubmission}>
Upload
</button>
</div>
</div>
)
}
async function getSignature() {
const response = await fetch("/api/sign");
const data = await response.json();
const { signature, timestamp } = data;
return { signature, timestamp };
}
I tried creating a signature without a folder name, and it worked fine. However, when I attempted to create a signature using the folder name, it returned unauthorized.
After 13 bad requests and 31 unauthorized requests, I was finally able to resolve the issue.
sign.js - This is the file responsible for creation of signature.
const cloudinary = require("cloudinary").v2;
const sign = async (req, res) => {
const timestamp = Math.round((new Date()).getTime() / 1000);
const signature = cloudinary.utils.api_sign_request({
timestamp: timestamp,
folder: 'product'
}, process.env.NEXT_PUBLIC_CLOUDINARY_SECRET);
res.statusCode = 200;
res.json({ signature, timestamp });
};
export default sign;
index.js - This is the main file.
import Head from 'next/head'
import Image from 'next/image'
import styles from '../styles/Home.module.css'
import { useState } from 'react';
export default function Home() {
const [selectedFile, setSelectedFile] = useState();
const [isFilePicked, setIsFilePicked] = useState(false);
const [file, setFile] = useState();
const changeHandler = (event) => {
console.log(event.target.files[0]);
setSelectedFile(event.target.files[0]);
setFile(URL.createObjectURL(event.target.files[0]))
setIsFilePicked(true);
};
const handleSubmission = async () => {
const { signature, timestamp } = await getSignature();
const formData = new FormData();
const url = `https://api.cloudinary.com/v1_1/${process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME}/upload`
formData.append('file', selectedFile)
formData.append("signature", signature);
formData.append("timestamp", timestamp);
formData.append("api_key", process.env.NEXT_PUBLIC_CLOUDINARY_KEY);
formData.append("folder", "product")
fetch(url, {
method: 'POST',
body: formData
}).then((response) => response.json())
.then((result) => {
console.log(result);
})
.catch((error) => {
console.error('Error:', error);
});
}
return (
<div className={styles.container}>
<Head>
<title>Tailwind-Next</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<div className={styles.main}>
<div className="flex items-center justify-center w-[50%] mb-5">
{selectedFile == null ?
<label htmlFor="dropzone-file" className={`flex flex-col items-center justify-center w-full h-64 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer bg-gray-50 dark:hover:bg-bray-800 hover:bg-gray-100 ${styles.dropzone}`}>
<div className="flex flex-col items-center justify-center pt-5 pb-6">
<svg aria-hidden="true" className="w-10 h-10 mb-3 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"></path></svg>
<p className="mb-2 text-sm text-gray-500 dark:text-gray-400"><span className="font-semibold">Click to upload</span> or drag and drop</p>
<p className="text-xs text-gray-500 dark:text-gray-400">SVG, PNG, JPG or GIF (MAX. 800x400px)</p>
</div>
<input id="dropzone-file" type="file" className="hidden" accept="image/*" onChange={changeHandler} />
</label> :
<div className='flex flex-col items-center justify-center w-full h-64 relative'>
<div className={styles.image_wrapper}>
<img src={file} className={styles.img_preview} alt="" />
</div>
</div>}
</div>
<button className="w-[50%] bg-blue-600 hover:bg-blue-500 text-white font-bold py-2 px-4 border-b-4 border-blue-700 hover:border-blue-600 rounded" onClick={handleSubmission}>
Upload
</button>
</div>
</div>
)
}
async function getSignature() {
const response = await fetch("/api/sign");
const data = await response.json();
const { signature, timestamp } = data;
return { signature, timestamp };
}
Related
I Developer this Template and put its code below. When I type a phrase, Google offers me an address, and when I click on it, the map zooms automatically and displays the marker, as in the picture below:
In the image below, the fields (longitude and latitude) are automatically set only when the map is clicked, but I need the values of the fields (longitude) when I search for the address at the same time (as I explained above) and latitude) are updated and the user does not need to necessarily click on the map.
I just started working with vuejs. Please guide me by editing my code. Thanks
<template>
<div no-body class="mb-1 w-100">
<div class="body-bg shadow-none" id="test2">
<div
:class="{
'is-invalid':
$parent.form.errors.has('auto_complete_map') ||
$parent.form.errors.has('location'),
}"
class="d-flex flex-row justify-content-center border border-1 rounded border-dark"
>
<h4 block v-b-toggle.accordion-2 variant="info">Location</h4>
</div>
</div>
<b-collapse
class="navbar-fixed-top"
id="accordion-2"
accordion="my-accordion"
>
<div class="d-flex flex-row gap-2 justify-content-between mt-2">
<div class="col-sm-6">
<label for="latitude" class="form-label"
>{{ $t("latitude")
}}<span class="small text-danger m-1">*</span></label
>
<input
id="latitude"
:value="latitude"
class="form-control bg-transparent"
name="latitude"
placeholder=""
spellcheck="false"
data-ms-editor="true"
:disabled="true"
/>
<has-error :form="form" field="latitude" />
</div>
<div class="col-sm-6">
<label for="longitude" class="form-label">{{
$t("longitude")
}}</label>
<input
id="longitude"
class="form-control bg-transparent"
:value="longitude"
name="longitude"
placeholder=""
spellcheck="false"
data-ms-editor="true"
:disabled="true"
/>
<has-error :form="form" field="longitude" />
</div>
</div>
<label class="form-label mt-2"
>{{ $t("location") }}<span class="small text-danger">*</span></label
>
<GmapAutocomplete
id="auto_complete_map"
ref="location"
v-validate="'required|min:5'"
class="form-control"
name="location"
:value="location"
:placeholder="$t('enter a location')"
aria-required="true"
:disabled="$parent.canEditProperty()"
#place_changed="setPlace"
#keydown.enter.prevent
/>
<has-error :form="form" field="auto_complete_map" />
<GmapMap
ref="map"
class="my-3"
:center="center"
:zoom="zoom"
style="width: 100%; height: 300px"
#click="clickMap"
>
<GmapMarker
v-for="(m, index) in markers"
:key="index"
:position="m.position"
#click="center = m.position"
/>
</GmapMap>
<!-- <div class="d-flex flex-row justify-content-end m-2">
<b-button class="rounded-lg px-4 text-lg" block v-b-toggle.accordion-3 variant="info">next</b-button>
</div> -->
<div class="d-flex flex-row justify-content-end gap-3 m-2 mt-3">
<b-button
class="rounded-lg px-4 col-sm-2 text-white text-lg border border-dark"
block
v-b-toggle.accordion-1
variant="dark"
>previous</b-button
>
<b-button
class="rounded-lg px-6 col-sm-2 text-lg border border-dark"
block
v-b-toggle.accordion-3
variant="light"
>Next Step</b-button
>
</div>
</b-collapse>
</div>
</template>
<script>
import Autocomplete from "vue2-google-maps/dist/components/autocomplete";
export default {
name: "GoogleMap",
props: {
location: { type: String, default: "" },
latitude: { type: String, default: "" },
longitude: { type: String, default: "" },
},
data() {
return {
center: {
lat: 45.508,
lng: -73.587,
},
currentPlace: null,
markers: [],
places: [],
zoom: 12,
};
},
mounted() {
this.geolocate();
},
methods: {
setPlace(place) {
this.markers.splice(0, this.markers.length);
this.places.splice(0, this.places.length);
this.currentPlace = place;
this.updateLocation(place.formatted_address);
this.addMarker();
},
addMarker() {
if (this.currentPlace) {
const marker = {
lat: this.currentPlace.geometry.location.lat(),
lng: this.currentPlace.geometry.location.lng(),
};
this.markers.push({ position: marker });
this.places.push(this.currentPlace);
this.center = marker;
this.zoom = 17;
this.currentPlace = null;
}
},
clickMap(location) {
const marker = location.latLng;
// this.markers.clear()
this.markers.splice(0, this.markers.length);
this.places.splice(0, this.places.length);
this.markers.push({ position: marker });
// this.places.push(this.currentPlace)
// this.center = marker
// this.zoom = 17
this.currentPlace = null;
const geocoder = new google.maps.Geocoder();
geocoder
.geocode({ location: location.latLng })
.then((response) => {
if (response.results[0]) {
this.updateLocation(response.results[0].formatted_address);
this.getLoc(location.latLng);
} else {
window.alert("No results found");
}
})
.catch((e) => window.alert("Geocoder failed due to: " + e));
},
geolocate: function () {
const oldLocation = this.location;
if (oldLocation.length > 0) {
const _this = this;
const geocoder = new google.maps.Geocoder();
geocoder.geocode({ address: oldLocation }, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
// const lat = results[0].geometry.location.lat()
// const lng = results[0].geometry.location.lng()
// const placeName = results[0].address_components[0].long_name
if (results.length > 0) {
_this.setPlace(results[0]);
}
}
});
} else {
navigator.geolocation.getCurrentPosition((position) => {
this.center = {
lat: position.coords.latitude,
lng: position.coords.longitude,
};
});
}
},
updateLocation: function (newLocation) {
this.location = newLocation;
this.$emit("eventname", newLocation);
},
getLoc: function (location) {
// this.location = this.markers
this.longitude = location.lng();
this.latitude = location.lat();
this.$emit("getlog", location);
},
},
};
</script>
the passed in variable place in setPlace(place) has properties .geometry.location.lag() and .geometry.location.lng(). These are your latitude and longitude values for the entered address. Use these to set the latitude and longitude variables used by your <input> elements
setPlace(place) {
this.markers.splice(0, this.markers.length);
this.places.splice(0, this.places.length);
this.currentPlace = place;
this.updateLocation(place.formatted_address);
this.addMarker();
this.latitude = place.geometry.location.lat();
this.longitude = place.geometry.location.lng();
},
I do also want to point out that you've created latitude and longitude as props, which aren't meant to be mutated. If you don't have a reason to have them as props put them as data variables instead
I am using supabase auth helper package in my project. When the user enters the login details it should be redirected to the order page. I am using supabase auth condition in the order page. But when the user enters the login details it is not redirected to the order page.
code for the authentication in the order-page is given below :-
export const getServerSideProps = withPageAuth({ redirectTo: '/admin/login',
async getServerSideProps(ctx) {
// Access the user object
const { user, accessToken } = await getUser(ctx);
return { props: { email: user?.email } };
}
});
login page code is given below:-
async function handleSubmit(e) {
e.preventDefault();
const { user, error } = await supabase.auth.signIn({
email,
password
})
router.push('/orders')
}
return (
<Layout>
<div className="flex items-center justify-center h-screen">
<form onSubmit={handleSubmit}>
<p className="text-center text-[27px] text-white">Admin Login</p>
<div className="mt-4 text-white">
<p className="text-[14px]">Email</p>
<input type="text" className="bg-[#4A4949] rounded-md py-2 px-1 w-[300px]"
placeholder="Email Address"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</div>
<div className="mt-4 text-white">
<p className="text-[14px]">Password</p>
<input type="password" className="bg-[#4A4949] rounded-md py-2 px-1 w-[300px]"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</div>
<button className="mt-8 bg-[#4A4949] text-white rounded w-[300px] py-2">Login</button>
</form>
</div>
</Layout >
)
}
You will need to wait for the user object to be defined before calling router.push:
import { useUser } from '#supabase/supabase-auth-helpers/react';
const { user } = useUser();
useEffect(() => {
if (user) {
router.push('/orders');
}
}, [user]);
async function handleSubmit(e) {
e.preventDefault();
const { user, error } = await supabase.auth.signIn({
email,
password
})
}
I'm using next-iron-session in my next.js project with discord oauth2.
My Session Code:
import {
GetServerSidePropsContext,
NextApiRequest,
NextApiResponse,
} from "next";
import { withIronSession, Session } from "next-iron-session";
export type NextIronRequest = NextApiRequest & { session: Session };
export type NextIronHandler = (
req: NextIronRequest,
res: NextApiResponse
) => void | Promise<void>;
export type NextRoute = (
ctx: GetServerSidePropsContext & { req: { session: Session } },
redirect: string
) => any;
export function withSession(handler: NextIronHandler | NextRoute) {
return withIronSession(handler, {
password: process.env.COOKIE_SECRET as string,
cookieName: "session",
ttl: 15 * 24 * 3600,
cookieOptions: {
secure: process.env.NODE_ENV === "production",
sameSite: "strict",
httpOnly: true,
},
});
}
Auth Handler Code:
import next, { GetServerSidePropsContext } from 'next'
import { withSession } from './session'
export const withAuth = (gssp: any) => {
return async (context: GetServerSidePropsContext) => {
const { req } = context
const user = req.session.user
if (!user) {
return {
redirect: {
destination: '/api/auth/login',
statusCode: 302,
},
}
}
return await gssp(context)
}
}
export const withAuthSsr = (handler: any) => withSession(withAuth(handler))
Dashboard Code:
import { GetServerSideProps, NextApiRequest } from "next";
import Head from "next/head";
import Image from "next/image";
import Link from "next/link";
import { NextIronRequest } from "../util/session";
import { User, UserData } from "src/types";
import { developerRoute } from "src/util/redirects";
import { withSession } from "src/util/session";
import Router from "next/router";
import { redirect } from "next/dist/server/api-utils";
import { withAuthSsr } from '../util/AuthHandler'
interface Props {
user?: User;
}
export default function Dashboard(req: NextIronRequest) {
function Icon(icon: string) {
console.log(icon)
}
return (
<>
<Head>
<title>{req.user.user.username} | Dashboard</title>
<link rel="shortcut icon" href={req.user.avatar} />
</Head>
<body>
{
req.user.guilds.map((guild: any) => {
return (
<>
<div className="p-10 items-center justify-center flex flex-center grid grid-cols-2 grid-flow-row sm:grid-cols-1 md:grid-cols-3 lg:grid-cols-3 xl:grid-cols-3 gap-5">
<div className="rounded overflow-hidden shadow-lg">
<img className="w-full" src={Icon(guild.icon)} alt="Guild Icon" />
<div className="px-6 py-4">
<div className="font-bold text-center text-xl mb-2">{guild.name}</div>
</div>
<div className="px-6 pt-4 pb-2 items-center content-center justify-center">
<a href="#_" className="relative inline-flex items-center justify-center p-4 px-6 py-3 overflow-hidden font-medium text-indigo-600 transition duration-300 ease-out border-2 border-purple-500 rounded-full shadow-md group">
<span className="absolute inset-0 flex items-center justify-center w-full h-full text-white duration-300 -translate-x-full bg-purple-500 group-hover:translate-x-0 ease">
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M14 5l7 7m0 0l-7 7m7-7H3"></path></svg>
</span>
<span className="absolute flex items-center justify-center w-full h-full text-purple-500 transition-all duration-300 transform group-hover:translate-x-full ease">Manage Settings</span>
<span className="relative invisible">Manage Settings</span>
</a>
</div>
</div>
</div>
</>
)
})
}
</body>
</>
)
}
export const getServerSideProps: GetServerSideProps = withSession(withAuthSsr(developerRoute))
What's happening now is, if the user is even loggedIn it will redirect for login again, How do i fix that?
What did i do wrong? If the user isn't logged he/she gets redirected.
Next.js Version: 12.0.7
Next-Iron-Session Version: 4.2.0
I'm trying to Loop through the API in nuxt using XML2JS and then looping through the data to display it within an html file.
It's working when console logged but it's not looping through within the HTML. There arent any errors its, just blank and not displaying anything in the html.
<template>
<article>
<TheHeader />
<h1>Destinations</h1>
<main class="mt-8 mx-auto max-w-screen-xl px-4 sm:mt-12 sm:px-6 md:mt-20 xl:mt-24">
<section v-for="(mountain, index) in mountains" :key="index">
<div v-for="category in mountain.property" :key="category.id">
<div class="lg:grid lg:grid-cols-12 lg:gap-8 mt-12">
<div class="sm:text-center md:max-w-2xl md:mx-auto lg:col-span-6 lg:text-left">
<h2 class="mt-1 text-4xl tracking-tight leading-10 font-extrabold text-gray-900 sm:leading-none sm:text-6xl lg:text-5xl xl:text-6xl">
{{ category.propertyname }}
</h2>
<p class="mt-3 text-base text-gray-500 sm:mt-5 sm:text-xl lg:text-lg xl:text-xl">
{{ category.propertyaddress }}
</p>
</div>
<div class="mt-12 relative sm:max-w-lg sm:mx-auto lg:mt-0 lg:max-w-none lg:mx-0 lg:col-span-6 lg:flex lg:items-center">
<div class="relative mx-auto w-full rounded-lg shadow-lg">
<button type="button" class="relative block w-full rounded-lg overflow-hidden focus:outline-none focus:shadow-outline">
<img class="w-full" :src="category.photos.img.main._url" :alt="category.photos.img.caption">
<div class="absolute inset-0 w-full h-full flex items-center justify-center">
</div>
</button>
</div>
</div>
</div>
</div>
</section>
</main>
</article>
</template>
<script>
const xml2js = require('xml2js');
export default {
data() {
return {
mountains: []
}
},
async fetch() {
this.mountains = await fetch('http://localhost:3000/api/')
.then( res => res.text() )
.then( data=> {
const xml = data;
xml2js.parseString(xml, (err, result) => {
if(err) {
throw err;
}
const output = JSON.stringify(result, null, 4);
console.log(output);
});
})
}
}
</script>
JSON:
{
"scdata": {
"property": [
{
"propertycode": "123456"
Any help would be much appreciated, please let me know if you need me to provide any more information.
Thanks.
I guess you are not returning any data to the mountains. Might be possible that's why you didn't get any display at visual while you got data in const output
So, Please try with the replace the fetch function with below
<template>
<div class="container">
<h1>Test demo of xml parse</h1>
<div v-for="(category, index) in mountains" :key="index">
<div class="property-name"> <h3>Property Code:</h3><span>category.propertycode</span>
</div>
</div>
</div>
</template>
<script>
const xml2js = require('xml2js');
export default {
data() {
return {
mountains: []
}
},
methods: {
xmlToJSON: (str, options) => {
return new Promise((resolve, reject) => {
xml2js.parseString(str, options, (err, jsonObj) => {
if (err) {
return reject(err);
}
resolve(jsonObj);
});
});
}
},
async fetch() {
const xmlData = await fetch('<YOUR-API-URL>')
.then(res => res.text())
.then(data => {
console.log('<<==');
const xml = data;
return data;
});
const jsonData = await this.xmlToJSON(xmlData);
if (jsonData && jsonData.scdata && jsonData.scdata.property) this.mountains = jsonData.scdata.property
}
}
</script>
I am new to web development. I am working in development of an angular application. I have developed an Angular form in front end, Node.js and Sql server in backend. I need some help to connect my angular form to the sql server database. I want to save the data from my angular form to my sql server database.
Versions:
Angular CLI: 8.0.3
Node: 10.16.0
OS: win32 x64
Angular: 8.0.1
I have tried using the restful api to insert data through the server page using post and get.
How can i to connect my angular form to sql databse and insert and update the data when the submit button in my angular form is clicked.
var express = require("express");
var bodyParser = require("body-parser");
var tediousExpress = require("express4-tedious");
var path = require("path")
var app = express();
// Body Parser Middleware
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
//CORS Middleware
app.use(function (req, res, next) {
//Enabling CORS
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS,POST,PUT");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, contentType,Content-Type, Accept, Authorization");
next();
});
app.get('/', function (req, res) {
var sql = require("mssql");
// config for your database
var config = {
server:'******',
database: '*******',
user:'******',
password: '*****',
};
// connect to your database
sql.connect(config, function (err) {
if (err) console.log(err);
// create Request object
var request = new sql.Request();
// query to the database and get the records
request.query('select * from dbo.contact', function (err, recordset) {
if (err)
console.log(err)
else
// send records as a response
res.send(recordset);
});
});
});
app.use("/contact", (req, res) => {
res.sendfile( __dirname + "/src/app/contact/contact.component.html");
});
app.use("/product", (req, res) => {
res.sendfile( __dirname + "/src/app/product/product.component.html");
});
app.use("/homepage", (req, res) => {
res.sendfile( __dirname + "/src/app/home/home.component.html");
});
const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`Listening on port ${port}....`));
<div style="text-align:center">
<h1 class="well">
We are available here for you
</h1>
<div class="loader" *ngIf="dataloading"></div>
<div class = "row">
<div class="column">
<div class ="container">
<div class="col-md-5">
<div class="form-area">
<form role="form" (ngSubmit)="processForm()">
<br style="clear:both">
<h3 style="margin-bottom: 50px; text-align: center;">Contact</h3>
<div class="form-group">
<input required ngModel name="Name" #FirstName="ngModel" (change)="log(Name)" type="text" class="form-control" id="Name" name="name" placeholder="Name" [(ngModel)]="name" required>
<div class="alert alert-danger" *ngIf="Name.touched && !Name.valid" > Name is required.</div>
</div>
<div class="form-group">
<input required type="email" class="form-control" id="email" name="email" placeholder="Email" [(ngModel)]="email" required>
<div class="alert alert-danger" *ngIf="email.touched && !email.valid" > Email is required.</div>
</div>
<div class="form-group">
<input type="phone" class="form-control" id="mobile" name="mobile" placeholder="Mobile Number" [(ngModel)]="mobile" required>
</div>
<div class="form-group">
<input type="text" class="form-control" id="subject" name="subject" placeholder="Subject" [(ngModel)]="subject" required>
</div>
<div class="form-group">
<textarea class="form-control" type="textarea" id="message" name="message" placeholder="Message max(200)" [(ngModel)]="message" maxlength="140" rows="7"></textarea>
</div>
<hr class="mb-4">
<button type="button" id="submit" name="submit" class="btn btn-primary btn-lg btn-block" (click)="save(name, email, mobile, subject, message)">Submit</button>
</form>
</div>
</div>
</div>
````
````contact.component.ts
import { Component, OnInit} from '#angular/core';
import {HttpClient} from '#angular/common/http';
import {HttpErrorResponse} from '#angular/common/http';
import {Router} from '#angular/router';
#Component({
selector: 'app-contact',
templateUrl: './contact.component.html',
styleUrls: ['./contact.component.css']
})
export class ContactComponent {
title = 'Simple Example Contact-us page using Angular 4';
public data:any=[]
constructor(private http: HttpClient){
}
save(name, email, mobile, subject, message): void {
this.data['name']= name;
this.data['email']= email;
this.data['mobile']= mobile;
this.data['subject']= subject;
this.data['message']= message;
console.log(this.data);
//add request to send email or into mysql
this.http.post<any>('/contact1', this.data).subscribe(
res => {
console.log(res);
},
(err: HttpErrorResponse) => {
if (err.error instanceof Error) {
console.log("Client-side error occured.");
} else {
console.log("Server-side error occurred.");
}
});
}
}
````