I am building a Wordpress site, and using AJAX to load in subsequent pages when the user navigates, so that page transitions are nicely animated.
By default, if you load and inject a page with an embedded Ninja Form, the form simply does not display. There is very little information out there on how to achieve this. I hoped there would be an official out of the box way to get this working, but there doesn't appear to be.
What steps need to be taken in order to get the form to display on the page, when the form has been dynamically loaded with AJAX?
I have experimented with some completely undocumented code samples, and managed to figure it out, along with a few necessary tweaks and additions. I thought I'd share here in case anyone else has difficulty with this.
Step 1. Enqueue necessary JS and CSS
Add the following to your functions.php, because Ninja Forms relies on backbone js, and needs css, which won't be loaded if your initial landing page does not already have a form on it.
add_action('wp_enqueue_scripts', function () {
// enqueue ninja forms css, including for any ninja forms addons
wp_enqueue_style('nf-display', content_url('/plugins/ninja-forms/assets/css/display-structure.css'), ['dashicons'], core_dev_version(''));
wp_enqueue_style('nf-layout-front-end', content_url('/plugins/ninja-forms-style/layouts/assets/css/display-structure.css'), ['nf-display'], core_dev_version(''));
// make sure that backbone is enqueued on the page, as ninja forms relies on this
wp_enqueue_script('backbone');
}, 100);
.
Step 2. Add an AJAX function that returns the form data
You shouldn't need to edit this, just add it to your functions.php. The javascript we use in step 3 will make its own AJAX call to request some form data in a very specific format.
add_action('init', function () {
// if this is not an AJAX form request, return
if (! isset($_REQUEST[ 'async_form' ])) {
return;
}
// clear default loaded scripts.
global $wp_scripts;
unset($wp_scripts->registered);
// get the requested form id
$form_id = absint($_REQUEST['async_form']);
// retrieve the requested form
ob_start();
$form = do_shortcode("[ninja_forms id='{$form_id}']");
ob_get_clean();
// output the requested form on the page
ob_start();
NF_Display_Render::output_templates();
$templates = ob_get_clean();
$response = [
'form' => $form,
'scripts' => $wp_scripts->registered,
'templates' => $templates
];
echo json_encode($response);
// die, because we don't want anything else to be returned
die();
});
.
Step 3. Add JS helper functions to your landing page
This simply adds some helpful JS code into your site.
You can add this to functions.php as is, or include it in a separate JS file.
This is what we will use to load and initialise the form we want.
add_action('wp_footer', function () {
// adds a script to the footer
?>
<script type="text/javascript">
var NinjaFormsAsyncForm = function(formID, targetContainer) {
this.formID = formID;
this.targetContainer = targetContainer;
this.formHTML;
this.formTemplates;
this.formScripts;
this.fetch = function(callback) {
jQuery.post('/', { async_form: this.formID }, this.fetchHandler.bind(this))
.then(callback);
}
this.fetchHandler = function(response) {
response = JSON.parse( response );
window.nfFrontEnd = window.nfFrontEnd || response.nfFrontEnd;
window.nfi18n = window.nfi18n || response.nfi18n || {};
this.formHTML = response.form;
this.formTemplates = response.templates;
this.formScripts = response.scripts;
}
this.load = function() {
this.loadFormHTML(this.formHTML, this.targetContainer);
this.loadTemplates(this.formTemplates);
this.loadScripts(this.formScripts);
}
this.loadFormHTML = function(form, targetContainer) {
jQuery(targetContainer).append( form );
}
this.loadTemplates = function(templates) {
document.body.innerHTML += templates;
}
this.loadScripts = function(scripts) {
jQuery.each( scripts, function( nfScript ){
var script = document.createElement('script');
// note that eval() can be dangerous to use - do your research
eval( scripts[nfScript].extra.data );
window.nfFrontEnd = window.nfFrontEnd || nfFrontEnd;
script.setAttribute('src',scripts[nfScript].src);
var appendChild = document.head.appendChild(script);
});
}
this.remove = function() {
jQuery(this.targetContainer).empty();
}
}
</script>
<?php
}, 100);
.
Step 4. Initialise the form after you AJAX in your page
I can't tell you how to AJAX in your pages - that's a whole other topic for you to figure out.
But, once you have loaded your page content with included Ninja Form, and once that has successfully been on the page, now you need to initialise the form.
This uses the javascript helper (step 3), which in turn calls the php helper (step 2), and finally displays the form on your page!
You'll need to know the ID of the form that's been injected. My tactic was to include this as a data-attribute in my page markup.
var form_id = 1;
var asyncForm = new NinjaFormsAsyncForm(form_id, '.ninja-forms-dynamic');
asyncForm.fetch(function () {
asyncForm.load();
});
And that's about all there is to it!
Hopefully this may save others the time and effort of figuring all of this out.
Related
NOTE: There are a lot of details here, so if anyone needs a condensed version of this, I'm happy to summarize.
I am trying to run a function in my php file, that will in turn, update a template variable. As an example, here is one such function:
function get_vehicle_makes()
{
$sql = 'SELECT DISTINCT make FROM phpbb_vehicles
WHERE year = ' . $select_vehicle_year;
$result = $db->sql_query($sql);
while($row = $db->sql_fetchrow($result))
{
$template->assign_block_vars('vehicle_makes', array(
'MAKE' => $row['make'],
));
}
$db->sql_freeresult($result);
}
I know that this function works. I am trying to access this function in my Javascript with:
function updateMakes(pageLoaded) {
var yearSelect = document.getElementById("vehicle_year");
var makeSelect = document.getElementById("vehicle_make");
var modelSelect = document.getElementById("vehicle_model");
$('#vehicle_make').html('');
$.ajax({ url: '/posting.php',
data: {action: 'get_vehicle_makes'},
type: 'post',
success:function(result)//we got the response
{
alert(result);
},
error:function(exception){alert('Exception:'+exception);}
});
<!-- BEGIN vehicle_makes -->
var option = document.createElement("option");
option.text = ('{vehicle_makes.MAKE}');
makeSelect.add(option);
<!-- END vehicle_makes -->
if(pageLoaded){
makeSelect.value='{VEHICLE_MAKE}{DRAFT_VEHICLE_MAKE}';
updateModels(true);
}else{
makeSelect.selectedIndex = -1;
updateModels(false);
}
}
The section in my javascript...
<!-- BEGIN vehicle_makes -->
var option = document.createElement("option");
option.text = ('{vehicle_makes.MAKE}');
makeSelect.add(option);
<!-- END vehicle_makes -->
... is a block loop and will loop through the block variable, vehicle_makes, set in the PHP function. This works upon loading the page because the page that loads, is the new.php that I'm trying to do an Ajax call to, and all of the PHP runs in that file upon loading. However, I need the function to run again, to update that block variable, since it will change based on a selection change in the HTML. I don't know if this type of block loop is common. I'm learning about them since they are used with a forum I've installed on my site, phpBB. (I've looked in their support forums for help on this.). I think another possible solution would be to return an array, but I would like to stick to the block variable if possible for the sake of consistency.
This is the bit of code in the php that reads the $_POST, and call the php function:
if(isset($_POST['action']) && !empty($_POST['action'])) {
$action = $_POST['action'];
//Get vehicle vars - $select_vehicle_model is used right now, but what the heck.
$select_vehicle_year = utf8_normalize_nfc(request_var('vehicle_year', '', true));
$select_vehicle_make = utf8_normalize_nfc(request_var('vehicle_make', '', true));
$select_vehicle_model = utf8_normalize_nfc(request_var('vehicle_model', '', true));
switch($action) {
case 'get_vehicle_makes' :
get_vehicle_makes();
break;
case 'get_vehicle_models' :
get_vehicle_models();
break;
// ...etc...
}
}
And this is the javascript to run the Ajax:
function updateMakes(pageLoaded) {
var yearSelect = document.getElementById("vehicle_year");
var makeSelect = document.getElementById("vehicle_make");
var modelSelect = document.getElementById("vehicle_model");
$('#vehicle_make').html('');
$.ajax({ url: '/posting.php',
data: {action: 'get_vehicle_makes'},
type: 'post',
success:function(result)//we got the response
{
alert(result);
},
error:function(exception){alert('Exception:'+exception);}
});
<!-- BEGIN vehicle_makes -->
var option = document.createElement("option");
option.text = ('{vehicle_makes.MAKE}');
makeSelect.add(option);
<!-- END vehicle_makes -->
if(pageLoaded){
makeSelect.value='{VEHICLE_MAKE}{DRAFT_VEHICLE_MAKE}';
updateModels(true);
}else{
makeSelect.selectedIndex = -1;
updateModels(false);
}
}
The javascript will run, and the ajax will be successful. I've checked the network tab and console tab, and have done multiple tests to confirm that. It appears that the block variable is not being set. Is what I'm trying to do even possible? I have a feeling that to get this answer, we'll need to know more about phpBB's template engine, and how it works with these template variable. Also, just to clarify, I think the term 'template variable' is specific to phpBB. It's the term they use for variables set in PHP, to be accessed by the HTML, and javascript files. This works through a phpBB class called 'template', and a function called 'assign_block_vars'. I don't know exactly how that work.
If anyone has done this for phpBB, or has any ideas, I would appreciate it.
Think I found the problem. At the beginning of my PHP, I have an include statement to include the PHP file containing the class for connecting to the database. In the statement $result = $db->sql_query($sql);, $db is set in this other PHP file. I don't entirely understand, but because of that, $db was outside of the scope of my function get_vehicle_makes(). I had to create a class inside my PHP file, and pass $db as a parameter to the function using:
class vehicle {
public function __construct($db)
{
$this->db = $db;
}
function get_vehicle_makes()
{
$sql = 'SELECT make FROM phpbb_vehicles
WHERE year = ' . $select_vehicle_year;
$result = $this->db->sql_query($sql);
Hope this helps.
My current code is:
Play.save = function() {
//Hide the buttons
this.printButton.visible = false;
this.saveButton.visible = false;
this.backButton.visible = false;
//Force the game to re-render.
this.game.cameras.render(); //generally not recommended if you can help it
//Get the canvas information
var img = this.game.stage.canvas.toDataURL("image/octet-stream");
this.saveajax(img);
//Show UI again.
this.printButton.visible = false;
this.saveButton.visible = true;
this.backButton.visible = true;
}
Play.saveajax = function(img){
$.ajax
({
url: "http://localhost/ourthing/character/save.php",
type: "POST",
cache: false,
data: {
img: img
}
});
}
The file 'save.php' works (when i simply open the file). It will execute a query which it has to do. Problem here is: with this script i want to update a user with the given post data (img). But it doesnt execute on this request.
(i create data for var img and send this data to the saveajax function, which will open save.php to execute the query).
Im very new to JS/ajax. Does anyone can help me?
Best regards
apparently I cant comment, so my question is what are you getting on save.php
with command like
error_log(print_r($_REQUEST,true));
I suspect JSON issue here. can we see save.php?
Ok didn't see your previous comment, scrap that - your Jquery is not included.
Answer:
I had to include jQuery:
<script src="assets/js/jquery.js" ></script >
Thanks #Don'tVoteMeDown and #Lixus
Save.php file:
$db = framework::getDBO();
$data = array(
'canvas_character' => $_POST['img'],
);
$db->where('id', 1);
$db->update('ot_users', $data);
(i use my own framework)
I have implemented these two plugins:
contact form 7, and
contact form 7 success page redirect.
These plugins been conflicts.
Firstly, the client side validation is not working.
Secondly, on success it is not redirecting to success page.
If you just want to redirect users upon successful message then better approach would be to use javascript hook.
Remove the success page redirect plugin.
on_sent_ok: "location = 'http://example.com/';"
just go to Additional Settings tab in contact form 7 and paste this without any leading spaces, replace your success page url with the example.com.
To work on client side validation, please put these lines in footer.php
<script type='text/javascript' src='/wp-content/plugins/contact-form-7/includes/js/scripts.js?ver=3.5.2'></script>
<script type='text/javascript' src='/wp-content/plugins/contact-form-7/includes/js/jquery.form.min.js?ver=3.40.0-2013.08.13'></script>
<script type='text/javascript'>
please copy syntax at your own this is cdata .>var _wpcf7 = {"loaderUrl":"/wp-content/plugins/contact-form-7/images/ajax-loader.gif","sending":"Sending ..."};
]]>
now in your cf7-success-page-redirects.php that is in plugin directory .
change this function
function cf7_success_page_form_submitted( $contact_form ) {
$contact_form_id = $contact_form->id();
// Send us to a success page, if there is one
$success_page = get_post_meta( $contact_form_id, '_cf7_success_page_key', true );
if ( !empty($success_page) ) {
$items = array();
$items['mailSent'] = true;
$items['redirectLink'] =get_permalink( $success_page );
echo wp_json_encode( $items );
die();
}
}
now in your contact form 7 directory of plugin find js folder , and look for script.php and replace this bunch of code.
else if (1 == data.mailSent) {
$responseOutput.addClass('wpcf7-mail-sent-ok');
$form.addClass('sent');
if (data.onSentOk) {
$.each(data.onSentOk, function(i, n) { eval(n) });
}
$(data.into).trigger('wpcf7:mailsent');
$(data.into).trigger('mailsent.wpcf7'); // deprecated
location.assign(data.redirectLink);
}
i have fixed this on my website for client hope this will help someone in future.
Use below code in functions.php (located in themes -> themeName Folder).
Put this in the end of file.
add_action( 'wp_footer', 'mycustom_wp_footer' );
function mycustom_wp_footer() {
?>
<script type="text/javascript">
document.addEventListener( 'wpcf7mailsent', function( e ) {
var str = window.location.href;
if( str.includes("flp") ){
window.location.href = "http://www.WebsiteName.com/facebook-thank-you";
} else if( str.includes("glp") ){
window.location.href = "http://www.WebsiteName.com/google-thank-you";
}
}, false );
</script>
<?php
}
Working Perfectly..
I have a javascript variable called "list". I need to send it as a POST data to another page and open that page in a new tab (with the POST data present).
This code:
jQuery.post('datadestination.php', list);
sends the data all right, but ofcourse it opens the page in the same tab.
I saw some solutions to similar problems using invisible form and things like that, but I could not get them to work. Is there any simple solution?
You can send a form using the target="_blank" attribute.
<form action="datadestination.php" method="POST" target="_blank" id="myform">
<input type="hidden" name="list" id="list-data"/>
<input type="submit" value="Submit">
</form>
Then in JS:
jQuery('#list-data').val(list);
jQuery('#myform').submit();
This is an implementation of Sergey's solution.
<?php // this is save.php
session_start();
// DO NOT just copy from _POST to _SESSION,
// as it could allow a malicious user to override security.
// Use a disposable variable key, such as "data" here.
// So even if someone passed _POST[isAdmin]=true, all that he would do
// is populate _SESSION[data][isAuthenticated], which nobody reads,
// not the all-important _SESSION[isAuthenticated] key.
if (array_key_exists('data', $_POST)) {
$_SESSION['data'] = $_POST['data'];
$_SESSION['data.timestamp'] = time();
// Let us let the client know what happened
$msg = 'OK';
} else {
$msg = 'No data was supplied';
}
Header('Content-Type: application/json; charset=utf8');
die(json_encode(array('status' => $msg)));
?>
In the first page:
$.post('save.php', { data: list }, function(response){
if (!response.status) {
alert("Error calling save");
return;
}
if (response.status !== 'OK') {
alert(response.status);
return;
}
// We had a response and it was "OK". We're good.
window.open('datadestination.php');
});
And in datadestination.php add the fix:
if (!array_key_exists('data', $_SESSION)) {
die("Problems? Did you perchance attempt to reload the page and resubmit?");
// For if he did, then yes, $_SESSION would have been cleared.
// Same if he is operating on more than one window or browser tab.
}
// Do something to validate data. For example we can use data.timestamp
// to assure data isn't stale.
$age = time();
if (array_key_exists($ts = 'data.timestamp', $_SESSION)) {
$age -= $_SESSION[$ts];
}
if ($age > 3600) {
die("Data is more than one hour old. Did someone change server time?!?");
// I actually had ${PFY} do that to me using NTP + --hctosys, once.
// My own time zone is (most of the year) exactly one hour past GMT.
}
// This is safe (we move unsecurity-ward):
$_POST = $_SESSION['data'];
unset($_SESSION['data'], $_SESSION['data.timestamp']);
// keep things clean.
// From here on, the script behaves "as if" it got a _POST.
Update
You can actually merge save.php and datadestination.php and use a "saving stub" savepost.php that you can recycle in other pages:
<?php
session_start();
// DO NOT just copy from _POST to _SESSION,
// as it could allow a malicious user to override security.
// Use a disposable variable key, such as "data" here.
if (array_key_exists('data', $_POST)) {
// Timestamp sent by AJAX
if (array_key_exists('ts', $_POST)) {
// TODO: verify ts, but beware of time zones!
$_SESSION['data'] = $_POST['data'];
Header("Content-Type: application/json;charset=UTF-8");
die(json_encode(array('status' => 'OK')));
}
die("Error");
}
// This is safe (we move unsecurity-ward):
$_POST = $_SESSION['data'];
unset($_SESSION['data']); // keep things clean.
?>
Now your call becomes
$.post('datadestination.php', { data: list, ts: Date.now() }, function(){
window.open('datadestination.php');
});
and in your datadestination.php (or anywhere else) you add
require 'savepost.php';
I suggest:
Pass that list with the jquery.post() function and save it in the SESSION array.
Open a new tab with the same file/address/URL with the window.open() function.
Retrieve saved data from the SESSION array.
This seems straightforward and clean to me.
I have a web page that allows users to complete quizzes. These quizzes use JavaScript to populate original questions each time it is run.
Disclaimer: JS Noob alert.
After the questions are completed, the user is given a final score via this function:
function CheckFinished(){
var FB = '';
var AllDone = true;
for (var QNum=0; QNum<State.length; QNum++){
if (State[QNum] != null){
if (State[QNum][0] < 0){
AllDone = false;
}
}
}
if (AllDone == true){
//Report final score and submit if necessary
NewScore();
CalculateOverallScore();
CalculateGrade();
FB = YourScoreIs + ' ' + RealScore + '%. (' + Grade + ')';
if (ShowCorrectFirstTime == true){
var CFT = 0;
for (QNum=0; QNum<State.length; QNum++){
if (State[QNum] != null){
if (State[QNum][0] >= 1){
CFT++;
}
}
}
FB += '<br />' + CorrectFirstTime + ' ' + CFT + '/' + QsToShow;
}
All the Javascript here is pre-coded so I am trying my best to hack it. I am however struggling to work out how to pass the variable RealScore to a MySql database via PHP.
There are similar questions here on stackoverflow but none seem to help me.
By the looks of it AJAX seems to hold the answer, but how do I implement this into my JS code?
RealScore is only given a value after the quiz is complete, so my question is how do I go about posting this value to php, and beyond to update a field for a particular user in my database on completion of the quiz?
Thank you in advance for any help, and if you require any more info just let me know!
Storing data using AJAX (without JQuery)
What you are trying to do can pose a series of security vulnerabilities, it is important that you research ways to control and catch these if you care about your web application's security. These security flaws are outside the scope of this tutorial.
Requirements:
You will need your MySQL database table to have the fields "username" and "score"
What we are doing is writing two scripts, one in PHP and one in JavaScript (JS). The JS script will define a function that you can use to call the PHP script dynamically, and then react according to it's response.
The PHP script simply attempts to insert data into the database via $_POST.
To send the data to the database via AJAX, you need to call the Ajax() function, and the following is the usage of the funciton:
// JavaScript variable declarations
myUsername = "ReeceComo123";
myScriptLocation = "scripts/ajax.php";
myOutputLocation = getElementById("htmlObject");
// Call the function
Ajax(myOutputLocation, myScriptLocation, myUsername, RealScore);
So, without further ado...
JavaScript file:
/**
* outputLocation - any HTML object that can hold innerHTML (span, div, p)
* PHPScript - the URL of the PHP Ajax script
* username & score - the respective variables
*/
function Ajax(outputLocation, PHPScript, username, score) {
// Define AJAX Request
var ajaxReq = new XMLHttpRequest();
// Define how AJAX handles the response
ajaxReq.onreadystatechange=function(){
if (ajaxReq.readyState==4 && xml.status==200) {
// Send the response to the object outputLocation
document.getElementById(outputLocation).innerHTML = ajaxReq.responseText;
}
};
// Send Data to PHP script
ajaxReq.open("POST",PHPScript,true);
ajaxReq.setRequestHeader("Content-type","application/x-www-form-urlencoded");
ajaxReq.send("username="username);
ajaxReq.send("score="score);
}
PHP file (you will need to fill in the MYSQL login data):
<?php
// MYSQL login data
DEFINE(MYSQL_host, 'localhost');
DEFINE(MYSQL_db, 'myDatabase');
DEFINE(MYSQL_user, 'mySQLuser');
DEFINE(MYSQL_pass, 'password123');
// If data in ajax request exists
if(isset($_POST["username"]) && isset($_POST["score"])) {
// Set data
$myUsername = $_POST["username"];
$myScore = intval($_POST["score"]);
} else
// Or else kill the script
die('Invalid AJAX request.');
// Set up the MySQL connection
$con = mysqli_connect(MYSQL_host,MYSQL_user,MYSQL_pass,MYSQL_db);
// Kill the page if no connection could be made
if (!$con) die('Could not connect: ' . mysqli_error($con));
// Prepare the SQL Query
$sql_query="INSERT INTO ".TABLE_NAME." (username, score)";
$sql_query.="VALUES ($myUsername, $myScore);";
// Run the Query
if(mysqli_query($con,$sql))
echo "Score Saved!"; // Return 0 if true
else
echo "Error Saving Score!"; // Return 1 if false
mysqli_close($con);
?>
I use these function for ajax without JQuery its just a javascript function doesnt work in IE6 or below. call this function with the right parameters and it should work.
//div = the div id where feedback will be displayed via echo.
//url = the location of your php script
//score = your score.
function Ajax(div, URL, score){
var xml = new XMLHttpRequest(); //sets xmlrequest
xml.onreadystatechange=function(){
if (xml.readyState==4 && xml.status==200){
document.getElementById(div).innerHTML=xml.responseText;//sets div
}
};
xml.open("POST",URL,true); //sets php url
xml.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xml.send("score="score); //sends data via post
}
//Your PHP-script needs this.
$score = $_POST["score"]; //obtains score from POST.
//save your score here
echo "score saved"; //this will be displayed in the div set for feedback.
so call the javascript function with the right inputs, a div id, the url to your php script and the score. Then it will send the data to the back end, and you can send back some feedback to the user via echo.
Call simple a Script with the parameter score.
"savescore.php?score=" + RealScore
in PHP Side you save it
$score = isset ($_GET['score']) ? (int)$_GET['score'] : 0;
$db->Query('INSERT INTO ... ' . $score . ' ...');
You could call the URL via Ajax or hidden Iframe.
Example for Ajax
var request = $.ajax({
url: "/savescore.php?score=" + RealScore,
type: "GET"
});
request.done(function(msg) {
alert("Save successfull");
});
request.fail(function(jqXHR, textStatus) {
alert("Error on Saving");
});