POST FORM in node and receive data response back to same webpage - node.js

I have a webpage that takes form details, POSTS the data and should then show the results. I'm using express for my routing.
This all works fine by resending the data with the HTML template after the POST but I think there must be a better way by hiding the "results" HTML section then just showing it once the data is known from the form. I've shown a cutdown version of my pages below.
On first load, the page says "your result is undefined", which I would expect but is ugly.
I could remove the "result" section and create a 2nd HTML page to resend from the POST route with it in which would work but I think there must be a better way.
I want to hide the result section on 1st page load then make it appear on the button submit with the result data. I can get the section hide/unhide but I can't get the data results back to display them. On button submit the form results just appear in the weburl www.mywebsite.com/?data almost like a GET request
I have tried using FormData and npm 'form-data' in a POST but can't get it working following these examples https://javascript.info/formdata and https://www.npmjs.com/package/form-data.
My structure in Node is
Router.js file
return res.send(htmlFormTemplate({}));
});
router.post('/css',
[],
async (req, res) => {
let {data} = req.body;
///
result= do some calculation on {data}
///
return res.send(htmlFormTemplate({result}));
});
The htmlFormTemplate is a js file
module.exports = ({result}) => {
return `
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<form class="box" method ="POST">
<inputname="data" />
<button>Submit</button>
</form>
<script>
///tried form processing here
</script>
<section id="Results">
<ul><li>Your result is ${result}</li></ul>
</section>
</body>
</html>
`;
};
I'm self-taught and new so hope this makes sense and thanks for any help/ideas

You can check if the result variable is null before it gets to the section div:
${ result === null ? '' :
`<section id="Results">
<ul><li>Your result is ${result}</li></ul>
</section>`}
Like this, it wont show the result div if result if null.

There is a very simple to solve this problem,
just use some templating engine for ex EJS, its very easy to use and will help you better,
and your result is undefined because your using a promise and it might have happened that the response might have not come and you loaded the page. Just use await
return await res.send(htmlFormTemplate({result}));

Related

How insert result of a Netlify function into the HTML web page whose form called the function (example: calculator)

Background:
I'm coming from the server-side world of Rails, and trying to figure out the Netlify static html + serverless functions approach to doing a few extremely basic landing page web apps which need a serverless function to insert data into an HTML page.
I'm trying to start with the simplest possible case of an HTML page with a form and a serverless function that returns a result back to the page. (e.g., no static site generators).
I have not found any Netlify tutorials that show how a HTML page can have a form that posts to a function which then returns the result of that function back into the same web page.
The simplest sample app I can think of is a page asks a question, the user POSTs their answer to a serverless function, and the same HTML page is updated with the result of the function... a trivial case being to display "your answer was X" above the form. (It is immaterial to me whether the actual page is rewritten again with the result string included, or the result string is dynamically inserted by somehow poking the string to the div, so long as the result string originates in a serverless function; integrating serverless functions results with HTML pages is what I'm trying to learn.)
In the code below a simple HTML page below displays a form, the form POSTs an answer to a javascript function check_answer.js, and the javascript function erases the current page and displays the string "Your answer was XXXX".
That was simple to do, and lots of tutorials show how to have a function accept a form post then return a result string to the browser (overwriting the prior page).
My question:
How can the serverless function insert the result string back into the original HTML page (at the div id="answer") instead of outputting the result to a blank page?
Current code:
# index.html
<html>
<head>
<title>A test form</title>
</head>
<body>
<div id="answer">
</div>
<p>How much is 1 + 3?</p>
<p>form using POST:</p>
<form method="post" name="calc 2" action="/.netlify/functions/check_answer" id="calcform2" data-netlify="true" >
<p>
<label for="my_answer">Answer:</label>
<input type="text" name="my_answer" id="my_answer">
<label for="my_comment">Comment:</label>
<input type="text" name="my_comment" id="my_comment">
</p>
<p>
<input type="submit">
</p>
</form>
</body>
</html>
# functions/check_answer.js
exports.handler = async event => {
console.log(event.queryStringParameters);
console.log(event);
console.log(event.body);
if (event.httpMethod == 'POST')
{
console.log('is POST');
var params = parseQuery(event.body);
console.log(params);
var answer_string = params['my_answer'];
}
else
{
console.log('is GET');
var answer_string = event.queryStringParameters.my_answer || 'empty'
};
return {
statusCode: 200,
body: `Your answer was ${answer_string}`,
}
}
// handle parsing form query aaaa=bbbbb&cccc=dddd into hash object
// from https://stackoverflow.com/a/13419367/597992
function parseQuery(queryString) {
var query = {};
var pairs = (queryString[0] === '?' ? queryString.substr(1) : queryString).split('&');
for (var i = 0; i < pairs.length; i++) {
var pair = pairs[i].split('=');
query[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1] || '');
}
return query;
}

