In my series of component-based posts, I have another small, but useful, component – an auto resizing text area.
Salesforce Lightning Dynamic Text Area
For some of my other component based blogs, look here:
- How to build a Date Picker
- How to build a Multi Select Component
- How to build a Drag and Drop Duelling Picklist
This time, I’m going to show you how to create an Auto Sizing Text Area.
Background
To build this component, I used a lot of basic HTML and JavaScript as well as just a little Lightning.
A key part of the component is binding its data properly. This means it can be used as easily as any other built-in Lightning Component (no special events needed, just bind to the value attribute).
Important features of this component:
- Debounced data binding – this means that the component won’t send out a flood of notifications on changes; it will send out a rate limited update stream.
- No form divs are wrapped around this component because this sometimes proves difficult to manage if not in a form. It’s easy to put in a regular slds form though.
- No third party static resources used.
Method
Start by defining a Generic ComponentEvent – this has two attributes, TYPE and DATA (I use it for most events in my systems). I use it for onfocus and onclick event handling.
<aura:event type="COMPONENT" description="General change event" >
<aura:attribute name="type" default="DATA_CHANGE" type="string" description="can be anything - use this to verify that the data you receiving is what you expect"/>
<aura:attribute name="data" default="{}" type="Object" description="Object holding changed data"/>
</aura:event>
The actual markup of the component is quite small – just a text area with all requisite bindings and methods. You can see the two events that the component emits and also note the use of the Lightning `GlobalId`
Which is a special feature of Lightning and is replaced with an id that is Global to THIS component, but unique across all components. This means we won’t get id collisions if we have multiple text areas.
<aura:component>
<!--some elements omitted-->
<aura:method name="recalculateHeight" action="{!c.recalculateHeight}"/>
<aura:registerEvent name="onfocus" type="c:ComponentEvent" />
<aura:registerEvent name="onclick" type="c:ComponentEvent" />
<textarea aura:id="simpletextarea" value="{!v.value}" id="{!GlobalId + '-textarea'}" readonly="{!v.readonly}" tabindex="0" onfocus="{!c.handleOnFocus}" onclick="{!c.handleOnClick}" oninput="{!c.handleOnChange}" class="{! v.value.length gt 1 ? (v.class + ' dynamic-textarea slds-textarea') : (v.class + ' dynamic-textarea-empty slds-textarea') }" />
</aura:component>
The controller code contains most of the logic. I’ll explain it method by method:
`onRender` is the init routine of this component. I used `onRender` as an init event because I needed window height, which is not available on init.
onRender: function(component, event, helper) {
if (component.get("v.rendered")) {
return;
}
component.set("v.rendered", true);
helper.setHeight(component);
},
`focus` allows focus to be set via an `aura:method` from outside the component. This then triggers the following method, `handleOnFocus`.
focus: function(component, event, helper) {
var el = document.getElementById(component.getGlobalId() + '-textarea');
el.focus();
},
`handleOnFocus` handles input and sets the focus ring. This also allows the text area to resize via css that targets the `:focus` pseudo class.
handleOnFocus: function(component, event, helper) {
component.getEvent("onfocus").fire();
},
`getValue` returns the value of the text area – note the use of the `GlobaId`.
getValue: function(component, event, helper) {
var el = document.getElementById(component.getGlobalId() + '-textarea');
return el.value;
},
`handleOnChange` fires on every change to the text area, but is debounced to prevent too many screen updates / events being emitted.
handleOnChange: function(component, event, helper) {
var el = document.getElementById(component.getGlobalId() + '-textarea');
component.set("v.value", el.value);
var timer = component.get("v.storedTimer");
var timeout = 20;
if (timer) {
window.clearTimeout(timer);
}
timer = window.setTimeout(
$A.getCallback(function() {
helper.setHeight(component);
}), timeout
);
component.set("v.storedTimer", timer);
},
Finally, as you can see in the `handleOnChange` method, the autoresize part of the component is triggered in the helper class. This is the `setHeight` method.
This method measures the scroll height of the text area, adds a small buffer and resizes the text area to that.
setHeight: function(component) {
var el = document.getElementById(component.getGlobalId() + '-textarea');
// compute the height difference which is caused by border and outline
var outerHeight = parseInt(window.getComputedStyle(el).height, 10);
//3 pix is just a tiny static extra buffer. Adjust if necessary
var diff = (outerHeight - el.clientHeight) + 3;
// set the height to 0 incase it needs to be set smaller
el.style.height = 0;
// set the correct height
// el.scrollHeight is the full height of the content, not just the visible part
el.style.height = Math.max(60, el.scrollHeight + diff) + 'px';
}
As you can see, it’s really quite a simple component.
To use it, just place it in your form.
<c:DynamicTextArea aura:id="noteTextArea" value="{!v.noteBody}" readonly="{!v.editing ? false : true}" onfocus="{!c.handleEditClick}" onclick="{!c.handleDivClick}" class=" slds-m-top_xx-small slds-m-left_x-small " />
Download the Files
All the files are located on Github.
Enjoy!
Moving Forward in Lightning
Have questions? Let us know in a comment below or contact our team directly. You can also check out our other posts on customizing your Salesforce solution.
You save my day youngblood !! thank you very much!