My problem is that sometimes - when a user enters and loads the lightning page for the first time, I get an error that is caused by inheritance order of execution:
I have one base component that has extensible="true"
in it:
VisualField markup:
<aura:component extensible="true"
controller="VisualField_CTRL"
implements="force:hasRecordId,force:hasSObjectName"
description="Interface for a visual component">
<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
<aura:attribute name="sObjectName" type="String" description="sObject api name to control the field. Taken from force:hasSObjectName" />
<aura:attribute name="recordId" type="String" description="Id of the current record. Taken from force:hasRecordId" />
<aura:attribute name="fieldName" type="String" description="Visual field api name to control." />
VisualField controller:
({
doInit: function(component, event, helper) {
helper.getSelectedValue(component);
}
})
VisualField helper:
({
getSelectedValue: function(component){
// get the object and record data from the record home
var sObjectName = component.get("v.sObjectName");
var recordId = component.get("v.recordId");
var fieldName = component.get("v.fieldName");
// get the server side function and add params to it
var action = component.get("c.getVisualField");
action.setParams({
sObjectApiName : sObjectName,
recordId : recordId,
fieldName : fieldName
});
action.setCallback(this, function(response){
var state = response.getState();
if (component.isValid() && state === "SUCCESS") {
var record = response.getReturnValue();
if (record) {
// set the new value
component.set("v.fieldValue", record.fieldValue);
// show the selected value - call inherited method
component.getConcreteComponent().showSelected(record.fieldValue);
}
else {
alert("VisualField Failed to get visualField record.");
}
}
else {
alert(response.getError());
}
});
$A.enqueueAction(action);
}
})
I have also a second component that inherits the base component (extends="c:VisualField"
) and uses jQuery Raty - A Star Rating Plugin:
Rating Markup:
<aura:component extends="c:VisualField"
implements="flexipage:availableForAllPageTypes"
description="Controls a field with visual stars">
<ltng:require styles="{!$Resource.Rating + '/css/jquery.raty.css'}"
scripts="{!join(',',
$Resource.Rating + '/js/jquery.js',
$Resource.Rating + '/js/jquery.raty.js')}"
afterScriptsLoaded="{!c.loadRaty}"/>
<!--this method should be called from the super-component-->
<aura:method name="showSelected" action="{!c.showSelected}">
<aura:attribute name="selectedValue" type="String" />
</aura:method>
<aura:attribute type="String" name="currentValue"/>
<aura:attribute type="String" name="newValue" default="0"/>
<div>
<div aura:id="starRating" />
</div>
</aura:component>
Rating Controller:
({
// load raty rating jquery plugin.
loadRaty : function(component, event, helper) {
helper.initRatingElement(component);
},
// comes from aura:method name="showSelected"
showSelected: function(component, event, helper) {
if (!event)
return;
// get the component and the selected value from the event
var params = event.getParam('arguments');
if (params) {
// show selected stars
var newValue = params.selectedValue;
helper.setStars(component, newValue);
}
},
})
Rating Helper:
({
initRatingElement: function(component){
var ratingElement = component.find("starRating").getElement();
var helper = this;
$( ratingElement ).raty({
number : 5,
starOff : '/resource/Rating/images/star_off_darkgray--small.png',
starOn : '/resource/Rating/images/star_on--small.png',
single : false,
// load the click event and function
click : function(score, evt) {
if(score == null )
score = 0;
var newValue = score.toString();
if(component.get("v.currentValue") != newValue ){
component.set("v.newValue", newValue);
}
}
});
},
setStars: function(component, selectedValue) {
// save value as current
component.set("v.currentValue", selectedValue);
// show on the component
var ratingElement = component.find("starRating").getElement();
// check if raty has been loaded, if not - load it first:
if (!(typeof ($(ratingElement).raty) === "function")) {
// load raty first:
this.initRatingElement(component);
}
$(ratingElement).raty('score', selectedValue);
}
})
- Rating component loads jquery and raty plugins and after they load, it initiates the raty plugin.
- base VisualField component on it's init gets values from server side to see and set the selected value.
- when the server side function in the base component finishes to load, it calls the
showSelected
method in the inherited componentRating
. - the
showSelected
method in the inherited componentRating
executes thesetStars
helper method that uses the jQuery and Raty plugins$(ratingElement).raty('score', selectedValue);
this call fails sometimes when the page first loads with the error message:
Uncaught Action failed: c:Rating$controller$showSelected [$ is not defined] Callback failed: apex://VisualField_CTRL/ACTION$getVisualField throws at https://mycompany.lightning.force.com/auraFW/javascript/YKnqxnHX5EzqqKoVZYsoZQ/aura_prod.js:2:15 Object.setStars()@components/c/Rating.js:62:9 showSelected()@components/c/Rating.js:22:20
I believe this happens because the function is called from the base component and the jQuery and Raty plugins are not loaded from there and still did not load from the inherited Rating component.
Can anyone help me fix this very annoying problem?
thanks
Itai