Trying to implement closure on event handler? Error: undefined - javascript

I have trying to get the values out of a form when the register button is clicked.
setupFormUI() and the relevant form fields are saved in variables
$($rego_form).on("submit", getRegistrationFormValue); is called - a handler should be able to have access to setupFormUI() variables (closure) but it seems to not do anything
ISSUE: getRegistrationFormValue doesn't log anything. I can make it work if I pass arguments to the function... but I want to use
closure
setupFormUI();
function setupFormUI() {
var $name = $("#name");
var $age = $("#age");
var $department = $("#department");
var $position = $("#position");
var $rego_form = $("#rego-form");
$($rego_form).on("submit", getRegistrationFormValue);
}
function getRegistrationFormValue() {
// alert("asdasd");
var name = $name.val();
var age = $age.val();
var department = $department.val();
var position = $position.val();
console.log("----->", name, age, position, department);
}
html
<form id="rego-form">
<div class="row">
<div class="col-md-5">
<div class="form-group">
<label>Company (disabled)</label>
<input type="text" class="form-control" disabled placeholder="Company" value="Creative Code Inc.">
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label>name</label>
<input type="text" id="name" class="form-control" placeholder="name" value="michael">
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="exampleInputEmail1">Age</label>
<input id="age" class="form-control" placeholder="age">
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label>Department Name</label>
<input type="text" id="department" class="form-control" placeholder="department" value="Marketing">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label>Position</label>
<input type="text" id="position" class="form-control" placeholder="position" value="social media manager">
</div>
</div>
</div>
<button type="submit" id="rego-user-btn" class="btn btn-info btn-fill pull-right">Register</button>
<div class="clearfix"></div>
</form>

You need the variables to be in scope, you can use an anonymous closure as a callback to achieve this.
setupFormUI();
function setupFormUI() {
var $name = $("#name");
var $age = $("#age");
var $department = $("#department");
var $position = $("#position");
var $rego_form = $("#rego-form");
$rego_form.on("submit", function(){
var name = $name.val();
var age = $age.val();
var department = $department.val();
var position = $position.val();
console.log("----->", name, age, position, department);
});
}

