Así que soy nuevo en JS y tengo la tarea de dominar las solicitudes de Ajax. Debo enviar una entrada de correo electrónico desde el formulario al servidor y mucho más, pero no puedo entender cómo enviar estos datos a un servidor que está en otra carpeta. Perdí todos los nervios con esta tarea y no sé qué hacer. Entonces, tengo una carpeta personal-website-server y otra carpeta src donde está mi proyecto, ambas carpetas están en otra carpeta, la principal. Se parece a esto :
./
dist <carpeta del paquete webpack
node_modules
servidor-sitio-web-personal
/ paquete.json en servidor-sitio-web-personal
origen
y package.json en la carpeta principal Imagen para una mayor comprensión:
Entonces, debería hacer esto:
Al hacer clic en el botón "Suscribirse", implemente la funcionalidad para enviar un correo electrónico de usuario al servidor. Para eso, haga una solicitud POST Ajax usando http://localhost:3000/subscribe endpoint. La llamada al servidor solo debe hacerse cuando el formulario es válido (la función de validación)
La conexión se realiza a través de un proxy al servidor, sé cómo funciona esto y me resulta difícil realizar esta tarea porque no se describe así.
Códigos:
Creé fetch.js en src que verifica si el correo electrónico es válido y lo envía al servidor, como entendí:
import { validateEmail } from './email-validator.js' // const express = require('express'); // const app = express(); // app.listen(3000,() => console.log('listening at 3000')); // app.use(express.static(path.join(__dirname, 'public'))); export const sendSubscribe = (emailInput) => { const isValidEmail = validateEmail(emailInput) if (isValidEmail === true) { // fetch('http://localhost:9000/subscribe', { // method: 'post', // body: emailInput // }).then(function (response) { // return response.text(); // }).then(function (text) { // console.log(text); // }).catch(function (error) { // console.error(error); // }) //createRequest(url); app.post('/subscribe', (request, response) => { console.log(request.body); }); } } // const createRequest = (url) => { // const httpRequest = new XMLHttpRequest(url) // httpRequest.addEventListener('readystatechange', (url) => { // if (httpRequest.readyState === 4) { // console.log(httpRequest.responseText) // } // }); httpRequest.open('GET', url); // httpRequest.send(); // };
Pero esta aplicación de código también se encuentra en una tarea de servidor de sitio web personal, por lo que no sé cómo hacer la conexión desde mi aplicación a ese servidor.
Sección donde está disponible mi formulario:
import { subscribe, unsubscribe, subscribeEmail } from './subscribe.js' import { sendSubscribe } from './fetch.js' const addSection = () => { const sectionFour = createElement('sectionFour', 'section', 'app-section app-section--image-program', 'fourth-section') const sectionParent = getElbyID('sectionParent', 'third-section') const parentSection = sectionParent.parentNode parentSection.insertBefore(sectionFour, sectionParent.nextSibling) const heading2 = createElement('heading2', 'h2', 'program-title') const heading2Text = document.createTextNode('Join Our Program') heading2.append(heading2Text) const parent = getElbyID('parent', 'fourth-section') const heading3 = createElement('heading3', 'h3', 'program-subtitle') const heading3Text = document.createTextNode('Sed do eiusmod tempor incididunt') heading3.appendChild(heading3Text) const linebreak = createElement('linebreak', 'br') heading3.appendChild(linebreak) const textAfterBreak = document.createTextNode('ut labore et dolore magna aliqua') heading3.appendChild(textAfterBreak) const form = createElement('submitFieldWrapper', 'form', 'submitFieldWrapper', 'form') form.method = "POST"; parent.append(heading2, heading3, form) const emailForm = createElement('emailForm', 'div', 'form-wrapper', 'emailForm') const inputForm = createElement('inputForm', 'input', 'form-input', 'submit-info') setAttributes(inputForm, 'type', 'text', 'placeholder', 'Email') //create a variable to store localStorage email value const localEmail = localStorage.getItem("Email") if (localStorage.getItem('Email') !== null) { inputForm.setAttribute('value', localEmail) } else { inputForm.setAttribute('placeholder', 'Email') } emailForm.appendChild(inputForm) document.querySelector('form').addEventListener('submit', function (e) { //create a variable to store localStorage email value const introducedEmail = inputForm.value; e.preventDefault() console.log(introducedEmail) localStorage.setItem('Email', introducedEmail) subscribeEmail(introducedEmail) sendSubscribe(introducedEmail) //fetch data to /subscribe endpoint const data = { localEmail } const options = { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }; fetch('/subscribe', options); }) // const localEmail = localStorage.getItem("Email") // const data = { localEmail } // const options = { // method:'POST' // }; // fetch('/subscribe',options); const submitForm = createElement('submitForm', 'input', 'app-section__button submit-btn', 'subscribeButton') setAttributes(submitForm, 'type', 'submit', 'value', 'Subscribe') form.append(emailForm, submitForm) const isSubscribed = localStorage.getItem('isSubscribed') if (isSubscribed === 'true') { subscribe() } else if (isSubscribed === 'false') { unsubscribe() } } //functions const createElement = (elName, htmlEl, elClass, elID) => { const elementName = document.createElement(htmlEl) elementName.className = elClass elementName.id = elID return elementName } const getElbyID = (elName, searchedId) => { const elementToSearch = document.getElementById(searchedId) return elementToSearch } const setAttributes = (elem, ...elemArguments) => { for (let i = 0; i < elemArguments.length; i += 2) { elem.setAttribute(elemArguments[i], elemArguments[i + 1]) } } export const advancedSection = () => { addSection() const getHeading = document.getElementById('fourth-section') const sectionChildren = getHeading.children sectionChildren[0].innerHTML = 'Join Our Advanced Program' const getButton = document.getElementById('subscribeButton') setAttributes(getButton, 'type', 'submit', 'value', 'Subscribe to Advanced Program') getButton.className = 'app-section__button submit-btnAdvanced' } export default addSection
Se crea dinámicamente y luego se incluye con el paquete web.
Validar la función de correo electrónico:
const VALID_EMAIL_ENDINGS = ['gmail.com', 'outlook.com', 'yandex.ru'] export const validateEmail = (email) => !!VALID_EMAIL_ENDINGS.some(v => email.includes(v)) export { VALID_EMAIL_ENDINGS as validEnding }
package.json de la carpeta principal:
{ "name": "webpack-project", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "build": "webpack", "dev": "webpack-dev-server", "lint": "eslint src" }, "author": "", "license": "ISC", "devDependencies": { "@babel/core": "^7.16.0", "@babel/preset-env": "^7.16.4", "babel-loader": "^8.2.3", "copy-webpack-plugin": "^6.0.0", "css-loader": "^6.5.1", "eslint": "^7.32.0", "eslint-config-standard": "^16.0.3", "eslint-plugin-import": "^2.25.3", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^5.1.1", "html-webpack-plugin": "^5.5.0", "style-loader": "^3.3.1", "uglifyjs-webpack-plugin": "^2.2.0", "webpack": "^5.64.2", "webpack-cli": "^4.9.1", "webpack-dev-server": "^4.5.0" }, "dependencies": { "jquery": "^3.6.0", "request": "^2.79.0", "uglifyjs": "^2.4.11" } }
webpack.config.js:
const CopyPlugin = require("copy-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const path = require("path"); var webpack = require('webpack'); const TerserPlugin = require('terser-webpack-plugin'); module.exports = { mode: 'production', entry: "./src/index.js", output: { filename: "main.js", path: path.resolve(__dirname, "dist") }, optimization: { minimize: true, minimizer: [new TerserPlugin()], }, performance: { hints: false, maxEntrypointSize: 512000, maxAssetSize: 512000 }, devServer: { hot: true, static: [ { directory: path.join(__dirname, "dist") }], port: 9000, proxy: { '/api': 'http://localhost:3000' }, }, module: { rules: [ { test: /\.js$/, exclude: /(node_modules)/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } } }, { test: /\.css$/, use: [ { loader: 'style-loader' }, { loader: 'css-loader' }, ] } ] }, plugins: [ new CopyPlugin({ patterns: [ { from: "src/assets/images", to: "assets/images" }, ], }), new HtmlWebpackPlugin({ template: 'src/index.html' }), new webpack.HotModuleReplacementPlugin(), ], };
En este estado que te envié me salen los siguientes errores:
main.js:2 POST http://localhost:9000/subscribe 404 (Not Found) Uncaught ReferenceError: app is not defined at HTMLFormElement.<anonymous> (main.js:2)
Ahora algo de código del servidor del sitio web personal:
paquete.json:
{ "name": "personal-website-server", "version": "0.0.0", "private": true, "scripts": { "start": "node ./bin/www", "dev": "nodemon ./bin/www", "lint": "npx eslint ./", "lint-fix": "npx eslint ./ --fix" }, "dependencies": { "cookie-parser": "~1.4.4", "debug": "~2.6.9", "express": "~4.16.1", "morgan": "~1.9.1" }, "devDependencies": { "eslint": "^7.1.0", "nodemon": "^2.0.4" } }
aplicación.js:
const express = require('express'); const path = require('path'); const cookieParser = require('cookie-parser'); const logger = require('morgan'); const indexRouter = require('./routes/index'); const communityRouter = require('./routes/community'); const analyticsRouter = require('./routes/analytics'); const app = express(); global.appRoot = path.resolve(__dirname); app.use(logger('dev')); app.use(express.json()); app.use(express.urlencoded({ extended: false })); app.use(cookieParser()); app.use(express.static(path.join(__dirname, 'public'))); app.use('/', indexRouter); app.use('/community', communityRouter); app.use('/analytics', analyticsRouter); module.exports = app;
www archivo de bin:
#!/usr/bin/env node /** * Module dependencies. */ const app = require('../app'); const debug = require('debug')('javascript-frontend-applied-server:server'); const http = require('http'); /** * Get port from environment and store in Express. */ const port = normalizePort(process.env.PORT || '3000'); app.set('port', port); /** * Create HTTP server. */ const server = http.createServer(app); /** * Listen on provided port, on all network interfaces. */ server.listen(port); server.on('error', onError); server.on('listening', onListening); /** * Normalize a port into a number, string, or false. */ function normalizePort(val) { const port = parseInt(val, 10); if (isNaN(port)) { // named pipe return val; } if (port >= 0) { // port number return port; } return false; } /** * Event listener for HTTP server "error" event. */ function onError(error) { if (error.syscall !== 'listen') { throw error; } const bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port; // handle specific listen errors with friendly messages switch (error.code) { case 'EACCES': console.error(bind + ' requires elevated privileges'); process.exit(1); break; case 'EADDRINUSE': console.error(bind + ' is already in use'); process.exit(1); break; default: throw error; } } /** * Event listener for HTTP server "listening" event. */ function onListening() { const addr = server.address(); const bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port; debug('Listening on ' + bind); }
index.js de la carpeta de rutas:
const express = require('express'); const router = express.Router(); const FileStorage = require('../services/FileStorage'); /* POST /subscribe */ router.post('/subscribe', async function (req, res) { try { if (!req.body || !req.body.email) { return res.status(400).json({ error: "Wrong payload" }); } if (req.body.email === 'forbidden@email.com') { return res.status(422).json({ error: "Email is already in use" }); } const data = {email: req.body.email}; await FileStorage.writeFile('user.json', data); await res.json({success: true}) } catch (e) { console.log(e); res.status(500).send('Internal error'); } }); /* GET /unsubscribe */ router.post('/unsubscribe ', async function (req, res) { try { await FileStorage.deleteFile('user.json'); await FileStorage.writeFile('user-analytics.json', []); await FileStorage.writeFile('performance-analytics.json', []); await res.json({success: true}) } catch (e) { console.log(e); res.status(500).send('Internal error'); } }); module.exports = router;
Avíseme si necesita más información, no puedo terminar con esta tarea y esto me vuelve loco, por favor ayuda.
Solucioné el problema, estaba en webpack.config.js. No escuché las apis y ahora está así:
devServer: { hot: true, open: false, static: [ { directory: path.join(__dirname, "dist") }], port: 8080, proxy: [{ context: ['/community', '/subscribe', '/unsubscribe'], target: 'http://localhost:3000', }], },
Y publicar la solicitud de ajax:
const sendHttpRequest = (method, url, data) => { return fetch(url, { method: method, body: JSON.stringify(data), headers: data ? { 'Content-Type': 'application/json' } : {} }).then(response => { if (response.status >= 400) { return response.json().then(errResData => { const error = new Error('Something went wrong!'); error.data = errResData; throw error; }); } return response.json(); }); }; const sendData = (emailInput) => { sendHttpRequest('POST', 'http://localhost:8080/subscribe', { email: emailInput }).then(responseData => { console.log(responseData); }).catch(err => { console.log(err, err.data); }); }
Ahora, cuando envío un correo electrónico, lo envía al servidor y crea un archivo user.json donde se escribe el correo electrónico.