I have a DNN module that will build a product Carousel using Lightslider.
When the page loads, it takes a couple of seconds to get 10 products from our ERP server database. While this is loading, when I try to navigate to another page, it won't redirect until the Ajax query has returned data. It will wait until the products have loaded, and then it will redirect to the link I clicked on about 5 seconds ago.
I have enabled async: true and tried to add async to the Server-side code, but no luck.
$(document).ready(function () {
GetCarouselProductData();
});
async function GetCarouselProductData() {
var CarouselModID = "<%=CarouselModID%>";
var CarouselMode = "<%=CarouselMode%>";
var CarouselURL = $.fn.GetBaseURL() + 'DesktopModules/PRandomProductCarousel/API/RandomProductCarousel/GetRandomCarouselProductsSQLAsync?ModID=' + CarouselModID;
if (CarouselMode == 'SpecificSKU') {
CarouselURL = $.fn.GetBaseURL() + 'DesktopModules/PRandomProductCarousel/API/RandomProductCarousel/GetSpecificSKUCarouselProductsAsync?ModID=' + CarouselModID;
}
await $.ajax({
type: "GET",
contentType: "application/json; charset=utf-8",
cache: false,
url: CarouselURL,
datatype: "json",
async: true,
success: function (data) {
var counter = 0;
var VatText = "<%=IncTaxText%>";
var DetailedLinkRedirect = "<%=DetailedPageLink%>/sku/";
if (data.length > 0) {
var source = "";
$.map(data, function (item) {
var ImageSRC;
if (item.ProductImages.length > 0) {
ImageSRC = item.ProductImages[0].ProductThumbUrl
} else {
ImageSRC = '/DesktopModules/RandomProductCarousel/Images/no_image.png';
}
var alphabet = (counter + 10).toString(36);
var TitleDescription = item.Title;
if (TitleDescription.length > 70) {
var NewTitle = TitleDescription.substr(0, 70);
NewTitle = NewTitle.substr(0, NewTitle.lastIndexOf(" ")) + '...';
} else {
var NewTitle = TitleDescription;
}
var StockCode = item.StockCode.trim();
var FinalRedirectLink = DetailedLinkRedirect + StockCode;
source += "<li class=\"item-" + alphabet +"\">"+
"<div class='box'>"+
"<!--image box-------->"+
"<div class='slide-img'>"+
"<img src='" + ImageSRC + "' alt='" + item.Title + "' />"+
"<!--overlay-->"+
"<div class='overlay-carousel'>"+
"<!--buy btn---->" +
"<div class='overlay-inner-carousel-btn-positioning'><a href='javascript:void(0);' class='carousel-quote-btn' onclick=\"addQuoteWithOnClickFunction('" + item.StockCode + "', 1, '" + item.CustomerPriceInclVat + "');\">Add to Quotes</a></div>" +
"<div class='overlay-inner-carousel-btn-positioning'><a href='javascript:void(0);' class='carousel-buy-btn' onclick=\"addProductWithOnClickFunction('" + item.StockCode + "', 1, '" + item.CustomerPriceInclVat + "');\"><%=CartButtonText%> </a></div>" +
"<div class='overlay-inner-carousel-btn-positioning'>" +
"<a href='" + FinalRedirectLink + "' class='carousel-view-btn'>View</a>" +
"</div>" +
"</div>"+
"</div>"+
"<!--Detail box-->"+
"<div class='detail-box'>"+
"<!--type-->"+
"<div class='type'>"+
"<a href='" + DetailedLinkRedirect + item.StockCode + "'>" + NewTitle +"</a>"+
"</div>"+
"<!--price-->" +
"<div class='CarouselStockCode'>" +
"<span class='carousel-stock'>" + StockCode + "</span>" +
"</div>" +
"<span class='price'>" + item.CustomerPriceInclVat + " " + VatText + "</span>" +
"</div>"+
"</div>"+
"</li>";
counter++;
return source;
});
$("#autoWidth2").remove();
$(".lSSlideOuter").remove();
$(".responsive-product-carousel").remove();
$(".responsive-product-carousel2").append(source);
}
},
error: function (xhr, ajaxOptions, thrownError) {
$.fancybox.open(xhr.responseText);
}
}).promise().done(function () {
$(document).ready(function () {
//ZR - 29/01/2021 - Mode that determines if it should auto slide
var SliderMode = "<%=AutoSlideSetting%>";
if (SliderMode == "False") {
SliderMode = false;
} else {
SliderMode = true;
}
//ZR - 29/01/2021 - Sets the loop mode of the slider
var LoopMode = "<%=LoopSliderSetting%>";
if (LoopMode == "False") {
LoopMode = false;
} else {
LoopMode = true;
}
var SliderPauseTime = <%=SliderPausePeriod%>;
// website for settings sachinchoolur.github.io/lightslider/settings.html
$('#autoWidth').lightSlider({
autoWidth: true,
loop: LoopMode,
adaptiveHeight: true,
pauseOnHover: true,
auto: SliderMode,
pause: SliderPauseTime,
onSliderLoad: function () {
$('#autoWidth').removeClass('cS-hidden');
}
});
ViewCarouselProductLoading();
});
});
On the server-side of the Controller I have attempted to use the following two ways to get data, but it seems like it makes no difference.
Controller Code without Async Code (Test 1):
/// <summary>
/// Get Random Product Information via SQL
/// </summary>
/// <returns></returns>
/// <remarks>ZR 25/01/2021</remarks>
[AllowAnonymous]
[HttpGet]
public object GetRandomCarouselProductsSQLAsync(int ModID)
{
try
{
AmountOfRandomProductsToFetch = TryGetPortalSetting("AmountOfRandomProductsToLoad_" + ModID);
return GetRandomProductsFromSQL(Int32.Parse(AmountOfRandomProductsToFetch));
}
catch (Exception ex)
{
EventLogController logController = new EventLogController();
logController.AddLog("Could not retreive random products for the Product Carousel via SQL.", ex.ToString(), EventLogController.EventLogType.ADMIN_ALERT);
throw;
}
}
Controller Code with Async Code (Test 2):
/// <summary>
/// Get Random Product Information via SQL
/// </summary>
/// <returns></returns>
/// <remarks>ZR 25/01/2021</remarks>
[AllowAnonymous]
[HttpGet]
public async System.Threading.Tasks.Task<object> GetRandomCarouselProductsSQLAsync(int ModID)
{
try
{
return await GetRandomProductsSQLAsync(ModID);
}
catch (Exception ex)
{
EventLogController logController = new EventLogController();
logController.AddLog("Could not retreive random products for the Product Carousel via SQL.", ex.ToString(), EventLogController.EventLogType.ADMIN_ALERT);
throw;
}
}
public async System.Threading.Tasks.Task<object> GetRandomProductsSQLAsync(int ModID)
{
try
{
AmountOfRandomProductsToFetch = TryGetPortalSetting("AmountOfRandomProductsToLoad_" + ModID);
return GetRandomProductsFromSQL(Int32.Parse(AmountOfRandomProductsToFetch));
}
catch (Exception ex)
{
EventLogController logController = new EventLogController();
logController.AddLog("Could not retreive random products for the Product Carousel via SQL.", ex.ToString(), EventLogController.EventLogType.ADMIN_ALERT);
throw;
}
}
I have even attempted to do the Ajax call differently using "Then", but no luck.
$(window).load(function () {
console.log("window is loaded");
doAjaxGet()
.then(json => {
console.log('json: ', json);
processJSONData(json);
})
});
async function doAjaxGet() {
var CarouselModID = "<%=CarouselModID%>";
var CarouselMode = "<%=CarouselMode%>";
var CarouselURL = $.fn.GetBaseURL() + 'DesktopModules/PRandomProductCarousel/API/RandomProductCarousel/GetRandomCarouselProductsSQLAsync?ModID=' + CarouselModID;
if (CarouselMode == 'SpecificSKU') {
CarouselURL = $.fn.GetBaseURL() + 'DesktopModules/PRandomProductCarousel/API/RandomProductCarousel/GetSpecificSKUCarouselProductsAsync?ModID=' + CarouselModID;
}
const result = await $.ajax({
url: CarouselURL,
crossDomain: true,
async: true,
type: 'GET',
});
return result;
}
function processJSONData(data) {
// NOTE: data is already parsed
$.map(data, function (item) {
console.log('Data: ', item.Title);
});
}
UPDATE
I have changed the Front-End Code to use a normal XMLHttpRequest instead of JQuery Ajax. I have two videos below to show what it is exactly doing and the Javascript Ajax code below that.
Video 1 (Build Product Carousel)
https://www.youtube.com/watch?v=n637FGv3e9U
Video 2 (Log Product Titles only to Console Log)
https://www.youtube.com/watch?v=8mHNcgBoe-Q
Javascript Ajax Code:
$(document).ready(function () {
JavascriptCarouselFetch();
});
function JavascriptCarouselFetch() {
var CarouselModID = "<%=CarouselModID%>";
var CarouselURL = $.fn.GetBaseURL() + 'DesktopModules/ParrotRandomProductCarousel/API/RandomProductCarousel/GetRandomCarouselProductsSQLAsync?ModID=' + CarouselModID;
var http = new XMLHttpRequest();
http.open("GET", CarouselURL, true);
http.send();
http.onreadystatechange = function () {
if (http.readyState == 4 && http.status == 200) {
$.map(JSON.parse(http.response), function (item) {
console.log("Data: " + item.Title);
})
}
};
Controller Code:
/// <summary>
/// Get Random Product Information via SQL
/// </summary>
/// <returns></returns>
/// <remarks>ZR 25/01/2021</remarks>
[AllowAnonymous]
[HttpGet]
public async System.Threading.Tasks.Task<object> GetRandomCarouselProductsSQLAsync(int ModID)
{
try
{
AmountOfRandomProductsToFetch = TryGetPortalSetting("AmountOfRandomProductsToLoad_" + ModID);
return GetRandomProductsFromSQL(Int32.Parse(AmountOfRandomProductsToFetch));
}
catch (Exception ex)
{
EventLogController logController = new EventLogController();
logController.AddLog("Could not retreive random products for the Product Carousel via SQL.", ex.ToString(), EventLogController.EventLogType.ADMIN_ALERT);
throw;
}
}
/// <summary>
/// Gets Random Products from SQL
/// </summary>
/// <param name="ProdCode"></param>
/// ZR 25/01/2021</remarks>
public object GetRandomProductsFromSQL(int AmountOfRandomProductsToFetch)
{
try
{
if (AmountOfRandomProductsToFetch > 0)
{
int pid = PortalController.Instance.GetCurrentPortalSettings().PortalId;
if (SessionManager.GSettings == null) SessionManager.GSettings = new ParrotDNNCommon.Components.GlobalSettings(pid);
var portalCtrl = new ParrotDNNCommon.Controllers.ParrotPortalSettingsController();
var parrotPortalController = new ParrotPortalSettingsController();
var currSettings = parrotPortalController.GetPortalSettings(pid);
return m_Product.GetRandomProductWebDetailsFromSQL(SessionManager.GSettings.Globalvars, pid, AmountOfRandomProductsToFetch, currSettings.PricingModeIsShowRRPOnly);
}
return null;
}
catch (Exception exc)
{
throw;
}
}
Common Class
/// <summary>
/// Get the product details
/// </summary>
/// <param name="GV">Global Variables</param>
/// <param name="WebPortalId">Web Portal ID</param>
/// <param name="Count">Amount of random products to return</param>
/// <param name="ShowRRPOnly">Boolean to determine if RRP should only show or not</param>
/// <returns>WebProductDetails</returns>
/// <remarks>ZR - 25/01/2021</remarks>
public List<CommonDataDefinitions.Products.WebProductDetails> GetRandomProductWebDetailsFromSQL(CommonLibrary.Globalvars GV, int WebPortalId, int Count, bool ShowRRPOnly)
{
List<CommonDataDefinitions.Products.WebProductDetails> result = new List<CommonDataDefinitions.Products.WebProductDetails>();
try
{
result = GV.ClHelper.eBusiness.GetRandomProductWebDetails(WebPortalId, SessionManager.CurrentUserInfo.CustomerCode, ShowRRPOnly, Count, SessionManager.CurrentUserInfo.ParrotUserId);
}
catch (Exception ex)
{
EventLog.LogEvent("GetProductWebDetails", "There was an error retrieving the product details. " + ex.Message, DotNetNuke.Services.Log.EventLog.EventLogController.EventLogType.ADMIN_ALERT);
}
return result;
}
ERP Code
''' <summary>
''' Gets a Random Collection of <see cref="WebProductDetails"/> and pricing for the website.
''' </summary>
''' <param name="WebPortalId"><see cref="Integer"/>: Target Web Portal Id.</param>
''' <param name="CustomerCode"><see cref="String"/>: Customer Code.</param>
''' <param name="ShowRRPOnly"><see cref="Boolean"/>: Show RRP Only.</param>
''' <param name="ProductCount"><see cref="Integer"/>: Count of Products to Return.</param>
''' <param name="WebUserId"><see cref="Integer"/>: Web User Id.</param>
''' <returns><see cref="List(Of WebProductDetails)"/></returns>
''' <remarks>KeS/ZR 25/01/2021: created.</remarks>
Friend Function GetRandomProductWebDetails(WebPortalId As Integer, CustomerCode As String, ShowRRPOnly As Boolean, ProductCount As Integer, WebUserId As Integer) As List(Of WebProductDetails)
Dim Result As List(Of WebProductDetails) = GetRandomProductDetailsForWeb(WebPortalId, CustomerCode, ShowRRPOnly, ProductCount, WebUserId)
Return Result
End Function
''' <summary>
''' Gets a Random Collection of <see cref="WebProductDetails"/> and pricing for the website.
''' </summary>
''' <param name="WebPortalId"><see cref="Integer"/>: Target Web Portal Id.</param>
''' <param name="CustomerCode"><see cref="String"/>: Customer Code.</param>
''' <param name="ShowRRPOnly"><see cref="Boolean"/>: Show RRP Only.</param>
''' <param name="ProductCount"><see cref="Integer"/>: Count of Products to Return.</param>
''' <param name="WebUserId"><see cref="Integer"/>: Web User Id.</param>
''' <returns><see cref="List(Of WebProductDetails)"/></returns>
''' <remarks>KeS/ZR 25/01/2021: created.</remarks>
Friend Function GetRandomProductDetailsForWeb(WebPortalId As Integer, CustomerCode As String, ShowRRPOnly As Boolean, ProductCount As Integer, WebUserId As Integer) As List(Of WebProductDetails)
Dim result As List(Of WebProductDetails) = New List(Of WebProductDetails)
Try
result = dao.FinishedGoods.GetRandomWebProductDetailsByCount(ProductCount)
If result.IsNullOrEmpty() Then Return New List(Of WebProductDetails)
'User Id < 0 is not logged in, recommended price will always be fetched
Dim canWebUserViewCustomerPrice As Boolean = If(WebUserId <= 0, True, CheckIfUserHasWebRight(UserRights.WebUserCanViewStockItemPrices, WebUserId))
'override the User Right if DNN site set to always show recommended price no matter what
If (ShowRRPOnly) Then canWebUserViewCustomerPrice = False
For Each product In result
'Get the customer pricing, DS 21/06/2017 - fixed to get correct Web channel default ID, now uses default obj for this
Dim priceCalc As StockItemPricingForCustomer = blHelper.Fingoods.GetStockItemPricingForCustomer(DefaultStockItemPricingForWebArgs(product.StockCode, CustomerCode, Not canWebUserViewCustomerPrice, dao.Customers.SelectCustomerDealerType(CustomerCode)))
'RB 2017-07-11: Get the Kit Items for the product
Dim CodeList As IList(Of String)
CodeList = New List(Of String)
CodeList.Add(product.StockCode)
Dim relatedProducts As List(Of RelatedProduct) = dao.DaoPortal.SelectKitItems(CodeList)
product.KitItems = relatedProducts
product.ShowStockAvailable = blHelper.SystemRules.WebShowStockAvailability()
product.NoStockAvailableMessage = blHelper.SystemRules.WebNoStockAvailableMessage()
product.RetailPriceExVat = priceCalc.RetailPrice
product.CustomerDiscount = priceCalc.CustomerDiscount
product.CustomerPriceExVat = priceCalc.CustomerPrice
product.CustomerPriceInclVat = priceCalc.PriceWithTax
product.ExtraInfo = dao.FinishedGoods.SelectDetailedDescriptionForStockCode(product.StockCode).DescriptionHTML
product.DownloadLinksHTML = blHelper.Fingoods.GenerateProductDownloadLinksHTML(String.Empty, product.StockCode, "line-height:20px;text-align:left;", GetCompanyIdFromDatabaseName(dao.dbName, WebPortalId))
product.ProductImages = blHelper.Fingoods.GetProdcutImageLinksOnly(String.Empty, product.StockCode, GetCompanyIdFromDatabaseName(dao.dbName, WebPortalId))
' RB 2017-10-11: Lifestyle data for the product StockParent_MetaData
Dim MetaData As StockParent_MetaData = dao.ProductVariations.SelectMetaData(product.StockID)
product.ShowWebInfoButton = MetaData.ShowAdditionalWebInfo
product.WebInfoURL = MetaData.AdditionalWebInfoURL
product.WebInfoTarget = MetaData.AdditionalWebInfoTargetDisplay
product.WebInfoButtonText = MetaData.AdditionalWebInfoButtonText
' Rest of MetaData can also be returned here
'DS 21/01/2019 - Fetch Individual Warehouse Stock Levels and set the new property (following the business rule for showing stock)
If product.ShowStockAvailable Then product.WarehouseStockLevels = LLDataUtils.ConvertDataTableToObjectListUsingIMapFromDataTableInterface(Of PortalWarehouseStockLevel)(dao.DaoPortal.SelectWarehouseStockLevelsForPortal(New String() {product.StockCode}, Nothing))
product.ItemsInStock = If(product.ShowStockAvailable, product.WarehouseStockLevels.Sum(Function(w) w.Amount), -1)
Next
Catch ex As Exception
CommonServer.ErrorHandler.ServerError(ex, "An error occurred attempting to retrieve a Random Assortment of Products for the Website", ExceptionTypes.UndefinedException, True)
End Try
Return result
End Function
''' <summary>
''' Get Random product details for the web for the provided count of items.
''' </summary>
''' <returns>WebProductDetails</returns>
''' <remarks>KeS/ZR 25/01/2021: created.</remarks>
Friend Function GetRandomWebProductDetailsByCount(Count As Integer) As List(Of WebProductDetails)
Dim sql As New StringBuilder
With sql
.AppendLine($" SELECT TOP {Count} SP.Id 'StockID', SP.StockCode 'StockCode', ISNULL(SP.FriendlyTitle, FG.Description) 'Title', '' as 'Description', FG.DetailedDescription 'ExtraInfo', ")
.AppendLine(" SP.SlugURL 'SlugURL', ISNULL(SP.MetaTitle, ISNULL(SP.FriendlyTitle, FG.Description)) 'MetaTitle', SP.MetaDescription 'MetaDesc', SP.MetaKeywords 'MetaKeywords', SP.MainProductImageTitle 'MainImageTitle', sp.MainProductImageDescription 'MainImageDesc', fg.BarCode, fg.PrimaryPublishingCategories_ID AS PrimaryPublishingCategoryID,")
.AppendLine(" FG.Price 'RetailPriceExVat', COUNT(ISNULL(W.QUANTITY, 0)) 'ItemsInStock', PC.Name AS PrimaryPublishingCategoryName ")
.AppendLine(" FROM StockParent SP ")
.AppendLine(" INNER JOIN Fingoods FG ON FG.Id = SP.Id AND FG.IsDeleted = 0 AND FG.Publish = 1")
.AppendLine(" LEFT JOIN WAREHOUSESTOCK W ON w.FinGoods_ID = SP.Id ")
.AppendLine(" LEFT JOIN PublishingCategories AS PC ON PC.ID = FG.PrimaryPublishingCategories_ID AND PC.IsDeleted = 0 ")
.AppendLine(" WHERE SP.IsDeleted = 0 ")
.AppendLine(" GROUP BY SP.Id, SP.StockCode, ISNULL(SP.FriendlyTitle, FG.Description), FG.DetailedDescription, SP.SlugURL, ISNULL(SP.MetaTitle, ISNULL(SP.FriendlyTitle, FG.Description)), SP.MetaDescription, SP.MetaKeywords, SP.MainProductImageTitle, sp.MainProductImageDescription, FG.Price, fg.BarCode, fg.PrimaryPublishingCategories_ID, PC.Name")
.AppendLine(" ORDER BY NEWID() ")
End With
Using cmd As SqlCommandHelper = GetCommand(sql.ToString)
Return cmd.ExecuteList(Of WebProductDetails)
End Using
End Function