Javascript/Form call timing - javascript

I have a form that I use for login purposes here:
<form id = "membershipInfo" method = "post" action = "Login.aspx">
with an submit button that I want to do a post method and a javascript onclick method like this:
<input type ="submit" id = "submitInfo" class = "MemberInfo" value = "Take Me There!" onclick = "authorize(accept, reject)"/>
The method in the onclick is an facebook authorize method that will pull information from the user (access token). I need this token to have the user proceed in my program. The issue is that when this button is clicked the post method will happen before the onclick is finished, which means that the access token will never be passed with the form.
To get the access token to the page load method I use a hidden input in the form:
<input type = "hidden" id = "hiddenAccessToken" name = "accessToken" value = "<%=accessToken %>" />
and in the Facebook method I get the access token like this:
function authorize(successCallback, failureCallback) {
FB.getLoginStatus(function (response) {
if (response.session) {
// logged in and connected user, carry on
session = response.session;
//Set access token
accessToken = response.session.access_token;
//Call success callback
successCallback();
} else {
// no user session available, Lets ask for perms
FB.ui(
{
method: 'permissions.request',
perms: permissionString
},
function (response) {
if (response && response.session != null) {
//Get session
session = response.session;
//Set access token
accessToken = response.session.access_token;
//Call success callback
successCallback();
} else {
//Call failure callback
failureCallback();
}
});
}
});
//Method hit on successCallback
function accept() {
//Add access token to hidden input
$('#hiddenAccessToken').val(accessToken);
}
I don't want to use a time out (and I don't think that will work anyways), but I need to slow the page load down until these js methods are complete. Any suggestions?

Why don't you do the form submit with js, like this:
<input type ="button" id = "submitInfo" class = "MemberInfo" value = "Take Me There!" onclick = "authorize(accept, reject)"/>
And then:
function accept() {
//Add access token to hidden input
$('#hiddenAccessToken').val(accessToken);
$('#membershipInfo').submit();
}

Related

JavaScript form fetch request submitting empty values when they are filled in

