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

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;
}

Related

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

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}));

Node.JS :How to forward to the login page with parameter?

I am building the login function with node.js, express,express-session, ejs.
This is the log-in page (i.e. index.ejs)
<html>
<head>
<meta charset="UTF-8">
<title>Video Chat Room</title>
<link rel="stylesheet" type="text/css" href="/css/style.css">
<style>
#message
{
color:red;
font-weight: bold;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script>
$( document ).ready(function() {
var messageBox=document.getElementById("message");
<%
if (typeof errorField!="undefined") {
switch (errorField) {
case "email":%>
messageBox.innerHTML="Your email address has been used by another user, please use another one.";
document.getElementById("email").focus();
<% break;
case "logoutSuccess":%>
messageBox.innerHTML="<%=alias%> has successfully logged out.";
<% break;
}
}
%>
});
</script>
</head>
<body>
<form method="post" action="/login">
Nick name/Alias:<input type=text required name="alias" value="<%=((typeof user=='undefined')?'':user.alias)%>"><br>
Email Address:<input type=email id="email" required name="email" value="<%=((typeof user=='undefined')?'':user.email)%>"><br>
<input type="submit" value="Login">
</form>
<div id="message">
</div>
</body>
</html>
Here is the server-side code(i.e. server.js)
........
app.get('/',function (req,res) {
res.render('../ejs/index.ejs');
});
app.post('/login', function(req, res) {
var alias = req.body.alias;
var email = req.body.email;
var user=require("./classes/user.js");
user.alias=alias;
user.email=email;
if (user.login) {
req.session.user = user;
res.redirect('/home/');
} else {
res.locals.errorField="email";
res.locals.user=user;
res.render('../ejs/index.ejs');
}
It works fine, however, when the login process failed, although the web output the login page(i.e. index.ejs), the browser address bar still stay in "/login"; is it possible to change the browser address bar to "/" and the index.ejs can read the errorField and user value also?
If you want a user login error to go back to /, then you need to do res.redirect("/"). If you want to add some parameters to that page that can be used either in server-side rendering or client-side rendering, then the simplest way to do that is to add query parameters:
res.redirect("/?error=email")
Then, either your server-side rendering or some client-side Javascript can pick up that query parameter and display something like a message in the page that explains what happened.
There are lots of ways to do something like this. Here's a partial list:
Send data in a query parameter that server-side rendering picks up and adds some explanatory text to the page.
Send data in a query parameter that client-side Javascript picks up and adds some explanatory text to the page.
Set a cookie that contains an error message that your server-side rendering will pick upon the redirect and then clear the cookie.
Set some error data in the user session object on the server that your server-side rendering will pick up when rendering the redirected page and the clean that info from the session.
Use an Express middleware module built for these temporary messages called flash.
Render an error page from the server (without an immediate redirect) and then have a client-driven redirect back to "/" that occurs after a few seconds time. The client-driven redirect can either be from a <meta> tag or can be client-side Javascript on a timer.

handling JSOM clientcontext properly

I am trying out JSOM in Sharepoint 2016. I have made a WebPart containing the following code -
<div id="user-output"></div>
Movie Title: <input type="text" id="movie-title" /><br />
Description: <input type="text" id="movie-description" /><br />
<button type="button" id="submit-button">Add Movie</button>
<div id="movies-output"></div>
<script type="text/javascript" src="/SiteAssets/jquery-3.2.1.min.js"></script>
<script type="text/ecmascript" src="../_layouts/15/SP.UserProfiles.js"></script>
<script type="text/javascript">
$(function () {
$('#submit-button').on('click', function () {
var context = SP.ClientContext.get_current();
var movies = context.get_web().get_lists().getByTitle('Movies');
var movieCreationInfo = new SP.ListItemCreationInformation();
var movie = movies.addItem(movieCreationInfo);
movie.set_item("Title", $('#movie-title').val());
movie.set_item("MovieDescription", $('#movie-description').val());
movie.update();
context.load(movie);
context.executeQueryAsync(success, failure);
});
function success() {
$('#movies-output').text('Created movie!');
}
function failure() {
$('#movies-output').text('Something went wrong');
}
var upp;
// Ensure that the SP.UserProfiles.js file is loaded before the custom code runs.
//SP.SOD.executeOrDelayUntilScriptLoaded(getUserProperties, 'SP.UserProfiles.js');
SP.SOD.executeFunc('userprofile', 'SP.UserProfiles.PeopleManager', getUserProperties);
//SP.SOD.executeFunc('SP.UserProfiles.js', getUserProperties);
function getUserProperties() {
// Get the current client context and PeopleManager instance.
var clientContext = new SP.ClientContext.get_current();
var peopleManager = new SP.UserProfiles.PeopleManager(clientContext);
upp = peopleManager.getMyProperties();
clientContext.load(upp, 'UserProfileProperties');
clientContext.executeQueryAsync(onRequestSuccess, onRequestFail);
}
// This function runs if the executeQueryAsync call succeeds.
function onRequestSuccess() {
$('#user-output').html('User Name: ' + upp.get_userProfileProperties()['PreferredName'] +
'<br/>Department: ' + upp.get_userProfileProperties()['Department'] +
'<br/>Designation: ' + upp.get_userProfileProperties()['Title'] +
'<br/>Employee ID: ' + upp.get_userProfileProperties()['EmployeeID'] +
'<br/>Branch Code: ' + upp.get_userProfileProperties()['branchCode']
);
}
// This function runs if the executeQueryAsync call fails.
function onRequestFail(sender, args) {
$('#user-output').text("Error: " + args.get_message());
}
});
What this code does is -
Show user information in user-output div at document load ready
Saves a movie record when Add Movie button is clicked
However, for some reason, when Add Movie button is clicked, the code adds two movies instead of one. I think it has something to do with the ClientContext. But I am not sure why, or how to solve it. Can anyone help?
I am not sure how it happened, or if it's a bug, but while fiddling with the page source to find out why double posting was occurring, I saw that my web part code was being rendered twice in the page. One part was visible, and another was under a hidden div. However, when I went to edit page to delete the hidden web part, I couldn't. So I restored my page to the template version and re-added the web part. After that the web part was working correctly. There were no problems with the code.

Create jqueryMobile page dynamically based on the url

I am creating an application to get some experience in jQuery Mobile and backbone. I have made a "restful" API with node.js that handles the data I need. It works fine with all my static pages I made in index.html. But when I need to create a page with data from a certain id I am a bit lost.
For example when I want to display all items(/items) I have a data-role=page with id items that list all items, but when I need to go to a detailed page for each item (/items/1) i want to create that details page whenever a user wants details on an item, in other words when a user visit the url spots#3 for example.
Is this possible?
my router: the model gives me all data i want
Spoter.Router = Backbone.Router.extend({
routes: {
"": "",
"spot#:id": "spotDetails"
},
//Details on a certain spot with id
spotDetails: function(id) {
var spotDetailsContentDiv = Spoter.spotDetailsContent;
spotDetailsContentDiv.empty();
var spot = new Spoter.spotModel({id: id});
spot.fetch({
successCallback: function(data) {
var spotDetailsView = new Spoter.spotDetailsView({
model: data
});
spotDetailsContentDiv.html(spotDetailsView.render().el);
}
});
}
});
View:
Spoter.spotDetailsView = Backbone.View.extend({
render:function () {
this.$el.html(this.template(this.model));
return this;
}
});
Template with underscore
<ul data-role="listview" data-theme="c" data-inset="true">
<li>
<a href="#">
<h1><%= this.model.name %></h1>
<p><%= this.model.description %></p>
</a>
</li>
</ul>

Utilizing Bootstrap's typeahead as a search function

I've got typeahead working just fine, but I am too inexperienced with the Javascript to understand how to turn the typed results into a link.
<input type="text"
class="span3"
data-provide="typeahead"
placeholder="City Search:"
data-items="6"
autocomplete="off"
data-source=["Neuchatel","Moutier"]">
So, I really just want to know how to turn the strings from data-source into links to other pages. Hopefully this is fairly simple.
thanks!
You can turn the strings into links easily..
<input type="text" data-provide="typeahead" data-source="["/foo.html","http://www.google.com","/about.html"]">
Are you also looking to take the link from the input and then navigate to the selected page?
EDIT: Navigate to item selected in typeahead..
In this case you'd define an object map that contain keys (label) and values (url) like..
var data = {
"login":"/login",
"home":"/",
"user":"/user",
"tags":"/tags",
"google":"http://google.com"
};
Then you'd initiate the typeahead. The source and updater functions would be defined to handle 1) creating the typeahead data source array from the map object, and 2) navigate to the appropriate url when an item is selected..
$('.typeahead').typeahead({
minLength:2,
updater: function (item) {
/* navigate to the selected item */
window.location.href = data[item];
},
source: function (typeahead, query) {
var links=[];
for (var name in data){
links.push(name);
}
return links;
}
});
Demo on Bootply

Resources