- Регистрация
- 1 Мар 2015
- Сообщения
- 1,481
- Баллы
- 155
Today, we often reach for complex frameworks and toolchains to create interactive forms—but what if we told you that you can build smart, dynamic forms without writing a single line of traditional JavaScript logic?
In this article, we’ll show you how to create a fully functioning form that submits asynchronously using , a lightweight templating engine that simplifies client-server interactions.
Let’s start!
?️ Project Structure
We’ll use a simple folder layout:
? smart-form
├── ? components
│ └── ? Register
│ └── index.html
├── ? src
│ ├── global.css
│ ├── global.js
│ └── index.html
├── app.js
└── routes
└── post.js
Let’s start with our basic styles.
src/global.css
body {
font-family: Arial, sans-serif;
background: #f4f4f4;
padding: 50px;
}
form {
background: white;
padding: 20px;
border-radius: 6px;
box-shadow: 0 4px 10px rgba(0,0,0,0.1);
max-width: 400px;
margin: auto;
}
.form-example {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
}
input[type="text"],
input[type="password"] {
width: 100%;
padding: 8px;
box-sizing: border-box;
}
input[type="submit"] {
background-color: #649606;
color: white;
border-radius: 5px;
padding: 10px 15px;
cursor: pointer;
}
? Creating the Server
We’ll set up a simple Express server with one POST route to handle our form submission.
app.js
const express = require("express");
const path = require("path");
const cors = require("cors");
const app = express();
const PORT = 8000;
app.use(cors());
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use(express.static(path.join(__dirname, "src")));
const postRoutes = require("./routes/post");
app.use("/api", postRoutes);
app.get("/", (_, res) => {
res.sendFile(path.join(__dirname, "src/index.html"));
});
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
routes/post.js
const express = require("express");
const router = express.Router();
router.post("/register", (req, res) => {
const { login, password } = req.body;
if (!login || !password) {
return res.status(400).send("<p style='color: red;'>Missing fields!</p>");
}
console.log("User Registered:", login);
res.send(`<p style='color: green;'>Welcome, ${login}!</p>`);
});
module.exports = router;
? The Smart Form Component
Here’s where the magic happens. This form will submit data using HMPL's request block, without you writing any JavaScript event listeners.
components/Register/index.html
<div>
<form onsubmit="function prevent(e){e.preventDefault();};return prevent(event);" id="form">
<div class="form-example">
<label for="login">Login:</label>
<input type="text" name="login" id="login" required />
<br/>
<label for="password">Password:</label>
<input type="password" name="password" id="password" required />
</div>
<div class="form-example">
<input type="submit" value="Register!" />
</div>
</form>
<p>
{{#request
src="/api/register"
after="submit:#form"
repeat=false
indicators=[
{
trigger: "pending",
content: "<p>Loading...</p>"
}
]
}}
{{/request}}
</p>
</div>
What’s happening here?
No manual fetch, no async/await. Everything is declared.
Loading the Component with HMPL
Now, let’s render this component dynamically on our page using HMPL.
src/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Smart Form</title>
<link rel="stylesheet" href="global.css" />
</head>
<body>
<div id="wrapper"></div>
<script src=""></script>
<script src=""></script>
<script src=""></script>
<script>
import { compile } from "hmpl-js";
const templateFn = compile(`
{{#request src="/components/Register/index.html"}}{{/request}}
`);
const obj = templateFn();
document.getElementById("wrapper").append(obj.response);
</script>
</body>
</html>
Result
Here’s what you get:
? Why Use This Approach?
Building interactive web forms no longer requires JavaScript bloat or massive toolchains. With HMPL, you keep your code clean, semantic, and powerful—perfect for developers who love declarative logic and simplicity.
If you liked the article, consider giving HMPL a star!
Thank you for reading, and happy coding!
In this article, we’ll show you how to create a fully functioning form that submits asynchronously using , a lightweight templating engine that simplifies client-server interactions.
Let’s start!
?️ Project Structure
We’ll use a simple folder layout:
? smart-form
├── ? components
│ └── ? Register
│ └── index.html
├── ? src
│ ├── global.css
│ ├── global.js
│ └── index.html
├── app.js
└── routes
└── post.js
- Server: Pure HTML, CSS, and HMPL templates.
- Client: Node.js + Express to receive form data.
?️ Styling the FormNo frameworks like React, Vue, or even jQuery. Just clean web APIs and declarative logic.
Let’s start with our basic styles.
src/global.css
body {
font-family: Arial, sans-serif;
background: #f4f4f4;
padding: 50px;
}
form {
background: white;
padding: 20px;
border-radius: 6px;
box-shadow: 0 4px 10px rgba(0,0,0,0.1);
max-width: 400px;
margin: auto;
}
.form-example {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
}
input[type="text"],
input[type="password"] {
width: 100%;
padding: 8px;
box-sizing: border-box;
}
input[type="submit"] {
background-color: #649606;
color: white;
border-radius: 5px;
padding: 10px 15px;
cursor: pointer;
}
? Creating the Server
We’ll set up a simple Express server with one POST route to handle our form submission.
app.js
const express = require("express");
const path = require("path");
const cors = require("cors");
const app = express();
const PORT = 8000;
app.use(cors());
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use(express.static(path.join(__dirname, "src")));
const postRoutes = require("./routes/post");
app.use("/api", postRoutes);
app.get("/", (_, res) => {
res.sendFile(path.join(__dirname, "src/index.html"));
});
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
routes/post.js
const express = require("express");
const router = express.Router();
router.post("/register", (req, res) => {
const { login, password } = req.body;
if (!login || !password) {
return res.status(400).send("<p style='color: red;'>Missing fields!</p>");
}
console.log("User Registered:", login);
res.send(`<p style='color: green;'>Welcome, ${login}!</p>`);
});
module.exports = router;
? The Smart Form Component
Here’s where the magic happens. This form will submit data using HMPL's request block, without you writing any JavaScript event listeners.
components/Register/index.html
<div>
<form onsubmit="function prevent(e){e.preventDefault();};return prevent(event);" id="form">
<div class="form-example">
<label for="login">Login:</label>
<input type="text" name="login" id="login" required />
<br/>
<label for="password">Password:</label>
<input type="password" name="password" id="password" required />
</div>
<div class="form-example">
<input type="submit" value="Register!" />
</div>
</form>
<p>
{{#request
src="/api/register"
after="submit:#form"
repeat=false
indicators=[
{
trigger: "pending",
content: "<p>Loading...</p>"
}
]
}}
{{/request}}
</p>
</div>
What’s happening here?
- onsubmit prevents default behavior.
- {{#request}} captures the form submit event.
- after="submit:#form" defines when the request should fire.
- indicators show loading states or feedback.
No manual fetch, no async/await. Everything is declared.
Now, let’s render this component dynamically on our page using HMPL.
src/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Smart Form</title>
<link rel="stylesheet" href="global.css" />
</head>
<body>
<div id="wrapper"></div>
<script src=""></script>
<script src=""></script>
<script src=""></script>
<script>
import { compile } from "hmpl-js";
const templateFn = compile(`
{{#request src="/components/Register/index.html"}}{{/request}}
`);
const obj = templateFn();
document.getElementById("wrapper").append(obj.response);
</script>
</body>
</html>
Optionally, you can break this logic out into a separate global.js file if you prefer modularity.
Here’s what you get:
- A clean, styled form
- Asynchronous submission using just HTML + HMPL
- Validation and feedback—all without custom JS logic
? Why Use This Approach?
- No JavaScript Framework Needed: No React, no Angular.
- Declarative Logic: You describe what should happen, not how.
- Simple and Scalable: Great for landing pages, admin tools, and MVPs.
? Final ThoughtsYou can even expand this pattern to support multi-step forms, loaders, error handling, or auto-saving with repeat intervals.
Building interactive web forms no longer requires JavaScript bloat or massive toolchains. With HMPL, you keep your code clean, semantic, and powerful—perfect for developers who love declarative logic and simplicity.
If you liked the article, consider giving HMPL a star!
Thank you for reading, and happy coding!