I'm using express.js with the bodyParser middleware (which, is technically node-formidable, behind the scenes).
I'd like to take and create a form that represents inputs for each of the data elements listed here:
{
"name" : "asdf",
"children" : [
{
"child_name" : "xyz1234",
"size" : 12
},
{
"child_name" : "1234aszds"
"size": 14
}
]
}
The number of children here will be dynamic, but I'd like to be able to add additional fields client-side, and have them map into req.body on the server.
Note, I'm looking for how to name the inputs in the client in order for the raw POST body to have the correct encoding to allow node-formidable to parse them out into an array.
The general way to achieve this is as follows (excluding non-essential attributes for clarity):
<input name="name"/>
<input name="children[0[child_name]"/>
<input name="children[0[size]"/>
<input name="children[1[child_name]"/>
<input name="children[1[size]"/>
Note that the square brackets are not balanced, this is required due to a "bug" in the "querystring" package (or, at least it would seem so, as of August 20th, 2013).
But, if the square brackets are left unbalanced, as above, the result is that this will be parsed into the object structure requested in my original question (i.e. this will be made available as "req.body").
The benefit of this is that it does not require any Javascript to do any "pre-flighting" on form submission.
Using an HTML form with input fields will be problematic since there is no easy way to define the structure without parsing each field name to determine its place in the structure.
For instance, if you posted the following:
<form action="/post" method="post">
<input type="text" name="data[name]">
<input type="text" name="data[children][][child_name]">
<input type="text" name="data[children][][size]">
<input type="text" name="data[children][][child_name]">
<input type="text" name="data[children][][size]">
<input type="submit" value="Submit">
</form>
Formidable would interpret into this JSON structure:
[ [ 'data[name]', 'name' ],
[ 'data[children][][child_name]', [ 'cname1', 'cname2' ] ],
[ 'data[children][][size]', [ 'csize1', 'csize2' ] ] ]
To post deep structures this way, I would recommend using AJAX and posting a complete JSON object instead, which I have outlined below.
The following should allow you to copy all of the fields directly into req.body. I elected to use the .mixin() helper from the utile library.
var utile = require('utile')
var form = new formidable.IncomingForm()
, fields = {}
form
.on('field', function (field, value) {
fields[field] = value
})
.on('end', function () {
req.body = utile.mixin(req.body, fields)
})
Based on your JSON in the question itself, you could then access your values like this:
console.log(req.body.children[0].child_name)
If you are looking for a native way of merging the objects, see Combine or merge JSON on node.js without jQuery for a way to do this.
Hopefully this answers the question and helps you out!
Related
I have an html/handlebars form set up with a Node/Express backend. the form offers options populated from a database. I am able to get the form to return a single user selected value and save it to my mongodb, but I really need the whole object.
{{#each proxyObj}}
<p>
<label>
<input type="radio" name="proxyTitle" value="{{title}}"/>
<span>{{title}}</span>
</label>
</p>
{{/each}}
and this is the express:
router.post("/proxies/:id", ensureAuthenticated, (req, res) => {
Project.findOne({
_id: req.params.id
}).then(project => {
const newProxy = {
proxyTitle: req.body.proxyTitle
// I need the other object values to go here, or to be able to retrieve them later
};
// Add to proxy array on the Project object in the collection
project.proxies.push(newProxy);
project.save().then(project => {
res.redirect(`/projects/stakeholders/${project.id}`);
});
});
});
Is it more sensible to try to load in the entire object as a value in the input field, or to return the id of the object, and look it up in the db? I need to display some of the returned object information on the same page, and also to use it later. Which is more efficient, and what is the best way to achieve it?
If I'm getting it right, the problem is that you're trying to put multiple inputs with the same name on one form in <input type="radio" name="proxyTitle" value="{{title}}"/>, which gives you something like
<input type="radio" name="proxyTitle" value="Title 1"/>
<input type="radio" name="proxyTitle" value="Title 2"/>
<input type="radio" name="proxyTitle" value="Title 3"/>
As explained here, the browsers will chew it, but the server-side handling may require some adjustments.
In your case, the easiest fix would be to add index to the names of parameters. So, your form would be looking like this:
{{#each proxyObj}}
<p>
<label>
<input type="radio" name="proxies[{{#key}}]" value="{{this}}"/>
<span>{{this}}</span>
</label>
</p>
{{/each}}
(note that if proxyObj is an array, you would have to use #index instead of #key; also, depending on the proxyObj fields' structure, you may have to use this.title as the values to display and whatnot).
As for your server-side handling, you'll have to loop through the proxies you receive and handle them one by one, e.g.
router.post("/proxies/:id", ensureAuthenticated, (req, res) => {
Project.findOne({
_id: req.params.id
}).then(project => {
project.proxies = []; // this is only in case you wanna remove the old ones first
const proxies = req.body.proxies;
for(let i = 0; i < proxies.length; i++) {
// Add to proxy array on the Project object in the collection
project.proxies.push({ proxyTitle: proxies[i].title });
}
project.save().then(project => {
res.redirect(`/projects/stakeholders/${project.id}`);
});
});
});
A node, express, mongodb question.
I have a webpage, with a some JS-code. The user types some things into the my form and then they can generate a table with data. Before the table is printed to the user the data as saved as a variable (Array). The array itself is named obj.invoices.
I can easily save the data from the form to the DB using the "name". When I use it shows up in the req.body, which i then can use in my controller and save it to the DB.
But how do i pass the generated variable (obj.invoices) from the page so that it will follow along in the POST and shows up in the req.body?
As for now the array isn't parsed so i cant build a function in express/mongoose to save the data to the DB.
I solved it but I'm quite sure this is a bad solution.
When finished generating the array I run this function,
function showArray() {
var json_data = JSON.stringify(myArray);
document.getElementById('showArray').innerHTML = json_data;
}
This convert the array to string, and then post it into a input within my form;
<div class="field">
<div class="control">
<textarea id="showArray" class="textarea is-info" type="text" name="ArrayToDb"></textarea>
</div>
</div>
So when i submit my form the array as a string is posted with the req.body. Then in my controller.js for the app I convert the string back to an array;
let jsonArray = JSON.parse(req.body.ArrayToDb)
and then i save it to the DB
newLan.fakturor = jsonArray;
newLan.save(function (err) {
console.log(newLan._id)
});
Like I said, this is most likely a really bad way to do it but it works for me, for now.
I originally had an array of data with sub-arrays, however I need to make this an object instead.
My final data should look something like:
var invalidFields = {
0: {
lastName: <input type="text" ... />,
city: <input type="text" ... />,
...
}
2: {
lastName: <input type="text" ... />,
city: <input type="text" ... />,
...
}
}
Please note, above, that there is no key 1.
I am having trouble adding the data to the object. I am iterating over an existing array and finding matches that are invalid, and then want to "push" them to a new object.
For example the key for the top-level multi-dimension e.g 0 or 2 is stored as passengerIndex in my code but I cannot work out how to add it.
I am initialising with invalidFields = {}
I have tried. invalidFields[passengerIndex]
I have also tried invalidFields.passengerIndex
And invalidFields = invalidFields[passengerIndex]
But none work.
Assuming you are working with Javascript
You will need to find another variable-name, maybe add an underscore before the number since a variable cannot start with a number
see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types
A JavaScript identifier must start with a letter, underscore (_), or dollar sign ($)
or maybe you could use a Map https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
i have an Angular Storefront app set up. I have a shopping cart functionality in place and a stripe "pay with card" button etc. pretty much looks like this:
<form action="/#/order" method="POST">
<script
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="{{ stripeApiKey }}"
data-billingAddress=true
data-shippingAddres=true
data-amount="{{ amount }}"
data-name="StoreFront Name"
data-description="Custom-Made Jewellery"
data-image="../images/www/logo.png"
data-locale="auto">
</script>
</form>
Evrything up to this point is working fine. I submit the form and stripe returns the token but the form goes to the server following the route localhost/order (without the # symbol) instead of angular's localhost/#/order.
Why is stripe forcing this redirect? In other words why isn't angular capturing this return call?
Anyways. Then I create a route with Laravel to capture this and dump to inspect the returned data like so:
Route::post('/order', function($request){
dd($request);
});
Yep, data captured by stripe-generated form is returned except amount is missing... I mean everything including stripeToken, buyer's details such as: Name, Email, Billing and Shipping address are returned BUT detail regarding the amount is missing.
Is this normal or I'm I missing something?
Lastly currency is still showing the default: Where can I change currency from say USD to GBP?
Thanks in advance
1/ I don't think Checkout is forcing the redirect, but I don't know enough about Angular to explain what's going on, sorry.
2/ Yes, this is normal. The amount passed to Checkout in the data-amount configuration option is used for display purposes only. The actual amount that is charged is the one you pass in the amount parameter in the charge creation request in your server-side code.
If you need the amount to be user-specified (for instance, if you're taking donations), you'll need to add the amount to the form. Here is a simple JSFiddle to illustrate this case: https://jsfiddle.net/ywain/g2ufa8xr/
3/ You can use the data-currency parameter to change the currency displayed in the Checkout form. Just like data-amount, this is for display purposes only and the actual currency used for the charge is specified by the currency parameter in the charge creation.
This is what i managed to do.
I went with the custom form approach. I had a form template to capture both customer and card inputs in billing.template.html like so:
<form method="POST" id="payment-form">
<span class="payment-errors"></span>
<div>
<label>Name</label>
<input type="text" name="name" data-stripe="name">
</div>
<div>
<label>Email</label>
<input type="text" name="email" data-stripe="address_email">
</div>
<div>
<label>Address Line 1</label>
<input type="text" name="street" data-stripe="address_line1">
</div>
<div>
<label>Postcode</label>
<input type="text" name="postcode" data-stripe="address_zip">
</div>
<div>
<label for="country">Country</label>
<select ng-include="'../templates/_partials/_countrylist.html'"
id="countries" name="country" class="form-control"
name="country" ng-model="country" id="country" size="2"
data-stripe="address_country" required></select>
</div>
<div class="form-row">
<label>
<span>Card Number</span>
<input type="text" name="cardNumber" size="20" data-stripe="number"/>
</label>
</div>
<div class="form-row">
<label>
<span>CVC</span>
<input type="text" name="cvc" size="4" data-stripe="cvc"/>
</label>
</div>
<div class="form-row">
<label>
<span>Expiration (MM/YYYY)</span>
<input type="text" name="expMonth" size="2" data-stripe="exp-month"/>
</label>
<span> / </span>
<input type="text" name="expYear" size="4" data-stripe="exp-year"/>
</div>
<button id="customButton">Pay with Card</button>
</form>
I know we are not supposed to use name attribute in those form inputs but i left them so i could use angular validation, but i remove them using jquery before submitting to server.
Now i created a controller to handle the form: BillingController.js. In there i had an "on click" handler which kick started things by getting a hold of the form and doing some preparatory work: disabling button to prevent further clicks and removing those 'dreaded' name attributes, comme ca:
$('#customButton').on('click',function(event) {
var $form = $('#payment-form');
// Disable the submit button to prevent repeated clicks
$form.find('button').prop('disabled', true);
//NOW REMOVE THOSE NAME ATTRIBUTES
$form.find('input').removeAttr('name');
// call Stripe object and send form data to get back the token.
// NOTE first argument is $form
Stripe.card.createToken($form, stripeResponseHandler);
// Prevent the form from submitting with the default action
return false;
});
Now let me quote the documentation here as this is very important to understand: https://stripe.com/docs/tutorials/forms
The important code to notice is the call to Stripe.card.createToken.
The first argument is the form element containing credit card data
entered by the user. The relevant values are fetched from their
associated inputs using the data-stripe attribute specified in the
form.
Next we create stripeResponseHandler(). Remember it was the second argument in Stripe.card.createToken($form, stripeResponseHandler); above which gets called when Stripe returns the token.
function stripeResponseHandler(status, response) {
var $form = $('#payment-form');
if (response.error) {
// Show the errors on the form
$form.find('.payment-errors').text(response.error.message);
$form.find('button').prop('disabled', false);
} else {
// response contains id and card, which contains additional card details
var token = response.id;
// Insert the token into the form so it gets submitted to the server
$form.append($('<input type="hidden" name="stripeToken" />').val(token));
// and submit
$form.get(0).submit();
}
};
This is copy and paste stuff from stripe's own documentation: https://stripe.com/docs/tutorials/forms. Now, I want to say that, this is where a lot of us were tripping over the fact that form was performing a redirect etc. - notice final line $form.get(0).submit(); . Thats what caused the auto submit, redirecting to what ever action was on form, if u had any (in my case action attribute wasn't necessary as i was doing redirects in my controller).
So i decided to remove $form.get(0).submit() and implemented my own redirect after i was done sending data to the server.
NOTE: Stripe's response will have included data from the $form - try console.log(response); to have an idea of what's being posted back.
FINALLY:
We check if there were any errors returned and if so display them. Otherwise its all good, send data to the server.
The final code looks like:
function stripeResponseHandler(status, response) {
var $form = $('payment-form');
if (response.error) {
// Show the errors on the form
$form.find('.payment-errors').text(response.error.message);
} else {
// response contains id and card, which contains additional card details
var token = response.id;
// prepare data
var data = {
stripeToken: token,
fullName: response.card.name,
street: response.card.address_line1,
postcode: response.card.address_zip,
town: response.card.address_city,
country: response.card.address_country,
last4: response.card.last4
};
// send to server
$http.post('/checkout', data).then(function(result){
// here you can redirect yourself.
window.location.href = "/#/order-complete";
});
}
};
Angular really playing well with stripe here. Check out this link also: https://gist.github.com/boucher/1750368 - learn a lot from it.
I hope it helps someone today. Happy coding!
Stripe doesn't get involved with your form aside from preventing the default action on form submit event and stopping event propagation. Once the checkout process completes, it appends the relevant data to your form and then triggers a form submit event that is handled by HTML / Javascript natively.
I recommend using something like https://github.com/tobyn/angular-stripe-checkout to get your Stripe response handled correctly by Angular.
Otherwise you could add ng-submit="handleStripeCheckout($event)" to your form instead of action="/#/form". When Stripe's checkout process completes, your $scope.handleStripeCheckout method will be run and you can analyze the new form data inside that method.
Edit: Stripe checkout.js actually triggers form.submit(). That's a pretty bad bug on their part considering that almost no browsers handle that correctly. (Form submitted using submit() from a link cannot be caught by onsubmit handler)
I have a photography site driven in part by the 'Photoshelter' service, and I put an embedded search bar in my nav.
<form action="http://brettcole.photoshelter.com/search" method="get">
<input type="text" placeholder="search library" size="15" name="I_DSC">
<input type="submit" value="go">
<input type="hidden" name="I_DSC_AND" value="t">
<input type="hidden" name="_ACT" value="search">
</form>
It allows for a search to be executed with the no search term present, which then returns all 12,000 photos in my archive. Is there a best practice for preventing this, such that the user has to type something or nothing will happen when they click search?
It's also present on my advanced search page. This is generated by a search widget shortcode in the Photoshelter back end. I'd like to apply the same thing here, but not sure how the widgetization of it might affect the process.
Many thanks
You can use the onsubmit attribute of the form element to check if the user has entered information in any fields and then prevent submit based on that.
<script>
function checkValues() {
searchBox = document.getElementById("SearchField");
return searchBox.value != ""; // True will allow submission and false will prevent it
}
</script>
With this...
<form onsubmit="checkValues();" action="http://brettcole.photoshelter.com/search" method="get">
<input type="text" id="SearchField" placeholder="search library" size="15" name="I_DSC">
<input type="submit" value="go">
<input type="hidden" name="I_DSC_AND" value="t">
<input type="hidden" name="_ACT" value="search">
</form>
Should do what you need.
See also this answer: How to grab the onSubmit event for a form?
The actual search isn't working
From the contact page for example, it returns this
http://brettcolephotography.com/contact.html?I_DSC=red&I_DSC_AND=t&_ACT=search
the formula for my search returns is
http://brettcole.photoshelter.com/search?I_DSC=red&I_DSC_AND=t&_ACT=search
this search bar is present on all three of my web properties, personal site, blog, and photoshelter site, all three are tightly integrated to where you can't tell when you're switching between them. It needs to work regardless of where the search is being executed from. Thanks
Here is a function I wrote to disable the search form submitting if the search field is empty. It also focuses the cursor on the search field if the form is not submitted so that the user does not think that search is broken.
This is assuming that jQuery is loaded. Hope this helps!
var preventSearchIfEmpty = function() {
$('form[method="get"]').on( 'submit', function( ev ){
var query = $('input[type="text"]').val(),
queryLength = query.length;
if ( 0 === queryLength ) {
// Make the cursor blink so user is aware it's not broken, they need input to search
$('input[type="search"]').focus();
ev.preventDefault();
return;
}
});
}();