function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Timothy BarnardTimothy Barnard 

afterScriptsLoaded interrupting init function

Hi,

I'm attempting to load images into a carousel. I make a call to the server to retrieve the records, and then set my Video attribute which is of type list with the videos. After this, I use an aura iteration to load the carousel.

My issue is, is that the Javascript hits line 6, then 23, then comes back to the server call on line 13. This disrupts the loading of the carousel and in turn just loads them in line and not into the carousel. How do I ensure the video list is loaded fully to allow the carousel to load properly in this case?

JS Controller:
// Sets videoObject component attribute w/ record data
    getVideoObject: function(component){
       
        var action = component.get("c.getCurrVideo");
        console.log('line 6', action);
        action.setParams({
            recId : component.get("v.recordId")
        });
        
        action.setCallback(this, function(response){
            var state = response.getState();
            console.log('line 13', state);
            if (state === "SUCCESS") {
                component.set('v.Video', response.getReturnValue());
            }
        });
        $A.enqueueAction(action);
       
    },
    
	afterScriptsLoaded : function(component, event, helper) {
        imageSliderComponent = component.find('ImageSlider');
        console.log('line 23', imageSliderComponent);
        $(imageSliderComponent.getElement()).slick({
            accessibility : true,
            autoplay : true,
            dots: true,
            infinite: true,
            speed: 300,
            slidesToShow: 4,
            adaptiveHeight: true
        });
        $A.util.removeClass(component, 'slds-hide');
	},
Aura iteration located in the component:
<aura:iteration items="{!v.Video}" var="item">
                <figure class="slds-image slds-image--card">
                    <a href="javascript:void(0);" class="slds-image__crop slds-image__crop--16-by-9">
                        <img src="{!item.Thumbnail_URL__c}" alt="Description of the image" />
                    </a>
                </figure>
            </aura:iteration>

 
Best Answer chosen by Timothy Barnard
Alain CabonAlain Cabon
Just try this now that could work.