An alternative to the accepted answer — give the "handler" a meaningful context of this with Function.prototype.bind(), or maybe just use the ES6 class.
setupFormUI();
function setupFormUI() {
var args = {
$name: $("#name"),
$age: $("#age"),
$department: $("#department"),
$position: $("#position"),
$rego_form: $("#rego-form")
}
args.$rego_form.submit(getRegistrationFormValue.bind(args));
}
function getRegistrationFormValue(e) {
var name = this.$name.val();
var age = this.$age.val();
var department = this.$department.val();
var position = this.$position.val();
console.log("----->", name, age, position, department);
e.preventDefault();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form id="rego-form" action="#">
<input id="name" value="John Doe" />
<input id="age" value="37" />
<input id="department" value="Some dept" />
<input id="position" value="Debt collector" />
<button type="submit">Submit</button>
</form>

This is no closure, if the variable in setupFormUI is referenced, it is a closure.
getRegistrationFormValue is just a variable whose function is passed directly to the event trigger (and is asynchronous), note that it is not executed in setupFormUI, nor is it defined in setupFormUI, When it is executed, it has nothing to do with setupFormUI.
Mike Zinn's answer defines an anonymous function in setupFormUI, which in turn refers to the variable in setupFormUI, which is a closure.

Related

How do I display multiple inputs?

const form = document.querySelector("#user-form");
const userInput = document.querySelector("#name", "#surname", "#age", "#country");
const userList = document.querySelector(".list-group");
const firstCardBody = document.querySelectorAll(".card-body")[0];
const secondCardBody = document.querySelectorAll(".card-body")[1];
const filter = document.querySelector("#filter");
const clearButton = document.querySelector("#clear-users");
<form id="user-form" name="form">
<div class="form-row">
<div class="form-group col-md-6">
<input class="form-control" type="text" name="name" id="name" placeholder="İsim">
</div>
<div class="form-group col-md-6">
<input class="form-control" type="text" name="surname" id="surname" placeholder="Soyadı">
</div>
<div class="form-group col-md-6">
<input class="form-control" type="text" name="age" id="age" placeholder="Yaş">
</div>
<div class="form-group col-md-6">
<input class="form-control" type="text" name="country" id="country" placeholder="Ülke">
</div>
</div>
<button type="submit" class="btn btn-primary">Bilgilerinizi Kaydedin</button>
</form>
My form looks like this.
But when I run my JS code, it only shows me the name not surname, age or country. How do I display all of them?
enter image description here
document.querySelector cannot be use the way you use it as it returns the first matched element (in your case the name input). To get all input fields you can do it like this:
const userInput = document.querySelectorAll(".form-control");
This returns all 4 input fields in an array which can be iterated via
userInput.forEach(input => { ... });
Get a specific element like this:
Array.from(userInput).find(input => input.id === "name");
Another approach would be to get the input fields via their ID's:
const name = document.querySelector("#name");
const surname= document.querySelector("#surname");
const age = document.querySelector("#age");
const country = document.querySelector("#country");

JavaScript form validation not working as intended

Good morning,
I'm working on some simple form validation. Whenever I submit my form, the error message appears, but I can repeatedly spam the button for numerous error messages. Is there a way I can change this to only show the error message once? I've also noticed that even if I populate both fields it will still flash quickly in my console with the error log but not show the error.
Can anyone tell me what I'm doing wrong here?
var uname = document.forms['signIn']['userame'].value;
var pword = document.forms['signIn']['password'].value;
function validateMe (e) {
if (uname.length || pword.length < 1 || '') {
var container = document.getElementById('error-container');
var errorMsg = document.createElement('div');
errorMsg.className = 'error-message';
errorMsg.innerHTML = '<span class="heading-large">Please enter a valid username or password</span>';
container.appendChild(errorMsg);
console.log('An error occured');
return false;
}
}
<form id="signIn" action='#'>
<div class="boxed left-floater">
<h1 class="heading-large margin-top">Sign in</h1>
<div id="error-container"></div>
<div class="form-group">
<label class="form-label-bold" for="username">Username</label>
<input class="form-control log-in-form-control" id="username" name="username" type="text">
</div>
<div class="form-group">
<label class="form-label-bold" for="password">Password</label>
<input class="form-control log-in-form-control" id="password" type="password" name="password">
</div>
<div>
<a class="right-floater forgotten-password" href="forgottenpassword.html">Forgotten Password</a>
<button class="button clear right-floater" type="submit" onclick="validateMe();">Sign In</button>
</div>
</div>
</form>
Fiddle
You must be clearing the contents of your container to avoid duplication of elements. Below are few things to note:
You were trying to get userame instead of username in your fiddle. May be spelling mistake.
Keep input type=submit instead of button
Pass the event to your validateMe function to prevent the default action of post.
Move the variables within the function to get the actual value all the time
function validateMe(e) {
e.preventDefault();
var uname = document.forms['signIn']['username'].value;
var pword = document.forms['signIn']['password'].value;
var container = document.getElementById('error-container');
container.innerHTML = ''; //Clear the contents instead of repeating it
if (uname.length < 1 || pword.length < 1) {
var errorMsg = document.createElement('div');
errorMsg.className = 'error-message';
errorMsg.innerHTML = '<span class="heading-large">Please enter a valid username or password</span>';
container.appendChild(errorMsg);
console.log('An error occured');
return false;
}
}
<form id="signIn" action='#'>
<div class="boxed left-floater">
<h1 class="heading-large margin-top">Sign in</h1>
<div id="error-container"></div>
<div class="form-group">
<label class="form-label-bold" for="username">Username</label>
<input class="form-control log-in-form-control" id="username" name="username" type="text">
</div>
<div class="form-group">
<label class="form-label-bold" for="password">Password</label>
<input class="form-control log-in-form-control" id="password" type="password" name="password">
</div>
<div>
<a class="right-floater forgotten-password" href="forgottenpassword.html">Forgotten Password</a>
<input value="Sign In" class="button clear right-floater" type="submit" onclick="validateMe(event);" />
</div>
</div>
</form>
Updated Fiddle
Edit - if condition was failing and have updated it accordingly
this is full work code
var uname = "";
var pword = "";
function validateMe(e) {
e.preventDefault();
uname = document.forms['signIn']['username'].value;
pword = document.forms['signIn']['password'].value;
if (uname.length || pword.length < 1 || '') {
var container = document.getElementById('error-container');
var errorMsg = document.createElement('div');
errorMsg.className = 'error-message';
errorMsg.innerHTML = '<span class="heading-large">Please enter a valid username or password</span>';
container.appendChild(errorMsg);
console.log('An error occured');
return false;
}
return true;
}
<form id="signIn">
<div class="boxed left-floater">
<h1 class="heading-large margin-top">Sign in</h1>
<div id="error-container"></div>
<div class="form-group">
<label class="form-label-bold" for="username">Username</label>
<input class="form-control log-in-form-control" id="username" name="username" type="text">
</div>
<div class="form-group">
<label class="form-label-bold" for="password">Password</label>
<input class="form-control log-in-form-control" id="password" type="password" name="password">
</div>
<div>
<a class="right-floater forgotten-password" href="forgottenpassword.html">Forgotten Password</a>
<button class="button clear right-floater" type="submit" onclick="validateMe(event);">Sign In</button>
</div>
</div>
</form>

My JavaScript form validation function is called two times

I am trying to print the value from the form when a user submits the function but a blank value is returned.
Here is my JavaScript code:
var login = new function()
{
var name = null ;
this.validation = function()
{
this.name = document.getElementById("Username").value;
console.log(this.name);
document.getElementById("demo").innerHTML = this.name;
};
};
And my HTML form as :
<body>
<div class="container">
<div class="col-md-8">
<div class="starter-template">
<h1>Login with javascript</h1>
<p class="lead">Please Enter Following Details</p>
<h1 id="demo"></h1>
<form name="form" onSubmit="return login.validation();" action="#" method="post">
<div class="form-group">
<label for="exampleInputEmail1">Username</label>
<input type="text" name="username" class="form-control" id="Username" placeholder="Please Enter your Username">
</div>
<div class="form-group">
<label for="exampleInputEmail1">Email address</label>
<input type="email" class="form-control" id="Email" placeholder="Please enter your Password">
</div>
<div class="form-group">
<label for="exampleInputPassword1">Password</label>
<input type="password" class="form-control" id="Password" placeholder="Password">
</div>
<div class="form-group">
<label for="exampleInputPassword1">Re-Password</label>
<input type="password" class="form-control" id="Re-Password" placeholder="Password">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>
</div>
</div>
<script src="js/login.js"></script>
<script href="js/bootstrap.js"></script>
<!-- /.container -->
</body>
Why does the value not get into html <p> tag.
Your code simply works. But since the function executes on submitting the form, the username gets logged in the console fast before the page refreshed with submitted data. You can confirm this and test it by adding event.preventDefault(); to the function to prevent submitting the form so the page would stay visible with the console.
<script>
var login = new function()
{
var name = null ;
this.validation = function()
{
event.preventDefault();
this.name = document.getElementById("Username").value;
console.log(this.name);
document.getElementById("demo").innerHTML = this.name;
};
};
</script>
If that's not what you're looking for, let me know.
We the javascript validation failed you need to return false. If you don't it will proceed your form further. Thanks
var login = new function()
{
var name = null ;
this.validation = function()
{
this.name = document.getElementById("Username").value;
document.getElementById("demo").innerHTML = this.name;
return false;
};
};

appending a json file with javascript

I have an existing json file (data.json) that I would like to append with information captured from a form.
I have the form outputting json but I am not sure how to go about getting this to add to my existing json file.
So the form looks like this:
<form id="test" action="#" method="post">
<div class="form-group">
<label for="department">Department:</label>
<input class="form-control" type="text" name="department" id="department" />
</div>
<div class="form-group">
<label for="role">Role title:</label>
<input class="form-control" type="text" name="role" id="role" />
</div>
<div class="form-group">
<label for="pay_status">Pay status:</label>
<input class="form-control" type="text" name="pay_status" id="pay_status"/>
</div>
<div class="form-group">
<label for="typicalposts">Typical number of posts in a year:</label>
<input class="form-control" type="text" name="typicalposts" id="typicalposts"/>
</div>
<div class="form-group">
<label for="email">Restrictions:</label>
<input class="form-control" type="text" name="restrictions" id="restrictions" />
</div>
<div class="form-group">
<label for="recruitment_date">Recruitment date:</label>
<input class="form-control" type="date" name="recruitment_date" id="recruitment_date" />
</div>
<div class="form-group">
<label for="weblink">Weblink:</label>
<input class="form-control" type="text" name="weblink" id="weblink" />
</div>
<div class="text-center">
<p>
<input type="submit" value="Send" class="btn btn-primary center_block" />
</p>
</div>
</form>
<pre id="output" ></pre>
And the js I have to turn this data to json is:
(function() {
function toJSONString( form ) {
var obj = {};
var elements = form.querySelectorAll( "input, select, textarea" );
for( var i = 0; i < elements.length; ++i ) {
var element = elements[i];
var name = element.name;
var value = element.value;
if( name ) {
obj[ name ] = value;
}
}
return JSON.stringify( obj );
}
document.addEventListener( "DOMContentLoaded", function() {
var form = document.getElementById( "test" );
var output = document.getElementById( "output" );
form.addEventListener( "submit", function( e ) {
e.preventDefault();
var json = toJSONString( this );
output.innerHTML = json;
}, false);
});
})();
This shows the json in #output for the moment, I would like what is being shown here to be appended to data.json instead
Thanks for your help
My files are hosted on a server that I dont have access too which is why I would like to do this via js
So BBC News have an HTML document at http://www.bbc.co.uk/news. Would it be a good idea if it was possible for my browser to edit the page and save it back to the server?
It is absolutely impossible to do what you want, because it would require that any old browser could edit any old file on any old server.
In order to change data on the server, you have to have the cooperation of the server (which you say you don't have).

Knockout js - two way bind multiple inputs (hard-coded HTML inputs text box) and get JSON array

New to Knockout JS, just wondering how to define viewmodel and bind multiple hard-coded inputs text thanks in advance
UPDATE: Finally worked out the solution
The reason I can't use answer that Rahul provided is the HTML inputs have to be pre-defined/hard-coded. I knew this is against the nature of knockout js, however, the purpose of this data-entry screen is to let patients enter their contact info, so rely on user add contact type on fly is not reliable.
var AddressDetailModel = function (FName, LName) {
var self = this;
self.FName = ko.observable(typeof (FName) != "undefined" ? FName : "");
self.LName = ko.observable(typeof (LName) != "undefined" ? LName : "");
}
var EnrolViewModel = function () {
var self = this;
self.AddressDetails = ko.observable({
"Mother/Carer": new AddressDetailModel(),
"Contact 1": new AddressDetailModel()
});
}
var VM = new EnrolViewModel();
ko.applyBindings(VM);
.JSON {width:95%; margin: 15px auto 15px auto}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<link rel="stylesheet" type="text/css" href="//maxcdn.bootstrapcdn.com/bootswatch/3.2.0/united/bootstrap.min.css">
<fieldset id="CD">
<legend><strong>PERSONAL DETAILS</strong></legend>
<label class="col-sm-2 control-label" for="MFName">First Name</label>
<div class="col-sm-3">
<input type="text" class="form-control" name="MFName" id="MFName" data-bind="value: AddressDetails()['Mother/Carer'].FName"/>
</div>
<label class="col-sm-2 control-label" for="MLName">Last Name</label>
<div class="col-sm-3">
<input type="text" class="form-control" name="MLName" id="MLName" data-bind="value: AddressDetails()['Mother/Carer'].LName"/>
</div>
</fieldset>
<br>
<fieldset id="CT1">
<legend><strong>CONTACTS 1</strong></legend>
<label class="col-sm-2 control-label" for="C1FName">First Name</label>
<div class="col-sm-3">
<input type="text" class="form-control" name="C1FName" id="C1FName" data-bind="value: AddressDetails()['Contact 1'].FName"/>
</div>
<label class="col-sm-2 control-label" for="C1LName">Last Name</label>
<div class="col-sm-3">
<input type="text" class="form-control" name="C1LName" id="C1LName" data-bind="value: AddressDetails()['Contact 1'].LName"/>
</div>
</fieldset>
<br>
<fieldset>
<legend><strong>ViewModel JSON:</strong></legend>
<div class="JSON" data-bind="text: ko.toJSON(VM)"></div>
</fieldset>
Why not start with creating a model for ContactDetailsand pushing data in an observableArray once you click on add.
I assume you want to add Contacts on the fly as well..
Here is the example I have built for you. It is very basic and I encourage you to explore and learn yourself the stuff. I just wanted to give you a heads up :-)
var ContactDetailsModel = function(item){
var self = this;
self.type = ko.observable(typeof(item) != "undefined" ? item.type : "");
self.name = ko.observable(typeof(item) != "undefined" ? item.name : "");
self.phone = ko.observable(typeof(item) != "undefined" ? item.phone : "");
};
var viewModel = function(){
var self = this;
self.ContactDetails = ko.observableArray();
self.contactForm = ko.observable(new ContactDetailsModel());
self.add = function(item){
console.log(ko.toJSON(item))
self.ContactDetails.push(item);
self.contactForm(new ContactDetailsModel());
};
};
ko.applyBindings(new viewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<h4>KO starts here </h4>
<div data-bind="with: contactForm">
<input type="text" data-bind="value : type" placeholder="Enter Type"/>
<input type="text" class="form-control" name="MName" data-bind="value : name" placeholder="Enter Name " />
<input type="text" class="form-control" name="MPhone" data-bind="value : phone" placeholder="Enter Phone"/>
<button data-bind="click:$parent.add">Add</button>
</div>
<h3>Output </h3>
<pre data-bind="text: ko.toJSON(ContactDetails, 4, 2)"></pre>

Categories