I am doing CRUD for our website. Our implementation is to use submit but in some cases I need to pass data from JS file to my controller (BTW I am using Codeigniter) so I am now thinking if it is standard to use it at the same time. So far it works for me.
In my experience, pass it all through JS and basically do below. Note it's about as pseudo code as possible. You will need to make changes for it to even compile.
$("#submit").on('click', function(e) {
e.preventDefault();
if(normal_stuff()){
$(this).sumbit();
} else {
fancy_stuff();
}
});
Related
I'm a Back-end dev, and recently inherited a couple of legacy Apache Tapestry systems. My skills with Tapestry are null my knowledge on javascript medium.
I want to disable a submit button right before the submit is made to avoid multiple submits.
A very simple
$(document).ready(function () {
$("#form").submit(function () {
$("#submitbutton").attr("disabled", true);
return true;
});
});
approach wont do because the submit event is propagated before Tapestry does the client-side validation, so I could be disabling the button on a submint attempt that will fail validation.
On this question I learned that Tapestry also propagetes its own events, and if I could listen on tapestry:formprocesssubmit That would probably solve my problem.
I have my CreateContract.tml file with the form, fields and buttons that already work, I have my CreateContract.java file with
#Import(library = {
"context:js/jquery.mask.min.js",
"context:js/maintain-contracts.js",
"context:js/zone-overlay.js"},
stylesheet = "context:layout/zone-overlay.css")
And my maintain-contracts.js file with
$(document).ready(function () {
$("#form").on('tapestry:formprepareforsubmit', function () {
console.log("I want to arrive here!");
});
});
But it doesn't work.
On this mailing list thread I see people discussing very similar matters, but they go about listening on events on a different fashion, that I don't quite grasp.
Should I create the listener inside the initializer?
I'm reffering to the event as 'tapestry:formprepareforsubmit' instead of Tapestry.FORM_PREPARE_FOR_SUBMIT_EVENT because on both my maintain-contracts.js and my console the Tapestry variable is empty - why is that?
What about the .on versus .bind versus .observe trichotomy?
the first links's question was solved using ProptypeJS Class.create, but I think i dont have access to that.
I'm I going about that wrong?
Thank you, any help is appreciated.
Not an analysis of why your approach doesn't work, but a useful referral nonetheless:
Geoff Callender, creator of Tapestry JumpStart, has an excellent description of how duplicate form submissions can be avoided using a mixin.
See http://jumpstart.doublenegative.com.au/jumpstart/examples/javascript/creatingmixins1
After reading Tapestry's source code, i found out that the event is not called tapestry:formprepareforsubmit, but t5:form:prepare-for-submit, even though I found no documentation of that anywhere.
So
$(document).ready(function () {
$("#form").on('t5:form:prepare-for-submit', function () {
console.log("I want to arrive here!");
});
});
Works perfectly.
I'm working on a fairly simple form using crowd-html elements, which makes everything very simple. As part of our study, we want to see how workers interact with the form, so we have a bunch of basic JS logging. That is all prepared as a JSON and the idea is to log it using AWS API Gateway and AWS Lambda. The code all seems to work in unit tests, but not in the real form. I am trying to do this:
document.querySelector('crowd-form').onsubmit = function (e) {
if (!validateForm()) {
window.alert("Please check the form carefully, it isn't filled out completely!");
e.preventDefault();
} else {
let event_data = {
'specific_scroll_auditor': auditor_scrolled_pixels_specific.submit_callable(),
'specific_clicks_auditor': auditor_clicks_specific.submit_callable(),
'mouse_movements_total': auditor_mouse_movement_total.submit_callable(),
'on_focus_time': auditor_on_focus_time.submit_callable(),
'total_task_time': auditor_total_task_time.submit_callable(),
'focus_changes': auditor_focus_changes.submit_callable()
};
log_client_event('auditors', event_data);
post_event_log()
}
}
Note that the validation bit works, but the logging does not. I've tested post_event_log() on it's own, and that works just fine, so it seems like either 1) for some reason I never get to that else clause, or 2) the submission happens more quickly than I can call the logging functions. (but why, since the validation works?)
I also tried this, borrowed from the turkey code (https://github.com/CuriousG102/turkey) which was our inspiration.
$(window).ready(function () {
window.onbeforeunload = function () {
let event_data = {
'specific_scroll_auditor': auditor_scrolled_pixels_specific.submit_callable(),
'specific_clicks_auditor': auditor_clicks_specific.submit_callable(),
'mouse_movements_total': auditor_mouse_movement_total.submit_callable(),
'on_focus_time': auditor_on_focus_time.submit_callable(),
'total_task_time': auditor_total_task_time.submit_callable(),
'focus_changes': auditor_focus_changes.submit_callable()
};
log_client_event('auditors', event_data);
post_event_log()
}
});
That also doesn't work. I would prefer to do this in some simple way like what I have above, rather than completely rewrite the submit function, but maybe I have to?
your custom UI is placed inside a sandboxed iFrame by Ground Truth. It does that only for the real job, and not for previews (you're code might work while previewing the UI from AWS Console). The sandbox attribute on the iFrame goes like this
sandbox="allow-scripts allow-same-origin allow-forms"
Refer https://www.w3schools.com/tags/att_iframe_sandbox.asp for descriptions. Ajax calls are blocked regardless of the presence of allow-same-origin (not that you could change it in any way). See for a thorough explanation IFRAME sandbox attribute is blocking AJAX calls
This example might help.
It updates the onSubmit function to do some pre-submit validations.
https://github.com/aws-samples/amazon-sagemaker-ground-truth-task-uis/blob/master/images/keypoint-additional-answer-validation.liquid.html
Hope this helps. Let us know if not.
Thank you,
Amazon Mechanical Turk
I use ActiveForms often and find it handy as it includes client-side validation scripts yii.js and yii.activeForm.js. It normally takes care of model rules and basic validation on its own.
Until Yii 2.0.9:
We could use following script to prevent multiple form submission due to rapid button clicks:
$('form').submit(function(){
$(this).find('button[type!="button"],input[type="submit"]').attr("disabled",true);
setTimeout(function(){
$('form .has-error').each(function(index, element) {
$(this).parents("form:first").find(":submit").removeAttr("disabled");
});
},1000);
});
But,
Current Yii 2.0.10 release brought some changes and fails above script. Now, it will not submit the form if above code executes.
It has also been discussed earlier here and has been identified as bug.
Since, yii.js had two changes:
Bug #10358: Fixed race condition in yii.js AJAX prefilter (silverfire)
Enh #12580: Make yii.js comply with strict and non-strict javascript mode to allow concatenation with external code (mikehaertl)
and, yii.activeForm.js had four changes:
Bug #10681: Reverted fix of beforeValidate event calling in yii.activeForm.js (silverfire)
Enh #12376: Added parameter to yii.activeForm.js validate() method to be able to force validation (DrDeath72)
Enh #12499: When AJAX validation in enabled, yii.activeForm.js will run it forcefully on form submit to display all possible errors (silverfire)
Enh #12744: Added afterInit event to yii.activeForm.js (werew01f)
Can they be replaced with oder js files from v2.0.9?
Will replacing js files cause breakdown and unexpected behaviours?
Are there any better solution to prevent multiple submissions?
It looks like the issue had been taken care of, already.
Those who have installed fresh Yii 2.0.10 via composer will not have this issue; while, those who downloaded an archived file from 'Install from an Archive File' section may still have this issue since they might not have updated the archive files.
If you are facing this specific issue, then all you have to do is replace a specific file framework/assets/yii.activeForm.js from the github source.
In case of local copy, this file can be located at vendor\yiisoft\yii2\assets\yii.activeForm.js.
I suggest you to use uiBlocking to prevent multiple click or entries. Here is complte guide how to block ui while there is some task in progress.
http://malsup.com/jquery/block/
Works Like a Charm
I implemented and tested the following extension:
https://github.com/Faryshta/yii2-disable-submit-buttons
Composer Require
"faryshta/yii2-disable-submit-buttons": "*"
Register Asset Globaly
class AppAsset extends yii\web\AssetBundle
{
public $depends = [
'faryshta\\assets\\ActiveFormDisableSubmitButtonsAsset',
// other dependencies
];
}
Usage
$form = ActiveForm::begin([
'options' => ['class' => 'disable-submit-buttons'],
// other configurations
]);
// inputs
Html::submitButton('Submit', [
// optional, will show the value of `data-disabled-text` attribute
// while handling the validation and submit
'data' => ['disabled-text' => 'Please Wait']
])
$form->end();
Recently got some bug that my forms we not submiting, and button stayed disabled, so I changed it to this. Mostly posting it here for my future reference so I can find it out fast :D
<?php $this->registerJs("
$(function () {
$('body').on('submit', 'form', function() {
$(this).find('button[type!=\"button\"],input[type=\"submit\"]').attr('disabled',true);
setTimeout(function(){
$(this).find('.has-error').each(function(index, element) {
$(this).parents('form:first').find(':submit').removeAttr('disabled');
});
},1000);
});
});
", View::POS_END, 'prevent-double-form-submit'); ?>
Mid development I decided to switch to server-side rendering for a better control amongst other benefits. My web application is completely AJAX based, no url redirecting, so the idea here is a website that builds itself up
I just couldn't figure out the proper way to send javascript events/functions along with the html string, or should all the necessary javascript always be preloaded in the static files?
Let's say client clicks a pre-rendered button 'open table'
The server will make a query, build the html table and send it back, but this table also needs javascript triggers and functions to work properly, how are these sent, received and executed?
There are a couple of articles that mention to not use eval() in Javascript, is there any way around this? I don't want to have to preload unnecessary events for elements that don't yet exist
The server is Python and the Client is Javascript/JQuery
Theoretical example :
Client Base Javascript :
$("body").on("click", "#open_table", function() {
$.getJSON('/get_table', function(response){
$("#table_div").append(response.html);
eval(response.javascript()); //??
}
});
Python Server(views.py) :
def get_table(request):
data = {}
#String containing rendered html
data['html'] = get_render_table()
#String containing Javascript code?
data['javascript'] = TABLE_EVENTS_JAVASCRIPT
return HttpResponse(json.dumps(data),content_type='json')
Worth noting my question comes from an experimental/learning perspective
Update:
You can use jQuery.getScript() to lazy load JS. I think this solution is as close as you can get to run JS without using eval().
See this example:
jQuery.getScript("/path/to/script.js", function(data, textStatus, jqxhr) {
/* Code has been loaded and executed. */
console.log( data ); // Data returned
console.log( textStatus ); // Success
console.log( jqxhr.status ); // 200
console.log( "Load was performed." );
});
and "/path/to/script.js" could be a string returned from $.getJOSN response.
Also, the documentation for getScrippt() has examples on how to handle errors and cache files.
Old Answer:
Using .on() attaches events to current and future DOM elements.
You can either attache events prior to DOM insertion or attache event after DOM insertion.
So in your example you can do something like:
$("body").on("click", "#open_table", function() {
$.getJSON('/get_table', function(response){
var code = $(response.html);
code.find(".elementToFind").on("click", function (){
// Code to be executed on click event
});
$("#table_div").append(code);
}
});
I did not test the code but I think it should work.
Assuming you can't just set up an event-binding function and then call it from the main script (the JavaScript you need can't be guessed ahead of time, for example) then one really easy way is just to append the JavaScript to the bottom of the returned HTML content within script tags. When it's appended along with the HTML, the script should simply execute, with no eval() required.
I can't swear that this would work in old browsers, but it's a trick I've used a couple of times, and I've had no problems with it in Firefox, Chrome, or any of the later IE versions.
I think I see what you're asking here, from my understanding you want to send the new "page" asynchorously, and render the new javascript and html. It looks like you already got your request/response down, so i'm not gonna go and talk about sending JSON objects, and the whole "how-to" of sending html and javascript because it looks like you got that part. To do what you want and to dynamically add your javascript in, this stackoverflow question looks like it has what you need
Is there a way to create a function from a string with javascript?
So pertaining to your example, here is how it would look when you recieve the JSON string from your python script:
$("body").on("click", "#open_table", function() {
$.getJSON('/get_table', function(response){
$("#table_div").append(response.html);
/* Create function from string */
var newFunction = Function(response.javascript['param_1'], response.javascript['param_2'], response.javascript['function']);
/* Execute our new function to test it */
newFunction();
}
});
*Your actual function contents would be the string: response.javascript['function']
*Your parameter names if any would be in separate strings ex: response.javascript['param_1']
That is almost a direct copy of the "String to function" code that you can see in the linked question, just replaced it with your relevant code. This code is also assuming that your object is sent with the response.javascript object containing an array with your actual function content and parameter names. I'm sure you could change the actual name of the var too, or maybe put it in an associative array or something that you can keep track of and rename. All just suggestions, but hopefully this works for you, and helps you with your problem.
I am also doing similar work in my project where I had to load partial html using ajax calls and then this partial HTML has elements which requires events to be attached. So my solution is to create a common method to make ajax calls and keep a js method name to be executed post ajax call in html response itself. For example my server returns below html
<input type="hidden" data-act="onPartialLoad" value="createTableEvents" />
<div>.........rest of html response.....<div>
Now in common method, look for input[type='hidden'][data-act='onPartialLoad'] and for each run the method name provided in value attribute (value="createTableEvents")
Dont Use Eval() method as it is not recommended due to security
issues. Check here.
you can run js method using window["method name"]...so here is a part of code that I use.
$.ajax(options).done(function (data) {
var $target = $("#table_div");
$target.fadeOut(function () {
$target.html(data);
$target.fadeIn(function () {
try {
$('input[data-act="onPartialLoad"]', $target).each(function () {
try {
//you can pass parameters in json format from server to be passed into your js method
var params = $(this).attr('params');
if (params == undefined) {
window[$(this).val()]();
}
else {
window[$(this).val()]($.parseJSON(htmlutil.htmlDecode(params)));
}
} catch (e) {
if (console && console.log) {
console.log(e.stack);
console.log($(this).val());
}
}
});
}
catch (e) {
console.log(e.stack);
}
});
});
});
use jQuery.getScript() (as suggested by Kalimah Apps) to load the required js files first.
I have a complex form requiring me to switch specific validators on or off depending on selections made by the user.
ValidatorEnable seems to do the job, but it seems that when I call this method it actually fires the validation process as well, without the user actually hitting the submit button.
Is that how it works?
I eventually found the best way to do this was to use the following code:
var validatorObject = document.getElementById('<%=ValidHasShippingLocationZip.ClientID%>');
validatorObject.enabled = false;
validatorObject.isvalid = true;
ValidatorUpdateDisplay(validatorObject);
I wrote some code seems can meet your requests.
Iterate validators and enable these you needs.
ValidatorEnable(validatorObj, true);
then clear the screen,erase the error info.
The full code snippet can be found here http://codelife.cybtamin.com/enable-and-disable-asp-net-validator-by-javascript/