Data passed to Handlebars not showing

I want to display items from the database which I already log the result to the console. The console displays the result. but handlebars not displaying data.
This is controller
exports.createCategory = function(req, res, next){
knex('product_category')
.select()
.then(function(errors, result){
res.render('administration/category', { result, errors });
});
}
This is my router
router.get('/category', adminControllers.createCategory);
and this is my handlebar
<div class="col-md-4 col-lg-4">
{{#each result}}
<h1>{{category_name}}</h1>
{{/each}}
</div>
Just do debugging 101.
Add debug prints and check that you get correct data from DB and that it even executes.
Add to template some field to show you error too. Now if error is returned from query, nothing is shown.
Also add some static test attribute that you pass to template and print it to make sure that you are using handlebars template correctly.
I later got it.
Its the knex query.
after studying the knex docs and some online resources, I discovered I can also do this
knex('table').then(function(result)){
res.render('/', {result});
}
but I don't why this didn't work
knex('table').select().then(function(result)){
res.render('/', {result});
}

Grails 3.3.9: Call controller action when checkbox is checked

I am fairly new to Grails and frameworks in general, so this is most likely a very basic problem. The only promising looking solutions I was able to find were working with the Tag, which is apparently deprecated in Grails 3. Similar questions do exist, but all from the time when was still a thing.
I am trying to program a way of displaying products that are grouped in subcategories which are then grouped in categories. When my page loads the subcategories and categories are requested from my database and selection options (Select-tag and checkboxes) are rendered in the view.
When one of the checkboxes representing the subcategories is checked i need to run a database query to get the product information and update an HTML-element by rendering a template for every row I get back. I have a controller action that does all that. My only problem is that I need a way to call the controller action whenever one of the checkboxes is checked.
I could maybe work around it by using actionSubmit and a hidden submit button that is clicked by javascript whenever a checkbox is checked, but that doesn’t seem like a proper solution.
I am probably missing some very basic functionality here but I did already thoroughly search and haven’t come across a proper solution by now, probably because I didn't use the right search terms. I would be so happy, if anyone could help me with this. Thanks a lot already!
The following example uses a javascript function activated in response to the checkbox being checked/unchecked, the value of which is passed to an action from which you can do whatever with the value of the checkbox, run your query etc. At present the action renders a template to update the view with the database results.
index.gsp
<!DOCTYPE html>
<html>
<head>
<meta name="layout" content="main" />
<script type="text/javascript">
$(document).ready(function(){
$( '#cb' ).click( function() {
var checked = $(this).is(":checked");
$.ajax( {
url: "/yourController/yourAction?checked=" + checked,
type: "get",
success: function ( data ) {
$( '#resultDiv' ).html( data )
},
error: function(jqXHR, textStatus, errorThrown) {
console.log( 'Error rendering template ' + errorThrown )
}
} );
})
});
</script>
</head>
<body>
<div id="resultDiv"></div>
<g:form>
<g:checkBox name="cb" />
</g:form>
</body>
YourController
class YourController {
def yourAction() {
// you may want to do something with the value of params.checked here?
def dbResults = YourDomain.getStuff()
render ( template: 'theTemp', model: [dbResults: dbResults] )
}
}
_theTemp.gsp
<table>
<caption>Table of stuff</caption>
<g:each in="${dbResults}" var="aThing">
<tr>
<td>${aThing}</td>
</tr>
</g:each>
</table>

meteor with flow router layout is rendered twice

I don't know why but my layout is rendered two times.
Here is my index.html:
<head>
<title>title</title>
</head>
<body>
{{>layout}}
</body>
Here is my layout:
<template name="layout">
{{#if canShow}}
{{>Template.dynamic template=content}}
{{else}}
{{> loginButtons}}
{{/if}}
</template>
So here without route my template is display just one time.
Here is my route:
FlowRouter.route('/', {
action() {
BlazeLayout.render("layout", {
content: "home"
});
}
});
But with this route my template is display a second time.
This is my helpers, I think there is nothing to do with this problem but we never know.
Template.home.onCreated(function() {
this.autorun(() => {
this.subscribe('post');
});
});
Template.layout.helpers({
canShow() {
return !!Meteor.user();
}
});
Template.home.helpers({
cats() {
return Posts.find({});
}
});
you don't need to render layout in the body.
The router will take care of the rendering.
so, just have
<body>
</body>
or don't even have it at all.
Edit: Thanks to Keith, I have a better understanding of my problem. Here is his comment:
one thing to keep in mind, all the html you write in meteor isn't kept as html. It all gets converted to javascript. Things like index.html do not get pushed to the browsesr. Meteor just takes all the html you write converts it to javascript and renders what it needs to based on what your code says. This is how it knows todynamically change and rerender html
For things like changing title of the head or add meta etc, we can do it directely in the javascript.
ex: Meteor - Setting the document title

How do you post data to CouchDB both with and without using JavaScript

I have a show which displays a form with fields populated from a document. I'd like to change the values in the field and then save the updated document.
I'm having trouble finding a clear, concise example of how to do this.
Seriously, just finishing this example would work wonders for so many people (I'm going to leave a lot of stuff out to make this concise).
Install Couchapp
This is outside the scope of my question, but here are the instructions for completeness.
Create a couchapp
Again, this is kind outside the scope of my question. Here is a perfectly concise tutorial on how to create a couchapp.
Create a template
Create a folder in the root of your couchapp called templates. Within the templates folder create an HTML page called myname.html. Put the following in it.
<html>
<head>
<title>{{ title }}</title>
</head>
<body>
<form method='post' action='#'>
<fieldset>
Hello <input type='text' name='name' value='{{ name }}'>
<input type='submit' name='submit' value='submit'>
</form>
</body>
</html>
Create a show
See the tutorial above for hwo to do this.
Add this code to a show called myname.
function(doc, req) {
if (doc) {
var ddoc = this
var Mustache = require("vendor/couchapp/lib/mustache");
var data = {
title: "The Name",
name: "Bobbert"
}
return Mustache.to_html(ddoc.templates.myname, data)
} else {
return ('nothing here baby')
}
}
Update the document with a new name by ...
So who can complete this step via both the client side and the server side?
Please don't point me to the guide, I need to read it in your words.
Thanks.
Edit:
Although the return value isn't pretty, just posting a form to the update handler will update the document.
You will probably want to look into update handler functions.
An update handler handles granular document transformations. So you can take 1 form, that has one distinct purpose, and only update the relevant fields in your document via the update handler.
Your update handler will need to take a PUT request from your form. A browser can't do this directly, so you'll need some javascript to handle this for you. If you're using jQuery, this plugin can take your form and submit it seamlessly via AJAX using PUT for you.
Inside the function, you can take the fields you are accepting, in this case name and apply that directly to the document. (input validation can be handled via the validate_doc_update function)
Update Handler (in your Design Document)
{
"updates": {
"name": function (doc, req) {
doc.name = req.form.name;
return [doc, "Name has been updated"];
}
}
}
HTML
<form id="myForm" action="/db/_design/ddoc/_update/name/doc_id">...</form>
JavaScript
$(document).ready(function() {
$('#myForm').ajaxForm({
type: "PUT",
success: function () {
alert("Thank you");
}
});
});
Once you've gotten this basic example up and running, it's not much more difficult to add some more advanced features to your update handlers. :)

Resources