I'm building a page that uses the Slideshare API to pull a limited set of presentations from a specific Slideshare user and output a series of iframes with those presentations embedded.
For the most part the page is working as intended, however one of the presentations that the API is calling was recently deleted, and therefore when the page loads that iframe the browser returns a 410. Unfortunately there is no object in the XML returned by Slideshare that can be used to determine if that particular presentation has been deleted.
Therefore I would like to check for the 410 error prior to adding that particular iframe to the page. What's making this difficult for me is that the error doesn't appear when making the API call, but rather when the embedded iframe loads, so I don't know where or how to make the appropriate check.
Here's the relevant code:
$.ajax({
url: slideShareURL,
dataType: 'XML',
success: function(data) {
var allSlides = x2js.xml2json(data);
allSlides = allSlides.User.Slideshow;
for (i = 0; i < allSlides.length; i++) {
document.getElementById('slides').innerHTML += allSlides[i].Embed;
}
}
});
Notes:
slideShareURL is defined earlier in the code using the API
I'm using xml2json.js to convert the XML received from the API to a json object
allSlides is an array of presentations each containing a set of objects such as slide title, id, download URL, etc
Embed is the object that contains the full iframe embed code
EDIT: Answer provided by Christophe below was almost perfect, but I had to add a closure to make the second ajax call work within the for loop.
Hmmm,what about this solution :
$(function() {
$.ajax({
url: "www.domain.com/iframe",
dataType: "jsonp",
timeout: 5000,
success: function () {
$("#iframe").attr("src", "www.domain.com/iframe");
},
error: function (parsedjson) {
if(parsedjson.status == "200") {
$("#iframe").attr("src", "www.domain.com/iframe");
} else {
// Handle error
}
}
});
});
The answer provided by Christophe put me on the right track, but it wasn't possible to run the second ajax call properly within the for loop. After some more research I learned about closures, and came up with the following:
$.ajax({
url: slideShareURL,
dataType: 'XML',
success: function(data) {
var allSlides = x2js.xml2json(data);
allSlides = allSlides.User.Slideshow;
for (i = 0; i < allSlides.length; i++) {
(function(i) {
var slideURL = allSlides[i].SlideshowEmbedUrl;
$.ajax({
url: slideURL,
success: function () {
document.getElementById('slides').innerHTML += allSlides[i].Embed;
},
error: function () {
console.log('Failed to load');
}
})
})(i);
}
}
});
Related
This question already has answers here:
What does "async: false" do in jQuery.ajax()?
(7 answers)
Closed 3 years ago.
I have an ajax function and thought it would be nice to include a little ajax-spinner to tell the enduser something is actually happening. This is my current jQuery function:
$('#contact-form').submit(function(e)
{
e.preventDefault();
let overlay = $('#overlay'),
loader = $('#loader-popup');
console.log(overlay);
console.log(loader);
console.log('===================');
//show overlay
overlay.removeClass('hidden');
loader.removeClass('hidden');
console.log(overlay);
console.log(loader);
let formData = new FormData($(this)[0]),
params = [];
$.ajax({
data: formData,
type: 'post',
url: '/pages/contact-us/action/send.php',
cache: false,
contentType: false,
processData: false,
success: function(res)
{
if (res == 1) {
params['type'] = 1;
params['msg'] = 'We will be with you as soon as we can!'
} else {
try {
res = $.parseJSON(res);
let data = [];
$.each(res, function(key, value) {data.push(value)});
params['type'] = 2;
params['msg'] = data.join('<br />')
} catch(e) {
console.log(e);
alert('Huh. that\'s weird, something went wrong! Please try again');
//cause syntax error to stop script working
die()
}
}
validator.displayAlert(params['type'], params['msg'])
},
error: function(res)
{
console.log(res);
alert('Don\'t worry.. it\'s not you, it\'s us.')
}
});
//hide overlay
overlay.addClass('hidden');
loader.addClass('hidden');
});
But weirdly the overlay doesn't show, nor does the loader. What makes this hard to kinda debug and fathom is the console.log output.
first console.log(overlay)
Object [ div#overlay.hidden ]
second console.log(loader)
Object [ div#loader-popup.hidden ]
third console.log(overlay)
Object [ div#overlay ]
fourth console.log(loader)
Object [ div#loader-popup ]
So I can see that my .removeClass() function is working, however, inspecting my page once the form is being submitted shows the elements with the hidden class. If I manually remove that hidden class in the inspector tab then everything shows, so I know it's not a CSS issue.
You can see this happen on a much simpler scale here
I've also tried with .toggle() with no avail.
How do I even begin to debug something that seems to work behind-the-scenes but, not on screen?
You should call hide the overlay in your callback, because it'll be executing asynchronously.
Something like
try {
res = $.parseJSON(res);
let data = [];
$.each(res, function(key, value) {
data.push(value)
});
params['type'] = 2;
params['msg'] = data.join('<br />')
} catch (e) {
console.log(e);
alert('Huh. that\'s weird, something went wrong! Please try again');
//cause syntax error to stop script working
die()
} finally {
//hide overlay
overlay.addClass('hidden');
loader.addClass('hidden');
}
The logic within the $.ajax() call is asynchronous. As such you remove the class then immediately add it back in as the AJAX request is in progress.
To fix this, change the addClass() calls to be made after the AJAX request completes. In your case the best place to do this would be in the complete callback as it will fire whether the AJAX request completed successfully or with an error:
$('#contact-form').submit(function(e) {
e.preventDefault();
let $overlays = $('#overlay, #loader-popup').removeClass('hidden');
let formData = new FormData(this),
params = [];
$.ajax({
// ajax settings...
complete: function() {
$overlays.addClass('hidden');
}
});
});
I'm facing an issue trying to implement notifications in my website.
In fact, I'm trying to make a function that calls PHP with an ajax request, in order to check with mysql if there are any notification. Then, if there is one/few notification(s), I get back from PHP the information I need, and display them using notification API. I do this every 5 seconds.
In fact, notifications are displayed in the top right corner, and I can only see the bottom of it.
Other weird fact, when I use function alert();, notifications are properly displayed.. This issue is happening with Firefox, but not on chromium.
So my question is, do you have any idea why notifications are not placed properly on firefox but not on Chromium? If you need any more information do not hesitate. Thanks in advance, and if you need it, here is some code :
With this two functions, I get what I need thanks to a php script.
function notifications() {
$.ajax({ url: './get_notifications.php/',
type: 'post',
data: {"name": window.user},
success: function(output) {
if (output != "")
{
split_notifications(output);
}
else
return;
},
failure: function() {
console.log("failed");
}
});
}
function check_notifications() {
setInterval(function() {
notifications();
}, 10000);
}
In this function, I just split information and then call another function, in charge of creating my notification.
function split_notifications(notif) {
var tmp_notif = notif.split(";");
var index = 0;
while (tmp_notif[0].split(",,,")[index])
{
//!\\ When I put alert() HERE it's working
display_notification(tmp_notif[1].split(",,,")[index], tmp_notif[2].split(",,,")[index], tmp_notif[0].split(",,,")[index]);
index += 1;
}
}
Here is the function that creates my notification :
function display_notification(title, message, someone) {
{
if(window.Notification && Notification.permission !== "denied") {
Notification.requestPermission(function(status) { // status is "granted", if accepted by user
var project_notification = new Notification(title, {
body: someone + " " + message + '\nSee more...',
icon: "../img/" + someone.split(" ")[1] + ".png"
});
project_notification.onclick = function() {
alert("");
}
});
}
}
EDIT: The base question is the same, but instead of a global buttons array getting passed back, I'm now passing back a getButtons() function.
I'm making an AJAX request to the server to get data to populate a popup box, and it's passing the data back as a JSP. Part of the JSP includes a JSON object with data used by the client to draw some buttons.
The issue is that sometimes the JSON object isn't ready on the success callback, so the buttons don't get drawn. The rest of the JSP is there, so the ancillary functionality works fine.
For most pages this does work flawlessly, but on certain pages, where the content is ready much more quickly, the buttons haven't yet been set. How can I ensure that buttons will be set?
Server code:
//Arbitrary JSP code prior to this function
function getButtons() {
var buttons = [];
buttons.push(addButton('Send', 'functionName', buttonDescription));
buttons.push(addButton('Cancel', 'functionName', buttonDescription));
return buttons;
}
Client code:
popupBoxFunction(action, params, global) {
$.ajax({
url: action,
type: 'POST',
data: params,
dataType: 'html',
global: global,
success: function (data, textStatus, xhr) {
$("#container").html(data);
if (typeof getButtons() != "undefined") {
var myButtons = generatePopupButton(getButtons());
for (var i = 0; i < myButtons.length; i++) {
//do something
}
}
//arbitrary rest of function
}
Option - 1
If you want your generatePopupButton to be called only after all the buttons are available, why don't we just call it inside getButtons. That way you don't have to check if buttons are available and everything happens one after the other.
Your JSP :
<script>
// make it an IIFE so that it's executed right after parsing
(function() {
var buttons = [];
buttons.push(addButton('Send', 'functionName', buttonDescription));
buttons.push(addButton('Cancel', 'functionName', buttonDescription));
var myButtons = generatePopupButton(buttons);
for (var i = 0; i < myButtons.length; i++) {
//do something
}
})();
</script>
Your Client Code :-
popupBoxFunction(action, params, global) {
$.ajax({
url: action,
type: 'POST',
data: params,
dataType: 'html',
global: global,
success: function (data, textStatus, xhr) {
/* just add your html and script to DOM
and wait for the browser to execute it for you */
$("#container").html(data);
}
});
//arbitrary rest of function
}
Option - 2
If you don't like the idea of moving your generatePopupButton and other script code in to your JSP, you can make use of Function Body As a String technique used in most of the template engines.
In your JSP :-
<!-- make sure type is not text/javascript, something gibberish that
is not known to browser -->
<script id="template" type="my-type">
var buttons = [];
buttons.push(addButton('Send', 'functionName', buttonDescription));
buttons.push(addButton('Cancel', 'functionName', buttonDescription));
return buttons;
</script>
Make sure that script type is not something that is known to browser. You can even change the script tag to div tag if you please :)
In your client code :-
$.ajax({
url: action,
type: 'POST',
data: params,
dataType: 'html',
global: global,
success: function (data, textStatus, xhr) {
$("#container").html(data);
var funBody = $('#template').html();
/* Now that we've function body as text
retrieve that in code*/
var fn = new Function(funBody);
// Now that we've the function invoke it to get buttons
var myButtons = generatePopupButton(fn());
for (var i = 0; i < myButtons.length; i++) {
//do something
}
}
});
Here's a bin to play with the second approach.
There are even other ways of doing this, but these two are just from the top of my head :)
According to me the best way would be to move the code where html is set $("#container").html(html), inside the IF statement so that html is rendered only after the buttons are set.
EDIT
As rendering HTML and BUTTON are independent. A minimum amount of delay can be introduced before rendering the HTML so that difference between rendering HTML and rendering buttons gets reduced.
function popupBoxFunction(action, params, global) {
var ajaxPromise = $.ajax({
url: action,
type: 'POST',
data: params,
dataType: 'html',
global: global
});
}
promiseAjax.done(function(html){
setTimeout(function(html) {
$("#container").html(html);
}, 300);
if (typeof getButtons() != "undefined") {
var myButtons = generatePopupButton(getButtons());
for (var i = 0; i < myButtons.length; i++) {
//do something
}
// $("#container").html(html);
}
});
Could you please allow some milliseconds to pass before checking if (typeof getButtons() != "undefined") {.
For example inside success handler can you please try something like following:
success: function (data, textStatus, xhr) {
$("#container").html(data);
setTimeout(function(){
if (typeof getButtons() != "undefined") {
var myButtons = generatePopupButton(getButtons());
for (var i = 0; i < myButtons.length; i++) {
//do something
}// end for loop
} //end if check
},500); //500 milli sec delay
}// end success handler
I'm trying to add versioning functionality to a custom entity, MFAs, and I'm running into a very odd problem. I have a javascript webresource being called from two places: an onSave event on the form, and as the action of a custom ribbon button. Specifically, the onSave event calls captureSave, while the ribbon button calls makeARevision.
When called from the save button/event, everything works as expected; all information, including the new changes, are pulled to a new record and saved there, while the original record is closed without the changes being saved, and without a prompt to save. However, when called via the custom ribbon button, any unsaved changes do not get brought over to the new record, and the old record prompts for saving. Furthermore, even if the user chooses to save the changes to the old record, the changes are not saved, and the form doesn't automatically close.
The following code is the webresource in question. company_MFASaveOrRevise is just an html page that asks the user whether they want to save the record or create a new revision. Any ideas on what's causing the differences or how to resolve them is appreciated.
function captureSave(executionContext) {
if (Xrm.Page.ui.getFormType() != 1 && Xrm.Page.data.entity.getIsDirty()) {
var retVal = showModalDialog(Xrm.Page.context.getServerUrl() + '/Webresources/company_MFASaveOrRevise', null, 'dialogWidth: 300px; dialogHeight: 100px');
if (retVal == "Revise") {
executionContext.getEventArgs().preventDefault();
makeARevision();
}
else if (retVal == "Save") {
}
}
}
function createLookupValue(oldLookup) {
var lookupVal = new Object();
lookupVal.Id = oldLookup.id;
lookupVal.LogicalName = oldLookup.entityName;
lookupVal.Name = oldLookup.Name;
return lookupVal;
}
function makeARevision() {
var revisedMFA = {};
revisedMFA['company_mfaname'] = Xrm.Page.data.entity.attributes.get('company_mfaname').getValue();
revisedMFA['company_mfadate'] = Xrm.Page.data.entity.attributes.get('company_mfadate').getValue();
revisedMFA['company_estimatedliqdate'] = Xrm.Page.data.entity.attributes.get('company_estimatedliqdate').getValue();
revisedMFA['company_actualliqdate'] = Xrm.Page.data.entity.attributes.get('company_actualliqdate').getValue();
revisedMFA['company_mfanumber'] = Xrm.Page.data.entity.attributes.get('company_mfanumber').getValue();
revisedMFA['company_revisionno'] = Xrm.Page.data.entity.attributes.get('company_revisionno') == null ? 0 : Xrm.Page.data.entity.attributes.get('company_revisionno').getValue() + 1;
revisedMFA['company_requester'] = createLookupValue(Xrm.Page.data.entity.attributes.get('company_requester').getValue()[0]);
revisedMFA['company_mfapreviousrev'] = Xrm.Page.data.entity.attributes.get('company_totalmfatodate').getValue();
revisedMFA['company_contract'] = createLookupValue(Xrm.Page.data.entity.attributes.get('company_contract').getValue()[0]);
$.ajax({
type: 'POST',
contentType: 'application/json; charset=utf-8',
datatype: 'json',
url: getODataUrl() + '/' + 'company_mfaSet',
data: JSON.stringify(revisedMFA),
beforeSend: function (XMLHttpRequest) {
//Specifying this header ensures that the results will be returned as JSON.
XMLHttpRequest.setRequestHeader('Accept', 'application/json');
},
success: function (data, textStatus, request) {
Xrm.Utility.openEntityForm("company_mfa", data.d.company_mfaId.toUpperCase());
var attributes = Xrm.Page.data.entity.attributes.get();
for (var i in attributes) {
attributes[i].setSubmitMode('never');
}
Xrm.Page.ui.close();
},
error: function (request, textStatus, errorThrown) {
alert(errorThrown);
//alert("There was an error creating the revision");
}
});
}
Edit: I had debugger; inserted in various places and was using VS2012 debugger, and found that the attributes were being properly set not to submit, but apparently that didn't stop the confirmation dialog from popping up (even though it works when the webresource is called through the save button). Additionally, Xrm.Page.data.entity.attributes.get(attributeName) returns the post-changes values when called during onSave event, but pre-change values when called from the ribbon. I still don't know why or how to fix it though. Is there something else I should look for?
Use F12 to debug your code when being called from the ribbon (just remember since it is in the ribbon, your javascript code will be in a dynamic script / script block).
I have the code below to find the next sequential page number and load it at the bottom of the page once the user hits the bottom of the screen.
the loading div slides down and as it is loading and up once it is done... it is set to "display:none" by default
What i need is a line of code in there which basically hides the #loading div if no more pages can be found to load... " var url = "page"+nextpage+".html";" finds the new page... titled 'page 2.html, page3.html' and so on.
Any help would be appreciated. I'm assuming it is easy but I can't find a solution anywhere...
alreadyloading = false;
nextpage = 2;
$(window).scroll(function() {
if($(window).scrollTop() + $(window).height() == $(document).height()) {
if (alreadyloading == false) {
$("#loading").slideDown();
var url = "page"+nextpage+".html";
alreadyloading = true;
$.post(url, function(data) {
$('#newcontent').children().last().after(data);
alreadyloading = false;
nextpage++;
$("#loading").slideUp();
});
}
}
});
If there is no such file then the AJAX request will fail, so you can do what you need from inside a "failure" handler. To be able to specify that, you one solution is to move from using $.post to using the more configurable $.ajax, which gives you all the necessary options:
$.ajax({
type: 'POST',
url: url,
success: function(data) {
$('#newcontent').children().last().after(data);
nextpage++;
},
complete: function() {
alreadyloading = false;
$("#loading").slideUp();
}
});
The complete callback contains code which will be executed no matter what happens with the request; the success callback will be executed before complete, but only if the request was successful.