Exploring Variable Types’ Functionality within the Lightning Component Framework
When constructing a component or application, the option to assign a specific type to an attribute arises. Yet, the manner in which Aura, the foundational framework of Lightning Components, utilizes this type remains a query. What advantages stem from opting for one type over another? Equally important, what potential pitfalls necessitate vigilance? Lastly, how does the chosen type’s selection influence the bidirectional communication of data to and from the server?
The act of selecting an appropriate type holds paramount importance when designing an API. Hence, delving into the underlying mechanics becomes imperative.
In this article, we will delve into the operations of components concerning four distinct variable types. The rationale for defining these types distinctly will also be explored. It’s assumed that readers possess a functional grasp of Lightning Components. If this domain is novel, embarking on the Lightning Basics Module within Trailhead is a recommended starting point.
Defining the Attribute Type
Considering Attribute Specifications
In the context of attribute specification, it’s crucial to keep in mind that despite composing your components using JavaScript and markup files, the Aura Framework itself is coded in Java. Consequently, metadata associated with your component, including its attribute types, undergoes a mapping process to align with the comprehensible constructs of the Java backend within Aura.
When confronted with the task of selecting a specific type for your component, a clear comprehension emerges: the chosen type corresponds to a Java interpretation of the type, not JavaScript. For instance, the term “Object” pertains to java.lang.Object
, distinct from JavaScript’s window.Object
. Similarly, “String” aligns with java.lang.String
, “Map” relates to java.util.Map
, and so forth.
Let’s initiate our exploration from the client perspective. An attribute is characterized by two intrinsic properties: its “name” and its “type.”
<!-- myCmp.cmp --> <aura:attribute name="myString" type="String"/> <aura:attribute name="myObject" type="Object"/> <aura:attribute name="myMap" type="Map"/> <aura:attribute name="myFacet" type="Aura.Component[]"/>
Let’s examine each attribute separately:
Attribute named “myString”
<aura:attribute name="myString" type="String"/>
Attribute labeled as “myObject”
<aura:attribute name="myObject" type="Object"/>
As previously mentioned, the types are being linked back to Java types. Hence, this specification designates that the value within this attribute holds the type java.lang.Object
, a potentially troublesome scenario. While the client within the Aura Framework might remain unaffected, issues can arise on the server side where the type Object
might not be appropriately converted into the expected data type.
To handle type conversion on the server, we rely on the Aura Converter Service, which facilitates the transformation of values from one type to another. However, when utilizing type="Object"
, this attribute tends to evade the conversion process, potentially leading to the presence of an incorrect data type. Although this example might compile and operate initially, it’s susceptible to breaking down in the future. If the intention is to employ a generic foundational type, it’s advisable to steer clear of using Object
, given its distinct interpretation in JavaScript compared to our Java Services. Instead, opt for the use of Map
.
Subsequent instances of how these complications can intensify will be presented in the forthcoming sections of this article.
Attribute named “myMap”
<aura:attribute name="myMap" type="Map"/>
In this instance, we’re employing “Map,” which effectively manages data conversions between the client and server. It serves as a key-value repository that represents an instantiation of window.Object
, rather than a direct JavaScript Map.
Attribute designated as “myFacet”
<aura:attribute name="myFacet" type="Aura.Component[]"/>
“Aura.Component[]” is a distinctive type within the framework, signifying that its contents should be regarded as components. This specific type of metadata is the sole reference point for the Aura Framework Client Runtime when determining how to manage your component’s processing.
Certain challenges to be mindful of include:
Avoid using “type=’Object'”: As we’ll delve into in more detail, employing type='Object'
doesn’t yield the outcomes you might anticipate. This choice tends to be the root cause of numerous bugs, so it’s advisable to minimize its usage whenever possible. In most cases, what you truly intended to use was type='Map'
.
Always define Aura.Component[]
as an array: Due to the constraints of the framework, specifying a type as Aura.Component
will not yield the expected results. Instead, it’s essential to consistently define it as Aura.Component[]
.
Lack of Set or Get validation: It’s important to note that there’s no validation in place for the data being inserted into or retrieved from an attribute. Any errors that arise will propagate downstream, impacting code that receives an unexpected data type.
Utilization of Types by the Client
When obtaining or assigning a type, it holds the potential to be of any nature.
component.set(“v.myFacet”, “Parker Harris”); component.set(“v.myMap”, Date.now()); component.get("v.myFacet");
In the Aura framework, there isn’t any inherent mechanism that raises concerns about the data types when obtaining or setting an attribute.
However, you must maintain an awareness of how these attributes are utilized. For instance, when rendering v.myFacet
to the page using an expression like {!v.myFacet}
, any modifications you’ve made to it will prompt the framework to attempt to display that new value on the page. If you’ve assigned a String to v.myFacet
instead of a Component, the Aura rendering service is likely to generate exceptions.
Type implications in default values: Types play a role in determining the nature of our default values. This is another context where employing type="Object"
can lead to complications.
Let’s consider our myCmp
component, with all its attributes populated using default values.
<!-- myCmp.cmp --> <aura:component> <aura:attribute name="myFacet" type="Aura.Component[]"> <div>A Div</div> </aura:attribute> <aura:attribute name="myObject" type="Object" default='{"blog":true}'/> <aura:attribute name="myMap" type="Map" default='{"blog":true}'/> <aura:attribute name="myString" type="String" default="42"/> <aura:handler name="init" value="{!this}" action="{!c.init}"/> </aura:component>
In this instance, we’ve defined values for every attribute. Now, let’s examine the actual composition of the component.
<!-- myCmpController.js --> ({ init: function(component, event, helper) { console.log(Array.isArray(component.get("v.myFacet"))); // true console.log(typeof component.get("v.myObject")); // string console.log(typeof component.get("v.myMap")); // object console.log(typeof component.get("v.myString")); // string } })
As evident, due to the utilization of type="Object"
, we’ve encountered an incorrect data type in v.myObject
. Contrary to expectations, it isn’t a JavaScript object. This discrepancy arises because a Java String is a subclass of Java Object, consequently, no conversion occurred at the server level.
However, v.myMap
was accurately converted, as we had specified on the server that it should be treated as a Java Map, which is subsequently converted into a suitable JavaScript object.
All other attributes exhibit the appropriate data associations.
Creation of Components on the Server
Despite the presence of types in Apex and Lightning Data Service, attribute types do not play a role in these contexts. They only exert their influence on the server when instances of your component are being generated. This facet remains a feature not directly accessible to developers working within the Lightning Component Framework. However, it’s a matter that merits your attention, as improper handling could potentially lead to numerous exceptions with unclear origins.
The three methods for implementing server-side component creation are as follows:
- $A.createComponent
- Embedding your component within a layout
- Applications
When the framework constructs an application for user presentation—whether it’s a custom application or the Salesforce Lightning Experience—it employs a form of server component creation. Beneath the surface, an instance of Aura.Component
is established on the server, mirroring the structure of your application. All attributes are loaded, and the rendering outcome of the page is determined.
For applications with attributes, these attributes can be specified via query parameters in the URL.
<!-- myApp.app --> <aura:application > <aura:attribute name="myFacet" type="Aura.Component[]"/> <aura:attribute name="myObject" type="Object"/> <aura:attribute name="myMap" type="Map"/> <aura:attribute name="myString" type="String" default="Blank"/> <aura:handler name="init" value="{!this}" action="{!c.init}"/> {!v.myString} </aura:application>
As an illustration, when you access /c/myApp.app?myString=Blog, the resulting page will display the single word “Blog.”
Now, let’s explore the outcome of specifying myObject
and myMap
in the URL:
/c/myApp.app?myObject={“blog”:true}&myMap={“blog”:true}
In this scenario, we’re indicating the attribute values using JSON, which will undergo conversion. However, upon examining their values in the controller, this is what we discover.
({ init : function(component, event, helper) { console.log(typeof component.get("v.myObject")); // String console.log(typeof component.get("v.myMap")); // Object } })
What accounts for these disparities?
Why do these variations exist? This can be attributed to the manner in which we constructed the application on the server. During this process, the values were extracted from the URL. Given that these values initiate as strings, they need to be transformed into the designated data types as defined by your attributes.
In the case of myObject
, an evaluation was performed to ascertain whether it qualified as a java.lang.Object
. Given that strings are considered objects (due to the all-encompassing nature of this concept), no conversion was undertaken.
Conversely, for myMap
, an assessment was conducted to determine if it qualified as a java.util.Map
. Recognizing that it did not satisfy this criterion, a conversion process employing the StringToMap
converter was executed on the server.
The usage of $A.createComponent
The process of creating components on the server should ideally be seamless for the user. It shouldn’t matter whether the component is instantly generated or if it needs to wait for server interaction.
However, it’s beneficial to understand that the server interaction can indeed occur, primarily because data type conversions are executed in such scenarios.
Let’s proceed to generate an instance of the previously defined component, myCmp.cmp
<!-- myAppController.js --> ({ init : function(component, event, helper) { $A.createComponent("c:myCmp", { myMap: <b>42</b> }, function(cmp, state) { console.log(state); // SUCCESS }) } })
When we lack all the necessary code to create the requested component instance, the provided code will initiate communication with the server.
On the server, as part of the process to determine the response content, an instance of the requested component is generated. During this phase, data conversions are conducted on the provided parameter values. This is the juncture where an “Unexpected Exception Occurred” might arise.
Irrespective of whether the server interaction occurs or not, if you inadvertently set myFacet
to 42 instead of myMap
, you’ll observe that the client raises concerns, regardless of the presence of markup://
. This is because myFacet
holds the type Aura.Component[]
, and these attribute types hold special significance within the life cycle stages of the Aura framework.
For more detailed information, refer to the additional documentation available on $A.createComponent
.
Incorporating your component within a layout
You might be aware that incorporating the interface flexipage:availableForAllPageTypes
enables you to integrate your component into various layouts within the application.
Furthermore, by including interfaces like force:hasRecordId
, you introduce an attribute that Salesforce’s specialized components will set on the server. In this scenario, once again, you’re dealing with Server Component Creation. The spectrum of challenges here is extensive and warrants a dedicated blog post. It’s crucial to exercise caution in utilizing the correct data types to steer clear of potential pitfalls.
In conclusion,
Key Takeaways:
- Validation doesn’t occur on the client side for type enforcement, allowing flexibility in setting attributes, while potential discrepancies can occur during retrieval.
- Types play a more substantial role on the server, particularly in scenarios like default values and Server Component Creation through apps or
$A.createComponent
. type="Object"
can lead to unintended consequences in Server Component Creation and testing, makingtype="Map"
a more prudent choice.- With
Aura.Component[]
, default values in attributes can represent more components. - This encapsulates the translation of types between the client and server in Lightning Components. While component code can implement type checking, the framework APIs like
component.set()
orevent.setParams()
do not validate parameter types against what’s passed in. - Opting for appropriate data types aids in preventing bugs during server attribute serialization. Therefore, the selection should be deliberate, favoring alternatives to
Object
, such asMap
.