I am creating a Login form in JavaScript and am trying to send this form to my Ruby on Rails backend.
I have two problems:
When the form loads, it triggers the eventListener and sends the blank form's data to the backend. I have e.preventDefault() commented out in the second function below because I kept getting the error that it is not a function. Note: in the backend for Rails, I get the following message when I type in params. "Permitted: false" concerns me.
<ActionController::Parameters {"controller"=>"sessions", "action"=>"create", "session"=>{}} permitted: false>
When I fill in the form with an email and password and click the submit button, the loginData (from loginButton.addEventListener("submit", submitLogin(loginData) submits a blank value for the email and 'password' for the password (which are the default values I set to test the values) even though these elements are filled in in the form with an actual email address and password.
Function loading login form (note: this loads just fine):
// create the elements
var div = document.createElement("div"),
log = document.createElement("div"),
loginForm = document.createElement("form"),
//set form attributes
loginForm.setAttribute("method", "POST");
// set body styles
document.body.style.textTransform = "capitalize";
log.id = "login";
log.innerHTML = "login";
// set loginForm styles
loginForm.id = "loginForm";
// set the elements and styles on the form
loginForm.innerHTML =
"<label>username</label><br/>" +
"<input type='text' id='login-email' value='' placeholder='email' style='" +
inputStyles +
"' /><br/>" +
"<label>password</label><br/>" +
"<input type='password' id='login-password' value='value' placeholder='*************' style='" +
inputStyles +
"' /><br/>" +
"<input type='submit' id='login-button' value='Login' style='" +
btnStyles +
"' />" +
"<p><a style='" +
forgetStyles +
"' href='#'>forget password ?</a></p><br/>";
// append the buttons and form on main-div
div.appendChild(log);
div.appendChild(loginForm);
// append main-div on the body
document.body.appendChild(div);
//get login form values to submit
let loginEmail = document.getElementById("login-email").value;
let loginPassword = document.getElementById("login-password").value;
let loginData = { member: { loginEmail, loginPassword } };
}
SubmitLogin Function (fetch request to Rails backend):
async function submitLogin(e) {
// e.preventDefault();
const loginData = new FormData(e.target);
debugger;
let options = {
method: "POST",
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin":
"file:///Users/awb/Coding/Flatiron/Projects/bookclub-javascript-rails-api/bookclub-frontend-javascript/index.html",
"Access-Control-Allow-Methods": "POST",
"Access-Control-Allow-Headers": "Content-Type, Authorization",
Accept: "application/json",
},
body: JSON.stringify(loginData),
};
fetch("http://localhost:3000/login", options)
.then((resp) => {
resp.json();
})
.then((member) => {
console.log(member);
return new Member(member);
});
}
If it matters, this is the order of my scripts at the bottom of my index.html page:
<script src="src/suggestion.js"></script>
<script src="src/member.js"></script>
<script src="src/gathering.js"></script>
<script src="src/book_group.js"></script>
<script src="src/book.js"></script>
<script src="src/login_registration_form.js"></script>
<script src="src/index.js"></script>
Where index.js calls the function "loadRegistrationLogin()" as the last line on index.js
My routes are:
Rails.application.routes.draw do
resources :members, only [:create, :index, :show]
resources :registrations, only: [:create]
resources :sessions, only: [:create]
post '/login', to: 'sessions#create'
delete '/logout', to: 'sessions#destroy'
get '/logged_in', to: 'sessions#is_logged_in?'
root to: "static#home"
end
and finally, my session controller:
class SessionsController < ApplicationController
include CurrentMemberConcern
skip_before_action :verify_authenticity_token
def create
member = Member
.find_by(email: params[:member][:email])
.try(:authenticate, params[:member][:password])
if member
login!
render json: {
status: :created,
logged_in: true,
member: member
}
else
render json: {
status: 401,
errors: ['No such member', 'Verify credentials and try again or sign up']
}
end
end
end
Your form is submitting automatically because of the way you've set the event handler. The .addEventListener() API requires a reference to a callback function as the second argument. But you passed a function call with arguments like this:
loginButton.addEventListener("submit", submitLogin(loginData));
You have two choices to fix this:
Pass a reference.
This will require loginData to be available to the handler in some other fashion.
loginButton.addEventListener("submit", submitLogin);
Enclose the call within a function expression:
loginButton.addEventListener("submit", ()=>submitLogin(loginData));
Option 2 is generally preferred when needing to pass parameters to the handler. But you'll see below, for you, option one is the way to go.
The leads to the next problem - submitLogin() function itself.
async function submitLogin(e) {
// e.preventDefault();
const loginData = new FormData(e.target);
In your eventListener setup you went out of your way to pass a parameter to this function - yet it isn't used. You correctly attempt to create a new FormData() Object. But interestingly enough you pass e.target, which is the button element. That won't work Reference.
To fix that change:
const loginData = new FormData(e.target);
to: (assuming only one form on the page)
const loginData = new FormData(document.forms[0]);
But we aren't done yet. Using e.preventDefault() is unnecessary if you set up your button correctly. A button with type="submit" is designed to submit the form to the server. That's also true for a button with no type attribute at all. You clearly don't want to submit the form, so don't put a submit button on it.
Change the login button from:
type="submit"
To:
type="button"
Now you can remove the e parameter from your function.
All of that should get you a lot closer to success.

2checkout - Cannot generate token with sandbox mode

I am using 2checkout payment gateway in my PHP project and facing issue in generating token with 'sandbox' mode. Here's the code:
<form action="" method="post" id="frmSignup" name="frmSignup">
<input type="hidden" value="" name="token" id="token" />
<script type="text/javascript" src="https://www.2checkout.com/checkout/api/2co.min.js"></script>
<script>
// Called when token created successfully.
var successCallback = function(data) {
console.log("success::"+data);
var myForm = document.getElementById('frmSignup');
// Set the token as the value for the token input
myForm.token.value = data.response.token.token;
// IMPORTANT: Here we call `submit()` on the form element directly instead of using jQuery to prevent and infinite token request loop.
myForm.submit();
};
// Called when token creation fails.
var errorCallback = function(data) {
// Retry the token request if ajax call fails
console.log("error::"+data.errorMsg);
if (data.errorCode === 200) {
// This error code indicates that the ajax call failed. We recommend that you retry the token request.
} else {
alert(data.errorMsg);
}
};
var tokenRequest = function() {
// Setup token request arguments
var args = {
sellerId: "<?php echo accountnumber_checkout; ?>",
publishableKey: "<?php echo publickey_checkout; ?>",
ccNo: $("#creditcardno").val(), //input field
cvv: $("#billingcvv").val(), //input field
expMonth: $("#expiryMonth").val(), //input field
expYear: $("#expiryYear").val() //input field
};
// Make the token request
TCO.requestToken(successCallback, errorCallback, args);
};
$(function() {
// Pull in the public encryption key for our environment
TCO.loadPubKey('sandbox');
$("#frmSignup").submit(function(e) {
// Call our token request function
tokenRequest();
// Prevent form from submitting
return false;
});
});
</script>
</form>
Facing this error: ( in 2co.min.js 2:1628 )
Uncaught ReferenceError: tokenRequestUrl is not defined
If I use 'production' in TCO.loadPubKey('production') instead of 'sandbox', then the token is generated. But, in production mode, I am not able to use test cards:
Test Payment Methods

Challenge with javascript/jquery $.get and python backend

I have an HTML form to enter a username/password to register for a site. I am attempting to implement a javascript/JQuery $.get to send an HTTP GET to check if the username is already in use. On the server side, the "username" value (pulled by request.form.get()) is coming back as None. The javascript source does not seem responsive either on the HTML page.
Javascript as below:
var username = document.getElementById("username");
var inputForm = document.getElementById("form");
document.getElementById("submit").addEventListener("click", function(event) {
event.preventDefault()
});
inputForm.onclick = function(data) {
$.get("/check?username=" + username.value, function() {
alert("CHECKING")
if (data == false) {
inputForm.submit();
}
else {
alert("Sorry - that username is taken!");
}
});
};
Python (Flask) on backend as follows:
#app.route("/check", methods=["GET"])
def check():
"""Return true if username available, else false, in JSON format"""
print("***RUNNING CHECK***")
# get username from web form
username = request.form.get("username")
print(username)
# check that username is longer than 1, then pull list from DB to check against
if len(username) > 1:
usernames = db.execute("SELECT username FROM users")
# return false if username is available
if username in usernames:
return jsonify(False)
# return true if username is NOT available
else:
return jsonify(True)
This is what comes back:
TypeError: object of type 'NoneType' has no len()
INFO:werkzeug:192.168.179.21 - - [22/Aug/2019 16:20:41] "GET /check?username=ajd HTTP/1.0" 500 -
The client side issue that I can see are...
Checking the Javascript console will probably tell you that data is undefined. If should be added as a parameter to the callback function.
You need to escape the get paramters with encodeURIComponenet.
You probably want to run this code when the input is received than when the form is clicked on.
username.oninput = function(data) {
$.get("/check?username=" + encodeURIComponenet(username.value), function(data) {
alert("CHECKING")
if (data == false) {
inputForm.submit();
} else {
alert("Sorry - that username is taken!");
}
});
};

I am using Firebase authentication for my application. There seems to be an infinite loop where the page keeps being redirected over and over again

The following function is to redirect the user to "Select Stream.html" when the user logs in.It keeps on replacing the location over and over again.
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
window.location.replace("Select Stream.html");
// User is signed in.
} else {
// No user is signed in.
window.location.replace("index.html");
}
});
I am new to coding.
Here is the Log in Function
function login()
{
var userEmail=document.getElementById("email-field").value;
var userPassword=document.getElementById("password-field").value;
firebase.auth().signInWithEmailAndPassword(userEmail, userPassword).catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
/* window.location='Select Stream.html'; */
window.alert("Error : " + errorMessage);
// ...
});
}
The Login function will be triggered when the signin-button is clicked.
<input onclick="login()" type="submit" name="signin-button" value="Sign In"/>
First, change the name of Select Stream.html to have no spaces or capital letters. This is common practice, and I recommend changing the name of the file to select_stream.html.
Before opening select_stream.html or index.html, check whether the user is already on that page to prevent the page from refreshing, like this:
if (user) {
// User is signed in.
if(window.location.href.indexOf("select_stream.html") == -1){
window.location.replace("select_stream.html");
}
} else {
// No user is signed in.
if(window.location.href.indexOf("index.html") == -1){
window.location.replace("index.html");
}
}
The window.location.href variable refers to the URL of the current page, and the .indexOf function allows you to check if a value is contained inside the URL. The .indexOf function returns -1 if the specified value could not be found within the string, so this code simply only redirects if the user is not already on the redirect page.