$A.run(function() : is deprecated.
 
({
    // Sets videoObject component attribute w/ record data
    getVideoObject: function(component,event,helper){        
        var action = component.get("c.getCurrVideo");
        console.log('line 6', action);
        action.setParams({
            recId : component.get("v.recordId")
        });      
        action.setCallback(this, function(response){
            var state = response.getState();
            console.log('line 13', state);
            if (state === "SUCCESS") {
                component.set('v.Video', response.getReturnValue());  
                // helper.reloadMethod(component);
                setTimeout(function() {
                   // $A.run(function() { 
                        var imageSliderComponent = component.find('ImageSlider');
                        console.log('line 23', imageSliderComponent);
                        if (imageSliderComponent) {
                            var mySlider = imageSliderComponent.getElement();
                            console.log('line 26', mySlider);
                            
                            $(mySlider).slick({
                                accessibility : true,
                                autoplay : true,
                                dots: true,
                                infinite: true,
                                speed: 300,
                                slidesToShow: 3,
                                adaptiveHeight: true
                            });
                            
                        }       
                  // });
                }); 
            }
        });
        $A.enqueueAction(action);       
    },   
    afterScriptsLoaded : function(component, event, helper) {
      // empty now
    },
})

Alain
 

All Answers

Alain CabonAlain Cabon
Hi,

Did you solve your problem?

I will made some tests but that is quicker when I have more code (version of jQuery and so on).
 
Timothy BarnardTimothy Barnard
Hi Alain,

I have not solved the problem yet, the images are still stacking on top of one another and the carousel is not appearing on the page. The carousel loads without the server call, for example, it loads static resource images. But when it has to make the server call it gets thrown off. 
Alain CabonAlain Cabon
Ok, could you at least copy/paste some more parts of your code (css and js tool library versions)?

Other people could help you but for very tricky problem like this one, it is better to have almost all the code.

I solved another bizarre problem here: https://developer.salesforce.com/forums/ForumsMain?id=9060G0000005VaQQAU
But no news. That works now and Angello copied/pasted all his (not-)working code so it is much more easier to start. 
Alain CabonAlain Cabon
Hello Timothy,

I have used the standard <lightning:carousel​> with scrollDuration="2" seconds

https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/aura_compref_lightning_carousel.htm

The images come from here:  https://trailhead.salesforce.com/en/superbadges/superbadge_lcf
( Install the unmanaged package.)
The Lightning Component Framework Specialist​ is my favorite Superbadge (really great).

Salesforce has made great progress for its standard components (even if it is beta, it is now often more stable and easier to use than external JS libraries).

User-added image

Application
<aura:application  extends="force:slds">
    <c:MyCaroussel />
</aura:application>

Component: :MyCaroussel
<aura:component controller="MyCarousselServer" implements="flexipage:availableForAllPageTypes,force:hasRecordId" access="global" >
    <aura:handler name="init" value="{!this}" action="{!c.getVideoObject}"/>
    <aura:attribute  name="Video" type="List" />   
    <lightning:layout horizontalAlign="center">
        <div style="width:25%;">
            <lightning:carousel title="My Carousel" scrollDuration="2">         
                <aura:iteration items="{!v.Video}" var="item"  >            
                    <lightning:carouselImage  
                                             src = "{!$Resource.Sailboats +'/' + item.Thumbnail_URL}"
                                             header = "{!item.Description}" 
                                             description = "{!item.Thumbnail_URL}"
                                             alternativeText = "This is a card">
                    </lightning:carouselImage>                
                </aura:iteration>
            </lightning:carousel>
        </div>  
    </lightning:layout>
</aura:component>

Controller JS:
({
    getVideoObject: function(component,event,helper){       
        var action = component.get("c.getCurrVideo");
        action.setParams({
            recId : component.get("v.recordId")
        });       
        action.setCallback(this, function(response){
            var state = response.getState();
            if (state === "SUCCESS") {
                component.set('v.Video', response.getReturnValue());                
            }
        });
        $A.enqueueAction(action);        
    }
})

Controller Apex:
 
public class MyCarousselServer { 
    @AuraEnabled
    public static List <Video> getCurrVideo(String recId){  
        String[] boats = new String[] {'sailboat1.png',  'Beau sailbeat1',
            'sailboat2.png', 'Beau sailbeat2',
            'skiboat1.png','Super skiboat1',
            'skiboat2.png', 'Super skiboat2',
            'yacht1.png','Super yacht1',
            'yacht2.png','Super yacht2'};                 
                
             // not used here
            system.debug('rec Id:' + recId);
        
        List <Video> th = new  List <Video>();
        for(integer i=0;i< boats.size()/2;i++) {
            Video v = new Video();
            v.Thumbnail_URL = boats[i*2]; 
            v.Description = boats[i*2+1]; 
            th.add(v);
        }     
        return th;
    }
    public class Video {
        @AuraEnabled
        public String Thumbnail_URL {get;set;}
        @AuraEnabled
        public String Description {get;set;}
    }
}

Alain
Timothy BarnardTimothy Barnard
Hi Alain,

The main reason I am unable to use the standard <lightning:carousel> is that it only supports displaying one image at a time. I need to support 4 images on screen at a time, hence the use of the Slick Carousel.

The solution cannot pull the images from static resources, as the images are hosted on an external system and are referenced using the URL. I attempted implementing your solution but had the same result when using Thumbnail_URL__c coming from a record.

Here's my Apex controller code:
public with sharing class ImageSliderController {
    @AuraEnabled
    public static List<Video__c> getCurrVideo(Id recId){
        
        System.debug('recid = ' + recId);
        Video__c[] o = [SELECT Id, Topics__c FROM Video__c WHERE Id=:recId LIMIT 1];
        System.debug('Size of list should be 1 = ' + o.size());
        //String value = (String) o.get('Topics__c');
        String value2 = o[0].Topics__c;
        System.debug('Record Value = ' + value2);
        
        List<Video__c> videoList = [SELECT Id, Name, Description__c, Video_URL__c, Thumbnail_URL__c, Topics__c,  
                                    Primary_Video__c, CreatedDate, View_Count__c, Number_Order__c
                                    FROM Video__c WHERE Topics__c=:value2 AND Id!=:recId ORDER BY Number_Order__c ASC];
        
        System.debug('Size of related video list = ' + videoList.size());
        
        return videoList;
        
    }
    public class Video {
        @AuraEnabled
        public String Thumbnail_URL {get;set;}
        @AuraEnabled
        public String Description {get;set;}
    }
}

And lightning component:
<aura:component implements="flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,forceCommunity:availableForAllPageTypes" controller="ImageSliderController" access="global" >
    
    <ltng:require scripts="{!join(',', $Resource.ImageSlider + '/jquery.js',
                           $Resource.ImageSlider + '/slick.js')}"
                  styles="{!join(',', $Resource.ImageSlider + '/slick.css',
                          $Resource.ImageSlider + '/slick-theme.css')}"
                 afterScriptsLoaded="{!c.afterScriptsLoaded}"/>

    <aura:attribute name="Video" type="Video__c[]"/>
    <aura:attribute name="flag" type="boolean" default="false"/>
    <aura:attribute name="helpFlag" type="boolean" default="false"/>

    <div aura:id="ImageSlider" class="slds-m-horizontal--large slds-hide">
            <aura:iteration items="{!v.Video}" var="item">
                <div>
                <figure class="slds-image slds-image--card">
                    <a href="javascript:void(0);" class="slds-image__crop slds-image__crop--16-by-9">
                        <img src="{!item.Thumbnail_URL__c}" alt="Description of the image" />
                    </a>
                </figure>
                    </div>
            </aura:iteration>
    </div>
