So I can see the info of a user in a FullCalendar that is opened in a modal but when I try to open another the modal it doesn`t refresh. I tried all the solutions I found here on Stackoverflow but it didn't work. If I refresh the page then it works if I click in a diferent id.
Code where I bring the id of user to my function cale():
<button id="cal" onclick="cale('.$row["idutilizador"].') class="btn" data-toggle="modal" href="#calendario"><i class="fa fa-calendar"></i></button>
My calendar modal Bootstrap:
<div class="modal fade" id="calendario" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog modal-xl" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Calendario</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<br />
<h2 align="center">Calendario</h2>
<br />
<div class="container">
<div id="calendar"></div>
</div>
</div>
</div>
</div>
</div>
My function that loads the information from database with the id I got when I click:
function cale(uti){
var calendar = $('#calendar').fullCalendar({
editable:true,
header:{
left:'prev,next today',
center:'title',
right:'month,agendaWeek,agendaDay'
},
events: {
url:'../Components/calendar/load.php',
type: 'POST',
data: function() {
return {
id: uti
};
}
},
...
Your code currently reinitialises the calendar every time you click a button. You should only initialise the calendar once, and then change the data it displays. To do that, you need to first remove the previous event source, add the new one, and then get the new events.
A suggestion: convention is that POST is for changing data (eg making a purchase, updating a record), while GET is for reading data. Here your event source is just loading event data to display, that really should be a GET request. Changing that also makes the code a bit simpler. I've changed to GET here, if you want to do this you need to change your PHP to respond to GET instead of POST.
Another suggestion: AFAICT you are using multiple non-unique HTML IDs on the same page. Your code suggests that the button is inside a loop, so you have buttons for multiple users, but your buttons all have the same ID:
<button id="cal" ...
The code you've shown does not use that ID, but if you try to, it won't work. IDs must be unique, if they are not and you try to use them, only the first one will work.
Another suggestion: it is generally considered best to separate your JS and your HTML, so instead of using inline onclick, use a separate event handler. You'll need to add the user ID to the button somehow, maybe with a data attribute:
<button data-id="' . $row["idutilizador"] . '" ...
And then instead of onclick() on that button, add an event handler in your JS:
$('button').on('click', function(e) {
// Prevent any default action the button click might normally
// do, eg submit a form or something.
e.preventDefault();
// Find the ID of the clicked button
var userID = $(this).data('id');
// Now call the calendar function with that ID
cale(userID);
});
The code below implementes all these suggestions.
UPDATE As per comments you're using FullCalendar v3, so here's a working v3 solution (click Run to see it in action). I've also converted the previous v5 solution into a working snippet, see below.
FullCalendar v3 solution
// The base URL where your events are. I'm using npoint JSON
// bins from https://www.npoint.io/, yours would be:
// var sourceURL = '../Components/calendar/load.php';
var sourceURL = 'https://api.npoint.io/';
// The current source (none initially)
var currentSource;
// The calendar
var calendar = $('#calendar').fullCalendar({
defaultDate: '2022-01-15',
editable:true,
header:{
left:'prev,next today',
center:'title',
right:'month,agendaWeek,agendaDay'
},
// no events initially
});
// Handle button clicks
$('button').on('click', function(e) {
// Prevent any default action the button click might normally
// do, eg submit a form or something.
e.preventDefault();
// Find the ID of the clicked button
var userID = $(this).data('id');
// Now call the calendar function with that ID
cale(userID);
});
// Update sources
function cale(uti) {
// First remove the current source. First time through
// there is no source, but that does not matter.
// v3: https://fullcalendar.io/docs/v3/removeEventSource
calendar.fullCalendar('removeEventSource', currentSource);
// Set up the URL to the new source. I'm using npoint JSON
// bins from https://www.npoint.io/, so this URL format is
// different to yours, you would use:
// currentSource = sourceURL + '?id=' + uti
currentSource = sourceURL + uti;
// Now add the new source. Note this will use a GET request
// to retrieve events. The new events will be immediately
// fetched and displayed.
// v3: https://fullcalendar.io/docs/v3/addEventSource
calendar.fullCalendar('addEventSource', currentSource);
}
hr {
margin: 20px 0;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.4.0/fullcalendar.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.4.0/fullcalendar.min.js"></script>
Click to select a source:
<button data-id="965e830c3e8ab78990c5">Source 1</button>
<button data-id="5c8901e5173d5eab3ad6">Source 2</button>
<hr>
<div id="calendar"></div>
FullCalendar v5 solution
And here's the original v5 solution, as a working snippet, click Run to see it working.
// The base URL where your events are. I'm using npoint JSON
// bins from https://www.npoint.io/, yours would be:
// var sourceURL = '../Components/calendar/load.php';
var sourceURL = 'https://api.npoint.io/';
// The current source (none initially)
var currentSource;
// The calendar
var calendarEl = document.getElementById('calendar');
var calendar = new FullCalendar.Calendar(calendarEl, {
initialDate: '2022-01-15',
editable:true,
// no events initially
});
calendar.render();
// Handle button clicks
$('button').on('click', function(e) {
// Prevent any default action the button click might normally
// do, eg submit a form or something.
e.preventDefault();
// Find the ID of the clicked button
var userID = $(this).data('id');
// Now call the calendar function with that ID
cale(userID);
});
// Update sources
function cale(uti) {
// First get all the current event sources
// v5: https://fullcalendar.io/docs/Calendar-getEventSources
var sources = calendar.getEventSources();
// Now remove those event sources. Note the first time through there
// are none.
// v5: https://fullcalendar.io/docs/EventSource-remove
for (const source of sources) {
source.remove();
}
// Set up the URL to the new source. I'm using npoint JSON
// bins from https://www.npoint.io/, so this URL format is
// different to yours, you would use:
// currentSource = sourceURL + '?id=' + uti
currentSource = sourceURL + uti;
// Now add your new source. Note this will use a GET request to
// retrieve events. The new events will be immediately fetched
// and displayed.
// v5: https://fullcalendar.io/docs/Calendar-addEventSource
calendar.addEventSource(currentSource);
}
hr {
margin: 20px 0;
}
<link href="https://cdn.jsdelivr.net/npm/fullcalendar#5.10.1/main.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/fullcalendar#5.10.1/main.min.js"></script>
Click to select a source:
<button data-id="965e830c3e8ab78990c5">Source 1</button>
<button data-id="5c8901e5173d5eab3ad6">Source 2</button>
<hr>
<div id="calendar"></div>
Try to empty first the div
$('#calendar').html('');
obviously first of
var calendar = $('#calendar').fullCalendar({...
Related
I need some help assigning two very different functions to a single button. The following code shows an "export" button which exports a .kml file from a leaflet.Draw map, and the "submit" button opens a modal which is used to display a contact form:
index.html
<a href='#' id='export'>Export Feature</a>
<button onclick="document.getElementById('id01').style.display='block'">Submit</button>
<!-- The Modal -->
<div id="id01" class="modal">
<span onclick="document.getElementById('id01').style.display='none'"
class="close" title="Close Modal">×</span>
<div class="form-style" id="contact_form">
....
Script
document.getElementById('export').onclick = function(e) {
// Extract GeoJson from featureGroup
var data = featureGroup.toGeoJSON();
var kml = tokml(data);
// Convert to dataURL format
var convertedData = 'application/vnd.google-earth.kml+xml;charset=utf-8,' + encodeURIComponent(kml);
// Create export
document.getElementById('export').setAttribute('href', 'data:' + convertedData);
document.getElementById('export').setAttribute('download','data.kml');
}
....
var modal = document.getElementById('id01');
What I would like to do is have the modal open up when the feature is exported, using the "export" button.
Any ideas?
You should add id to your submit button, for example let it be id=submitbtn.
Then in your export onclick you should call document.getElementById('submitbtn').click()
I found the following to solve my question:
<a href='#' id='export'>
<span onclick="document.getElementById('id01').style.display='block'">
Export Feature
</span>
</a>
I was originally a little confused as the export button is an and I didn't know how to work this in with a
I would want to know when we open a html page ,if it is possible to pass the details from parent to child page using reactjs.I opted to open in a html page rather than modal pop up because of heavy data to be displayed in the page.
Below is my code :
I would want the value in variable "message" to be passed to the html page i would be opening. this message can be lengthy one having more than 3000 characters.
var App = React.createClass({
openModal: function () {
var message = "hello xyz";
window.open('../Scripts/Custom/Details.html', '', 'toolbar=no,status=0,width=548,height=325,menubar=no,location=no,resizable=yes');
},
render: function () {
return (
<div>
<button onClick={this.openModal} >Open Modal</button>
</div>
);
}
}
);
ReactDOM.render(<App/>, document.getElementById('container'))
I'm following this angular recipes page for adding a modal dialog to my ui. It suggests the following markup, which I've added to one of my views.
... html for my view is here ...
<button class="btn" ng-click="open()">Open Modal</button>
<div modal="showModal" close="cancel()">
<div class="modal-header">
<h4>Modal Dialog</h4>
... etc, from the recipe doc
</div>
What I want to see is my view, plus an "Open Modal" button on the bottom and nothing else. What I see instead is the button and the content of the modal already visible on the page.
The very next words in the recipe doc are:
Note that even though we don’t specify it explicitly the modal dialog
is hidden initially via the modal attribute. The controller only
handles the button click and the showModal value used by the modal
attribute.
Why is my modal mark up initially visible on the page? I think I have installed angular-ui properly... in my index.html:
<script src="bower_components/angular-bootstrap/ui-bootstrap-tpls.js"></script>
And in my app JS:
angular.module('MonteAdmin', [
...
'ui.bootstrap',
...
])
That recipes page is likely out of date. At the time of the writing it might have been possible to pass a variable showModal to the modal directive to reveal or hide it. In your controller, you would have been able to show the modal by setting the scope variable showModal to true or false:
$scope.showModal = false;
$scope.open = function() {
$scope.showModal = true;
}
The current version does not work that way. You will have much better experience if you read the official documentation for the library at Angular UI Bootstrap
If you are using the latest version of the library, the directive is no longer modal but uib-modal. In addition, you have a bit more work to do to implement your modal.
Modal markup should be in a script tag, with a type set to text/ng-template as per the official example:
<script type="text/ng-template" id="stackedModal.html">
<div class="modal-header">
<h3 class="modal-title" id="modal-title-{{name}}">The {{name}} modal!</h3>
</div>
<div class="modal-body" id="modal-body-{{name}}">
Having multiple modals open at once is probably bad UX but it's technically possible.
</div>
</script>
To actually open the modal, your button click should trigger the following example function:
var modalInstance = $uibModal.open({
animation: $ctrl.animationsEnabled,
ariaLabelledBy: 'modal-title',
ariaDescribedBy: 'modal-body',
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
controllerAs: '$ctrl',
size: size,
appendTo: parentElem,
resolve: {
items: function () {
return $ctrl.items;
}
}
});
You must also define a controller for the modal, itself:
angular.module('ui.bootstrap.demo').controller('ModalInstanceCtrl', function ($uibModalInstance, items) {
var $ctrl = this;
$ctrl.items = items;
$ctrl.selected = {
item: $ctrl.items[0]
};
$ctrl.ok = function () {
$uibModalInstance.close($ctrl.selected.item);
};
$ctrl.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
});
All of this code is found on the official documentation for Angular UI Bootstrap
I integrated the new hidden reCAPTCHA (v2) framework which by default verifies the user with the click event of the submit button. But this event is triggered before the built-in HTML form validation. I am looking for a way to make it in the expected order: form validation first, reCAPTCHA after.
You have to do it programmatically thanks to a new v2 grecaptcha method: grecaptcha.execute() so that recaptcha doesn't replace the button's default click event which was preventing the default HTML5 form validation.
The event path is:
Submit button click event: browser built-in form validation
Form submit event: call grecaptcha.execute()
reCAPTCHA callback: submit the form
$('#form-contact').submit(function (event) {
event.preventDefault();
grecaptcha.reset();
grecaptcha.execute();
});
function formSubmit(response) {
// submit the form which now includes a g-recaptcha-response input
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://www.google.com/recaptcha/api.js"></script>
<form action="?">
<div class="g-recaptcha"
data-sitekey="your-key"
data-size="invisible"
data-callback="formSubmit">
</div>
<button type="submit">Submit</button>
</form>
Here is my solution to get HTML5 validation + Invisible recaptcha:
HTML:
<form id="my-form">
<!-- Your form fields ... -->
<div class="g-recaptcha"
data-sitekey="..."
data-callback="submitMyForm"
data-size="invisible">
</div>
<button type="submit">Submit</button>
</form>
JS:
var myForm = $('my-form');
function submitMyForm () {
myForm.trigger('submit', [true]);
}
$(function () {
myForm.on('submit', function (e, skipRecaptcha) {
if(skipRecaptcha) {
return;
}
e.preventDefault();
grecaptcha.execute();
});
})
Hi got a working solution here. Working with invisible Recaptcha.
jQuery(document).ready(function() {
var commentform = jQuery("#commentform");
commentform.on("click", "#submit-comment", function(e) {
if(commentform[0].checkValidity()) {
e.preventDefault();
grecaptcha.execute();
}
});
});
function submitCommentForm(data) {
document.getElementById("commentform").submit();
}
<form action="blaba.php" method="post" id="commentform" class="comment-form">
<div class="form-submit">
<div data-callback="submitCommentForm" data-sitekey="yourkey" class="g-recaptcha" data-size="invisible">
<button id="submit-comment">Leave a comment</button>
</div>
</form>
I had this problem as the default method seems to override the html5 form validation. I also wanted all code to be generic rather than hard coding any functions/element names. In the end I came up with the following code using the v3 api -
HTML
<form method="post" action="?" class="ui-recaptcha" name="my_form_name">
...
<input type="submit" value="Submit">
</form>
<script src="//www.google.com/recaptcha/api.js?render={key}" async defer></script>
Javascript (I'm using jQuery but would be fairly easy to adapt to vanilla js)
$('.ui-recaptcha').submit(e => {
var form = e.target;
if( $(form).data('recaptcha-done') )
return;
e.preventDefault();
grecaptcha.execute('{key}', {'action': $(form).attr('name')}).then(token => {
$(form).append($('<input>').attr({'type': 'hidden', 'name': 'g-recaptcha-response', 'value': token}));
$(form).data('recaptcha-done', true);
$(form).submit();
});
});
I found that just calling submit as in some examples above caused a loop for me, which would make sense seeing as the recaptcha handler runs on the submit event.
This runs recaptcha for any ui-recaptcha form, passes the form name attribute as the action which can be seen in reCaptcha console, and then inserts the token into the form. Once run it sets a data attribute on the form so the recursive call to submit doesn't try to run recaptcha again.
Here's my solution.
Uses reCaptcha v3 (invisible) docs
Uses native HTML5 form validation
Uses pure JS
Uses standard POST processing (can be modified to AJAX)
Add as many forms as needed, just change the 'UNIQUE_FORM_ID' in the two places, and update the POST_URL for the form.
Ensure you use your own key in the locations of 'RECAPTCHA_SITE_KEY'.
<form id="UNIQUE_FORM_ID" method="post" action="POST_URL">
<!-- ** Notice ** this hidden input field that will later send our g-recaptcha token back to our server -->
<input type="hidden" name="g-recaptcha-response" value="">
<!-- Add other hidden nonce fields -->
<!-- Required field -->
<input name="fullname" type="text" placeholder="Full Name" required>
<!-- Submit button -->
<!-- ** Notice ** the 'form' attribute; using SAME value as it's parent's form id, above. -->
<!-- ** Notice ** the 'onclick' attribute; be sure to pass event -->
<button type="submit" form="UNIQUE_FORM_ID" onclick="formSubmitBtn(event)">Send</button>
</form>
<!-- Only add scripts once -->
<!-- ** Notice ** to manually call grecaptcha, our site key must be included when loading api.js using the 'render' query param -->
<script src="https://www.google.com/recaptcha/api.js?render=RECAPTCHA_SITE_KEY"></script>
<script>
/**
* Handles form submissions for Google recaptcha v3.
* Allows for HTML5 form validation to complete before processing.
*/
function formSubmitBtn($event) {
/**
* Checks the validity of the form.
* Return if invalid; HTML5 validation errors should display.
*/
if (!$event.target.form.checkValidity()) {
return;
}
/**
* Form is client-side valid; taking over the remainder of processing.
*/
$event.preventDefault();
grecaptcha.ready(function() {
grecaptcha.execute("RECAPTCHA_SITE_KEY", { action: 'submit' }).then(function(token) {
/**
* Adds the token g-recaptcha-response token to our hidden form element.
* ** Notice ** we our referencing the specific form's input element by name here (do not use IDs).
*/
$event.target.form.elements['g-recaptcha-response'].value = token;
/**
* Use the form API directly to submit the form.
*/
$event.target.form.submit();
});
});
}
</script>
let siteKey = "...";
$("form").submit(function (eventObj) {
var myForm = this;
eventObj.preventDefault();
grecaptcha.execute( siteKey, {
action: "submit"
})
.then(function (token) {
$('<input />').attr('type', 'hidden')
.attr('name', "g_recaptcha_response")
.attr('value', token)
.appendTo(myForm);
myForm.submit();
});
});
This will execute recapcha, wait for response, add hidden attribute g_recaptcha_response to any form when browser try to submit it and then actually submit it. You need global variable siteKey
I was wanting the same behavior, but using the new recaptcha, the invisible one. After looking at some code and testing some stuff, I got into this. The main difference is that this uses the default browser validation as well:
var contact_form;
$(function() {
contact_form = $('#contact-form');
contact_form.submit(function (event) {
if ( ! contact_form.data('passed')) {
event.preventDefault();
grecaptcha.execute();
}
});
});
function sendContactForm(token) {
contact_form.data('passed', true);
contact_form.submit();
}
It basically stores the jquery form object in a global var, including, it uses sendContactForm as the callback, but when called by the recaptcha, it sets a data var named passed, which allows the form to not be prevented. It's exactly the same behavior as recaptcha would normally do, but with that condition.
Update: re-looking at my code right reminds me that it probably needs a way to restore data passed to false after grecaptcha's execution. Consider that if you'll implement this.
This solution is similar to solution by #PigBoT but with the addition of reportValidity() and is using ReCAPTCHA v3
Credit to https://github.com/ambethia/recaptcha/issues/302#issuecomment-621794131
<script src="https://www.google.com/recaptcha/api.js"></script>
<script type="text/javascript">
function contactOnSubmit(token) {
var contactForm = document.getElementById('contactUs');
if(contactForm.checkValidity()) {
//SERVER SIDE VALIDATION here,
//on success, contactForm.submit();
} else {
grecaptcha.reset();
contactForm.reportValidity();
}
}
</script>
Form (id="contactUs")
<button class="g-recaptcha" data-sitekey="..." data-callback="contactOnSubmit" data-action="submit">Submit</button>
"Can I Use" site currently reports 97% uses have support for checkValidity() https://caniuse.com/?search=checkValidity
I'm trying to use HTML form validation when using Google Apps Script's HTML Service. As another user asked, according to the documentation example, you must use a button input instead of a submit input. Using a submit button seems to do the validation, but the server function is called anyway. The answer given to that user didn't work for me. Also, I want to call two functions when submitting the form and this can make it more complex.
This is what I'm trying to do: The user fills a form and I generate a Google Doc and give him the URL. When he clicks the submit button, I show him a jQuery UI dialog saying "Your document is being created" with a nice spinner. Then, when the document is generated, I give him the link. I use the success handler to show the result when the Google Doc stuff is finished, but meanwhile I need a function to show the spinner. I don't know if there is a better way to do that than adding another function to the onclick event and maybe it can be damaging the process in some way. Is there a way not to call any of these functions if the form is not valid (using HTML validation)?
This is a simplified version of my code:
Code.gs
function generateDocument(formObject) {
var doc = DocumentApp.create("Document name");
...
return doc.getUrl();
}
Page.html
<main>
<form id="myForm">
...
<input type="button" value="Generate document"
onclick="showProgress();
google.script.run
.withSuccessHandler(openDocument)
.generateDocument(this.parentNode);"/>
</form>
<div id="dialog-confirm" title="Your document">
<div id="dialog-confirm-text"></div>
</div>
Javascript.html
$( "#dialog-confirm" ).dialog({ autoOpen: false, resizable: false, modal: true });
function showProgress() {
$( "#dialog-confirm" ).dialog({ buttons: [ { text: "Cancel", click: function() { $( this ).dialog( "close" ); } } ] });
$( "#dialog-confirm" ).dialog( "open" );
$( "#dialog-confirm-text" ).html( "<br />Wait a second, your document is being generated...<br /><br /><img src='http://i.stack.imgur.com/FhHRx.gif' alt='Spinner'></img>" );
return false;
}
function openDocument(url) {
$( "#dialog-confirm" ).dialog({ autoOpen: false, resizable: false, width: 400, buttons: [ { text: "Ok", click: function() { $( this ).dialog( "close" ); } } ] });
$( "#dialog-confirm-text" ).html( '<br />Click here to open and print your document!' );
return false;
}
All three HTML docs are joined together (and working with its respective tags) with the include function as recommended in the documentation.
The Cancel button in the dialog will close it but won't stop the doc being created. Is it possible to stop this process?
Here's a solution that I found:
"<input type='submit' onclick='if(verifyForm(this.parentNode)===true){google.script.run.withSuccessHandler(YOUROUTPUT).YOURFUNCTION(this.parentNode); return false;}' value='Submit'></form>";
JavaScript side
function verifyForm(){
var elements = document.getElementById("myForm").elements;
for (var i = 0, element; element = elements[i++];) {
if (element.hasAttribute("required") && element.value === ""){
resetInputs();
return false;
}
if (element.hasAttribute("pattern")){
var value = element.value;
if(value.match(element.pattern)){
}else{
resetInputs();
return false;
}
}
}
return true;
}
Calling the window has issues in iOS sometimes, which is why I investigated this further.
Move the function call to the <form> element; remove any function call from the submit input element; and put intermediary JavaScript code into a <script> tag:
<input tabindex="9" type="submit" value="Save Input" id='idInputBtn'>
<form id="myInputForm" name="input" onsubmit="fncWriteInput(this)">
<script>
window.fncWriteInput= function(argTheInfo) {
// Do additional checks here if you want
var everythingIsOk = . . . . . . . ;
if (everythingIsOk) {
google.script.run
.withSuccessHandler(openDocument)
.generateDocument(argTheInfo);
};
};
Notice that this.parentNode gets removed to the arg of the function call, and just use this in the function argument because the function is getting called from the <form> element, which is the parent.
If there are any errors, the form will not be submitted, and the user will get a msg that something was wrong. No code will run.
This is pseudo code, but I do use a set up like this in my application. But use developer tools and you can put a break point right in your browser and step through every line to test it without needing to put in console.log statements.