I am currently developing a web app that pulls metadata from a webservice.
It currently works in all browsers except that we get this weird issue on Windows Phone in Internet Explorer.
If you have a clear cache (first time load) it works with out a hitch, however once you refresh the page or navigate away and come back to the page the drop down lists fail to display data returned from the web service
Before:
After:
We are using standard jQuery $.ajax calls to a local webservice
It appears that in the after situation the success call back is being fired but the dropdowns aren't being rendered , and again this only happens after the page has successfully loaded once from a clean cache state and works fine in all other mobile browsers
the jquery code being used for the web service
function getAllPublications() {
$('.error').addClass('dn');
$('#selectYear').prop('disabled', 'disabled');
$('#selectVehicle').prop('disabled', 'disabled');
$('#selectManual').prop('disabled', 'disabled');
$('.manual-section').hide();
$.ajax({
url: "/_Global/HttpHandlers/OwnersManuals/GetPublications.ashx",
dataType: "json",
type: "GET",
success: function (data) {
$('.manuals-loader').hide();
if (data.getPublicationYearModelDataResult.ErrorCode == 0) {
allResults = data.getPublicationYearModelDataResult;
extractUniqueYears(allResults);
populateYearsDropdown();
} else {
$('.error.no-publication-error').removeClass('dn');
}
debugLog(JSON.stringify(data));
},
error: function (error) {
$('.manuals-loader').hide();
$('.error.api-error').removeClass('dn');
console.log(error);
}
});
}
function populateYearsDropdown() {
$('#selectYear')
.empty()
.append($("<option />").val('-').html(__pleaseSelect))
.removeAttr('disabled');
$.each(years, function (val, text) {
$('#selectYear').append($("<option />").val(text).html(text));
});
}
function extractUniqueYears(result) {
years = [];
if (result.PublicationYearModels != null) {
$(result.PublicationYearModels).each(function (i, item) {
if (item.YearModels != null) {
$(item.YearModels).each(function (j, subItem) {
var year = subItem.year;
if (!checkIfYearExists(year))
years[years.length] = year;
});
}
});
}
years.sort();
years.reverse();
}
Note: I have tried adding no cache and cache expiration headers to the page and also tried using cache expiration meta tags on the page as well with no effect
You should be able to trick browser into thinking that you're hitting the page for the first time using Location.reload(forcedReload). Here's an example using cookies to prevent you from getting stuck in an infinite refresh loop:
var ie_mobile_reload = false, FORCED_RELOAD = true;
if /refresh=true/.test(document.cookie) {
document.cookie = 'refresh=;'; /* Remove the refresh cookie */
ie_mobile_reload = true;
location.reload(FORCED_RELOAD);
}
/* IE Mobile only */
if /IEMobile/.test(navigator.userAgent) {
window.onbeforeunload = function() {
if (ie_mobile_reload) {
document.cookie = 'refresh=true'; /* Set the refresh cookie */
}
};
}
I set FORCED_RELOAD = true to skip the cache and cause the page to be reloaded directly from the server.
We ended up changing how the dropdown is initialized.
The problem was the dropdown list init was firing too late because of the caching issue on windows and wiping out the dropdown list content after it had been populated
we moved the logic into the DDL fill function and it fixed the issue so it clears all three DDLs when it is initially populated in the ajax function instead
Related
I'm working with a Google-Extention which allows me to open a new tab containing a form. After the form gets filled out and saved, every time I open this tab again the form should be prefilled with the data saved earlier.
Here is how the data gets saved: WORKS!
function saveCheckoutData() {
var vName = document.getElementById('txbx_name').value;
chrome.storage.sync.set({'name': vName}, function() {
console.log(vName);
})
}
Here is how i get the data: WORKS!
function getdata() {
chrome.storage.sync.get('name', function(data) {
var name = data.name;
if(name != null){
document.getElementById("txbx_name").value = name;
}
});
}
The code above gets called on button click and works perfectly!
But as soon I try to do this when the tab gets opened it doesn't work (the tab gets opened but there is nothing in the textbox): DOESN'T WORK!
function configAutofill(){
var newURL = "autofill_data.html";
chrome.tabs.create({ url: newURL });
chrome.storage.sync.get('name', function(data) {
var name = data.name;
if(name != null){
document.getElementById("txbx_name").value = name;
}
});
}
Does some one have an Idea why these lines do not work when creating a new tab?
Many thanks in advance.
Here's a question for you.
After creating a new tab, you access document.getElementById. Yes, but which document?
In your case, it would be the page calling create - which is not the created page.
In your case, it seems like you're opening a page that's part of the extension. Then you should just include code in it that will run on load.
You may want to check document.readyState:
if (document.readyState === "loading") {
document.addEventListener('DOMContentLoaded', getdata);
} else {
getdata();
}
If you're trying to do this with a webpage, you'll need a content script. Again, those normally execute after DOM is parsed - so just call getdata() at top level.
I am playing with jquery and js, trying to build an ajax overlay image viewer for a PHP website. With this code included at the bottom of the 'gallery page', the viewer opens and i can navigate with next and previous links inside the viewer. But the back button and the history is hard to understand. The browser often shows only the response of the ajax call, without the underlying page and css files, after some clicks back.
Perhaps somebody knows what is generally happening in such a case? I would like to understand why back sometimes results in a broken page, i.e. only the ajax response.
<script type="text/javascript">
$(document).ready(function() {
function loadOverlay(href) {
$.ajax({
url: href,
})
.done(function( data ) {
var theoverlay = $('#flvr_overlay');
theoverlay.html( data );
var zoompic = $('#zoompic');
zoompic.load(function() {
var nih = zoompic.prop('naturalHeight');
var photobox = $('#photobox');
if($(window).width() >= 750){
photobox.css('height',nih);
}
theoverlay.show();
$('body').css('overflow-y','hidden');
$(window).resize(function () {
var viewportWidth = $(window).width();
if (viewportWidth < 750) {
photobox.css('height','auto');
zoompic.removeClass('translatecenter');
}else{
photobox.css('height',nih);
zoompic.addClass('translatecenter');
}
});
});
});
return false;
}
var inithref = window.location.href;
$(window).on('popstate', function (e) {
if (e.originalEvent.state !== null) {
//load next/previous
loadOverlay(location.href);
} else {
//close overlay
$('#flvr_overlay').hide().empty();
$('body').css('overflow-y','scroll');
history.replaceState(null, inithref, inithref);
}
});
$(document).on('click', '.overlay', function () {
var href = $(this).attr('href');
history.pushState({}, href, href);
loadOverlay(href);
return false;
});
});
</script>
edit
clicking forward works:
/photos (normal page)
/photos/123 (overlay with '/photos' below)
/locations/x (normal page)
/photos/567 (overlay with '/locations/x' below)
clicking back gives me the broken view at point 2.
Do you need to prevent the default behaviour in your popstate to prevent the browser from actually navigating back to the previous page?
you have to manage it by own code.
You have a few options.
Use localstorage to remember the last query
Use cookies (but don't)
Use the hash as you tried with document.location.hash = "last search" to update the url. You would look at the hash again and if it is set then do another ajax to populate the data. If you had done localstorage then you could just cache the last ajax request.
I would go with the localstorage and the hash solution because that's what some websites do. You can also copy and paste a URL and it will just load the same query. This is pretty nice and I would say very accessible
Changing to document.location.hash = "latest search" didn't change anything.t.
This goes into the rest of the jQuery code:
// Replace the search result table on load.
if (('localStorage' in window) && window['localStorage'] !== null) {
if ('myTable' in localStorage && window.location.hash) {
$("#myTable").html(localStorage.getItem('myTable'));
}
}
// Save the search result table when leaving the page.
$(window).unload(function () {
if (('localStorage' in window) && window['localStorage'] !== null) {
var form = $("#myTable").html();
localStorage.setItem('myTable', form);
}
});
Another solution is that use INPUT fields to preserved while using back button. So, I do like that :
My page contains an input hidden like that :
Once ajax content is dynamicaly loaded, I backup content into my hidden field before displaying it:
function loadAlaxContent()
{
var xmlRequest = $.ajax({
//prepare ajax request
// ...
}).done( function(htmlData) {
// save content
$('#bfCache').val( $('#bfCache').val() + htmlData);
// display it
displayAjaxContent(htmlData);
});
}
And last thing to do is to test the hidden field value at page loading. If it contains something, that because the back button has been used, so, we just have to display it.
jQuery(document).ready(function($) {
htmlData = $('#bfCache').val();
if(htmlData)
displayAjaxContent( htmlData );
});
I have a single-page web app built with jQuery Mobile. After the user completes a certain action, I want to programmatically bring them back to a menu page, which involves going back in history and then performing some actions on elements of the menu page.
Simply doing
window.history.go(-1); //or $.mobile.back();
doSomethingWith(menuPageElement);
doesn't work, because the going-back action is asynchronous, i.e. I need a way of waiting for the page to load before calling doSomethingWith().
I ended up using window.setTimeout(), but I'm wondering if there's not an easier way (different pattern?) to do this in jQM. One other option is to listen for pageload events, but I find it worse from code organization point of view.
(EDIT: turns out native js promises are not supported on Mobile Safari; will need to substitute by a 3rd-party library)
//promisify window.history.go()
function go(steps, targetElement) {
return new Promise(function(resolve, reject) {
window.history.go(steps);
waitUntilElementVisible(targetElement);
//wait until element is visible on page (i.e. page has loaded)
//resolve on success, reject on timeout
function waitUntilElementVisible(element, timeSpentWaiting) {
var nextCheckIn = 200;
var waitingTimeout = 1000;
timeSpentWaiting = typeof timeSpentWaiting !== 'undefined' ? timeSpentWaiting : 0;
if ($(element).is(":visible")) {
resolve();
} else if (timeSpentWaiting >= waitingTimeout) {
reject();
} else { //wait for nextCheckIn ms
timeSpentWaiting += nextCheckIn;
window.setTimeout(function() {
waitUntilElementVisible(element, timeSpentWaiting);
}, nextCheckIn);
}
}
});
}
which can be used like this:
go(-2, menuPageElement).then(function() {
doSomethingWith(menuPageElement);
}, function() {
handleError();
});
Posting it here instead of in Code Review since the question is about alternative ways to do this in jQM/js rather than performance/security of the code itself.
Update
To differntiate whether the user was directed from pageX, you can pass a custom parameter in pagecontainer change function. On pagecontainerchange, retrieve that parameter and according bind pagecontainershow one time only to doSomething().
$(document).on("pagecreate", "#fooPage", function () {
$("#bar").on("click", function () {
/* determine whether the user was directed */
$(document).one("pagecontainerbeforechange", function (e, data) {
if ($.type(data.toPage) == "string" && $.type(data.options) == "object" && data.options.stuff == "redirect") {
/* true? bind pagecontainershow one time */
$(document).one("pagecontainershow", function (e, ui) {
/* do something */
$(".ui-content", ui.toPage).append("<p>redirected</p>");
});
}
});
/* redirect user programmatically */
$.mobile.pageContainer.pagecontainer("change", "#menuPage", {
stuff: "redirect"
});
});
});
Demo
You need to rely on pageContainer events, you can choose any of these events, pagecontainershow, pagecontainerbeforeshow, pagecontainerhide and pagecontainerbeforehide.
The first two events are emitted when previous page is completely hidden and before showing menu page.
The second two events are emitted during hiding previous page but before the first two events.
All events carry ui object, with two different properties ui.prevPage and ui.toPage. Use these properties to determine when to run code.
Note the below code only works with jQM 1.4.3
$(document).on("pagecontainershow", function (e, ui) {
var previous = $(ui.prevPage),
next = $(ui.toPage);
if (previous[0].id == "pageX" && next[0].id == "menuPage") {
/* do something */
}
});
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 a site I'm working on which uses ajax to load page segments when js is available - this used to work in all browsers, the only problem being that the browser history was only updated in HTML5 broswers.
I have recently been doing a lot of work on the site and most recently decided to try to sort the history out for html4 browsers, in doing so I have checked the site in IE and found that a problem has developed with the way the data from some of the ajax calls is being displayed (only affecting IE - not FF).
The URLS are structured as follows:
sitename.dev/main_category/sub_category
if I just enter sitename.dev/main_category everything works fine but if I click the link for one of the sub categories at sitename.dev/main_category/sub_category the results are loaded but not displayed properly in IE. If I type the address into the browser for hard reload all works fine
WORKING (through hard reload):
Not working through ajax:
Because it is ajax loaded content I cannot inspect the dom, but my function appears to be firing correctly and the ajax call is returning the correct results.
Here is the code for my ajax call
function leftsort_click(event) { //main sort click (left menu)
var page = window.name, page_index,
api = $('#right_pane').jScrollPane({
showArrows: true,
maintainPosition: false
}).data('jsp');
if (!$(this).hasClass('sort_cat')) {
$('ul.sort_ul li, ul.cat_items li').removeClass('active');
$(this).addClass('active');
var sid = $(this).attr('id');
var title = $(this).html();
var loadUrlx= page;
if ((sid != '') && (sid != 'undefined')) {
loadUrlx += '/'+sid;
}
if($('.rpp_btn.active').length>=1){
var res_per_page = $.trim($('.rpp_btn.active').html());
page_index = $.trim($('.res_page_select.active a').html());
if (($('.rpp_btn.active').hasClass('just_clicked'))||(!$('.res_page_select').hasClass('just_clicked'))) {
page_index = '1';
}
if ((page_index != 1) || (res_per_page != 25)) {
loadUrlx += '/' + page_index + '/' + res_per_page;
}
$('.rpp_btn, .res_page_select').removeClass('just_clicked');
}
loadUrlx = b_url + loadUrlx;
if (History.enabled) {
History.pushState(null, null, loadUrlx);
}
$.ajaxSetup({
cache: false
});
$("#result_area").load(loadUrlx, function() {
$("#result_table").trigger("update");
api.reinitialise();
tsizer();
});
}
}
Any help or suggestions would be very much appreciated
worked this one out, i was injecting a div into a tbody - Ie's not happy about that.