</aura:component>

 
Timothy BarnardTimothy Barnard
Hi Alain,

Above I have posted the component and Apex controller. I'm testing with the component on a community. How did you figure out that ltng:require was not working as necessary and how did you resolve that issue?

JS: jQuery 3.1.1, Slick 1.6
CSS: slick.css & slick-theme.css

JS Controller:
({

    afterScriptsLoaded : function(component,event,helper) {
        console.log('hit');
        var imageSliderComponent = component.find('ImageSlider');
    	
        console.log('line 4 help', imageSliderComponent);
        $(imageSliderComponent.getElement()).slick({
            accessibility : true,
            autoplay : true,
            dots: true,
            infinite: true,
            speed: 300,
            slidesToShow: 4,
            adaptiveHeight: true
        });
    component.set('v.helpFlag', true);
        helper.getVideoObject(component);
        $A.util.removeClass(component, 'slds-hide');
	},
    
    // Sets videoObject component attribute w/ record data
    getVideoObject: function(component, event, helper){
        var action = component.get("c.getCurrVideo");
        console.log('line 6', action);
        action.setParams({
            recId : component.get("v.recordId")
        });
        
        action.setCallback(this, function(response){
            var state = response.getState();
            console.log('line 13', state);
            if (state === "SUCCESS") {
                component.set('v.Video', response.getReturnValue());
                console.log('23');
                component.set('v.flag', true);
            }
        });
        $A.enqueueAction(action);
        
    },
    itemsChange: function(cmp, evt) {
        console.log("numItems has changed");
        console.log("old value: " + evt.getParam("oldValue"));
        console.log("current value: " + evt.getParam("value"));
    }
    
})


 
Timothy BarnardTimothy Barnard
With your afterScriptsLoaded code, the carousel loads, but it is empty.
Alain CabonAlain Cabon
I reproduce your problem when there is an access to the database indeed.