C# Razor View passing null object to JavaScript

Here's the rundown. Users can view a razor page both anonymously and logged in. If they are logged in, they get certain features. In my controller, I have a boolean isAnonymous which I set to true or false depending on if there's a signed in user or not. I pass isAnonymous to my view model which gets sent to the razor page.
In the razor page, I have a javascript script tag which needs to retrieve that boolean value and, if isAnonymous is false (meaning someone is signed in), fire off one of two ajax calls to the server.
The first thing I do in my script tag is get the isAnonymous value and convert it to a JavaScript boolean with this:
var isAnonymous = #Json.Encode(Model.IsAnonymous);
after console logging, this appears to return correctly.
Then i put in my if statement. The summary here is if the user is not logged in, none of these functions nested inside the if statement should fire, because they take an ApplicationUser as part of the model. If there is no signed in user, Model.User is null and throws a Null Reference Exception. I thought putting my ajax calls inside the if statement would guard against the exception, but the the logic seems to be blowing right through the if (isAnonymous == false) and hitting those functions despite the logic. Any thoughts as to why this is happening? When isAnonymous is true, I can't have the functions fire.
if (isAnonymous == false) {
if ($('.bookmark-btn').hasClass('bookmark-story-btn')) {
addBookmark();
} else {
removeBookmark();
}
function addBookmark() {
//bookmark a story btn click event
$('.bookmark-story-btn').on('click', function () {
var storyid;
//the storyid should come as a string -
//try to parse it as an int for the controller
if (!isNaN($(this).attr('storyid'))) {
storyid = parseInt($(this).attr('storyid'))
//verify successful conversion from string to int before ajax call
if (typeof (storyid) == 'number') {
var userid = $(this).attr('userId')
var newBookmark = {
UserId: userid,
StoryId: storyid,
};
$.ajax({
url: "/api/bookmark/new",
method: "POST",
data: newBookmark,
success: function (data) {
//remove the save bookmark btn and dynamically add
//the remove bookmark btn so
//the page doesn't require a refresh
$('.bookmark-story-btn').remove();
$('.bookmark-btn-group').append("<button bookmarkId='"
+ data.Id
+ "' userId=#Model.User.Id storyId=#Model.StoryId"
+" class='btn remove-bookmark-btn bookmark-btn'>"
+"<i class='fas fa-2x fa-bookmark'></i></button>")
removeBookmark();
},
error: function (error) {
$('.page-alert').css('visibility', 'visible')
.html("Whoops. Something went wrong."
+" Adding the bookmark failed.")
//automatically close the alert-danger div
//after 2 seconds
setTimeout(function () {
$('.page-alert').css('visibility', 'hidden')
}, 3000);
}
});
}
}
});
}
function removeBookmark() {
//remove a bookmark click event
$('.remove-bookmark-btn').on('click', function () {
if (!isNaN($(this).attr('bookmarkid'))) {
bookmarkid = parseInt($(this).attr('bookmarkid'))
//verify successful conversion from string to int before ajax call
if (typeof (bookmarkid) == 'number') {
//call the ajax
$.ajax({
url: "/api/bookmark/" + bookmarkid,
method: "DELETE",
success: function (data) {
//show-hide the appropriate icons
$('.remove-bookmark-btn').remove();
$('.bookmark-btn-group').append("<button userId=#Model.User.Id"
+" storyId=#Model.StoryId class='btn bookmark-story-btn"
+" bookmark-btn'><i class='far fa-2x fa-bookmark'>"
+"</i></button>")
addBookmark();
},
error: function (error) {
$('.page-alert').css('visibility', 'visible')
.html("Whoops. Something went wrong here."
+" Removing the bookmark didn't work.")
//automatically close the alert-danger div
//after 2 seconds
setTimeout(function () {
$('.page-alert').css('visibility', 'hidden')
}, 3000);
}
})
}
}
})
}
}
You can use Request.IsAuthenticated in both the Razor view:
#if(Request.IsAuthenticated)
{
<script>
' your authenticated client side script here
</script>
}
And then check again server side when posting in your controller for example:
public ActionResult Index()
{
if(Request.IsAuthenticated)
{
//server logic here
}
}
Better still if you decorate the method with the AuthoriseAttribute the user will get an 403 Unauthorized.
You can then do something similar server side for the UserId:
[Authorize]
public ActionResult Index()
{
var userId = User.Identity.Name;
}
Then you don't even need to pass the UserId about. This is all based on using the common Identity practices:
https://learn.microsoft.com/en-us/aspnet/identity/overview/getting-started/introduction-to-aspnet-identity

Categories