Sunday 19 February 2017

Lightning Design System in Visualforce Part 2 - Forms

Lightning Design System in Visualforce Part 2 - Forms

(Update 20/02/2017 - added the sample to the github repo - see the Any Code? section) 

Jason

Introduction

In Part 1 of this series I covered getting started with the lightning design system for Visualforce developers. The example in that post was a page with a thin veneer of Visualforce, but with content that was pretty much vanilla HTML. In this post I’ll be making much more use of standard Visualforce components, which means I have to make some compromises. What I’m looking for here is to marry the speed of Visualforce development (provided by the standard component library) with the the modern styling of the Lightning Design System (LDS) rather than a pixel for pixel match with the Lightning Experience. Done is better than perfect!

Spring 17 and the LDS

<apex:slds>

In the original post the LDS was uploaded as a static resource, but Spring 17 means that this is no longer necessary - as long as you can live with the consequences.

A new Visualforce tag is available - <apex:slds>. This brings in the latest version of the LDS, hence my reference to consequences. If you can accept always being upgraded to the latest version as soon as it is available, this is the tag for you. If you need to fix the version (which I think I would, so that customer users don’t suddenly get presented with an unexpected change) then stick with the static resource. There are a few rules around using this tag, which are explained in the official docs.

<apex:page showHeader="false" sidebar="false" standardStylesheets="true"
           standardController="Contact" applyHTmlTag="false">
    <html xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
        <head>
            <apex:slds />
        </head>
        <body class="slds-scope">
                 ...
        </body>
    </html>
</apex:page>

As I’m including the SLDS via the HTML header, I have to specify the slds-scope class for the body tag in order to be able to use the SLDS tags. Interestingly the docs state that if I’m showing the header, sidebar or using the standard stylesheets then I can’t add attributes to the html tag and thus SVG icons aren’t supported. However, I am using the standard stylesheets and they are still working for me, at least in Firefox, so go figure. If this doesn’t work for you, you’ll need to switch the icons to another format.

$Asset

If you don’t upload the LDS as a static resource, you’ll need to get the assets (icons etc) from the system default. Enter the $Asset global variable, another new feature in Spring 17. Simply use $Asset.SLDS in place of your static resource, and you can access assets via the URLFOR function. Again more details in the official docs.

<div class="slds-media__figure">
    <svg aria-hidden="true" class="slds-icon slds-icon-standard-contact">
        <use xlink:href="{!URLFOR($Asset.SLDS, '/assets/icons/standard-sprite/svg/symbols.svg#contact')}"></use>
    </svg>
</div>

Styling Inputs

The key to applying the LDS to standard Visualforce form components is the styleClass attribute - this allows a custom style to override the standard Visualforce styling that we all know and love (!). 

Using a Visualforce standard component inside an SLDS styled form element doesn’t look too bad - just a little truncated, The following markup:

<apex:inputField value="{!Contact.FirstName}"/>

generates:

Screen Shot 2017 02 19 at 07 41 37

Supplying the SLDS style class fixes this:

<apex:inputField styleClass="slds-input" value="{!Contact.FirstName}"/>

 

Screen Shot 2017 02 19 at 07 46 42

Buttons

Buttons are another simple fix - I can still use command buttons, just styled for the LDS:

<div class="slds-p-horizontal--small slds-m-top--medium slds-size--1-of-1 slds-align--absolute-center">
    <apex:commandButton styleClass="slds-button slds-button--neutral" value="Cancel" action="{!cancel}" />
    <apex:commandButton styleClass="slds-button slds-button--brand" value="Save" action="{!save}" />
</div>

Screen Shot 2017 02 19 at 08 14 12

One size does not fit all

While the style class works well for simple inputs, fields which require more complex widgets are where the compromises come in. Lookups, for example, are very different in the LDS and Visualforce. In this case I have to live with the fact that the search will produce a popup window and the input will have a magnifying glass, but I add some styling to make it less jarring on the user:

<apex:inputField style="width:97%; line-height:1.875em;" value="{!Contact.AccountId}" />

which renders as:

Screen Shot 2017 02 19 at 07 54 55

So not perfect but not terrible either.

Required Fields

Required fields mean a bigger compromise, as I have to add the required styling myself. My page markup therefore knows which fields are required and which aren’t, which in turn makes the page less flexible. If an administrator makes one of the fields required, basic Visualforce skills are required to change the page to reflect this:

<div class="slds-form-element slds-hint-parent">
    <span class="slds-form-element__label"><abbr class="slds-required" title="required">*</abbr>Last Name</span>
    <div class="slds-form-element__control">
        <apex:inputField styleClass="slds-input" value="{!Contact.LastName}"/>
    </div>
</div> 

Screen Shot 2017 02 19 at 08 08 17

The end result

So here’s the final page - clearly not an exact match for LEX, but pretty close, and put together very quickly.

Screen Shot 2017 02 19 at 08 10 11

Any code?

As usual with LDS posts, the code is in my LDS Samples Github Repository. There’s also an unmanaged package available to save wasting time copying and pasting - see the README.

In Conclusion

Pragmatism is key here - there are some compromises around styling and losing some of the separation of the page and business logic, but I feel these are outweighed by the sheer speed of development. Of course I could switch to using vanilla HTML with LDS styling and manage the inputs via JavaScript, but if I’m going that route I’ll go the whole hog and use Lightning Components.

Related Posts