Only <lightning:carousel> works all the time. 

 jQuery.noConflict();   and   jQuery(document).ready(function() are not enough.
Alain CabonAlain Cabon
The good trick should be this one.

Lightning Components: Creating a Carousel Component  By Christophe Coenraets (June 15, 2015)
https://developer.salesforce.com/blogs/developer-relations/2015/06/creating-carousel-lightning-component.html

setTimeout(function() {
     $A.run(function() { $('.carousel').slick(); });
});

  // prevent default "pull-to-refresh" behavior when running in S1
   $('.carousel').on("touchmove", function()
   { return false;
   });

 
<aura:component controller="CandidateController" implements="force:appHostable">

    <ltng:require styles="/resource/recruiting/slick/slick.css,/resource/recruiting/slick/slick-theme.css"	
                  scripts="/resource/recruiting/jquery/jquery.js,/resource/recruiting/slick/slick.min.js"
        afterScriptsLoaded="{!c.doInit}"/>
    
	<aura:attribute name="contacts" type="Contact[]"/>

    <div class="carousel">
		<aura:iteration items="{!v.contacts}" var="contact">
            <c:CandidatePage contact="{!contact}"/>
        </aura:iteration>
	</div>
    
</aura:component>

({
	doInit : function(component, event, helper) {
		var action = component.get("c.findAll");
		action.setParams({
            "pageNumber": 1
    	});
        action.setCallback(this, function(a) {
            var result = a.getReturnValue();
            console.log(result);
            component.set("v.contacts", result.contacts);
            setTimeout(function() {
                $A.run(function() {
					$('.carousel').slick();
                });
            });
            // prevent default "pull-to-refresh" behavior when running in S1
            $('.carousel').on("touchmove", function() {
                return false;
            });
        });
        $A.enqueueAction(action);
	}
})


 
public with sharing class CandidateController {

    @AuraEnabled
    public static ContactPagedResult findAll(Decimal pageNumber) {
        Integer pageSize = 16;
        Integer offset = ((Integer)pageNumber - 1) * pageSize;
        
        ContactPagedResult r =  new ContactPagedResult();
        r.pageSize = pageSize;
        r.page = (Integer) pageNumber;
        r.total = [SELECT count() FROM contact 
                      WHERE PICTURE_URL__C != null];
        r.contacts = [SELECT Id, FirstName, LastName, MobilePhone, MailingCity, MailingState, Picture_URL__c FROM contact 
                      WHERE PICTURE_URL__C != null
                      LIMIT :pageSize OFFSET :offset];
        return r;
    }

}

Alain
Alain CabonAlain Cabon
Just try this now that could work.

$A.run(function() : is deprecated.
 
({
    // Sets videoObject component attribute w/ record data
    getVideoObject: function(component,event,helper){        
        var action = component.get("c.getCurrVideo");
        console.log('line 6', action);
        action.setParams({
            recId : component.get("v.recordId")
        });      
        action.setCallback(this, function(response){
            var state = response.getState();
            console.log('line 13', state);
            if (state === "SUCCESS") {
                component.set('v.Video', response.getReturnValue());  
                // helper.reloadMethod(component);
                setTimeout(function() {
                   // $A.run(function() { 
                        var imageSliderComponent = component.find('ImageSlider');
                        console.log('line 23', imageSliderComponent);
                        if (imageSliderComponent) {
                            var mySlider = imageSliderComponent.getElement();
                            console.log('line 26', mySlider);
                            
                            $(mySlider).slick({
                                accessibility : true,
                                autoplay : true,
                                dots: true,
                                infinite: true,
                                speed: 300,
                                slidesToShow: 3,
                                adaptiveHeight: true
                            });
                            
                        }       
                  // });
                }); 
            }
        });
        $A.enqueueAction(action);       
    },   
    afterScriptsLoaded : function(component, event, helper) {
      // empty now
    },
})

Alain
 
This was selected as the best answer
Timothy BarnardTimothy Barnard
Hi Alain,

Happy to report that that latest controller code works. Thank you for all of your help! It is much appreciated.

Thanks,
Tim