1

I have very peculiar problem.

I have two components that are in a visualforce page. The design is the same for all the components. We get a list of records and immediately below it we have a form that allows the user to dynamically update the table. In the table they can delete individual records. There are multiple forms for the user to fill out, but one of the forms that they fill out is throwing JavaScript Errors rather than server-side errors.

Problem

The javaScript/client side validation is preventing a rerender onclick. But when we have the server-side errors we can use immediate=true and it bypasses the validations:

Attempted fixes 1-11-21

I attempted to add the action region, but it is still firing the JS validations. It's unclear why the validation is firing. Right now I'm leaning towards using the fieldsetMember object to throw validations.

Fix 1 wrapping the button


<apex:actionRegion renderRegionOnly="false"> 
   <apex:commandButton styleClass="slds-button slds-button_destructive" action="{!deleteBarRecord}" immediate="true" value="Delete" rerender="BarAdmissionsEditFields, barAdmissionsPanel" status="deletingTableRecord"> 
    <apex:param name="barRecordId" value="{!rec.Id}" assignTo="{!selectedBarRecId}" /> 
 </apex:commandButton> 
</apex:actionRegion>

Fix 2 wrapping the table

<apex:actionRegion renderRegionOnly="false">
            <apex:outputPanel id="barAdmissionsPanel">
                <apex:dataTable value="{!barAdmissionsTableRecords}" var="rec" id="theTable" rowClasses="odd,even" styleClass="slds-table slds-table_cell-buffer slds-table_bordered">
                    <apex:column>
                        <apex:facet name="header">Jurisdiction</apex:facet>
                        <apex:outputText value="{!rec.Jurisdiction__c}" />
                    </apex:column>
                    <apex:column>
                        <apex:facet name="header">Admission Year</apex:facet>
                        <apex:outputText value="{!rec.Admission_Year__c}" />
                    </apex:column>
                    <apex:column>
                        <apex:facet name="header">Bar</apex:facet>
                        <apex:outputText value="{!rec.Bar__c}" />
                    </apex:column>
                    <apex:column>
                    
                        <apex:commandButton styleClass="slds-button slds-button_destructive" action="{!deleteBarRecord}" immediate="true" value="Delete"
                            rerender="BarAdmissionsEditFields, barAdmissionsPanel" status="deletingTableRecord">
                            <apex:param name="barRecordId" value="{!rec.Id}" assignTo="{!selectedBarRecId}" />
                        </apex:commandButton>
                   
                        <!-- <apex:commandButton  styleClass="slds-button slds-button_destructive" action="{!deleteBarRecord}" immediate="true" rerender="BarAdmissionsEditFields, barAdmissionsPanel" value="Test"  status="deletingTableRecord">
                            <apex:param name="barRecordId" value="{!rec.Id}" assignTo="{!selectedBarRecId}"/>
                        </apex:commandButton> -->
                    
                        <apex:actionStatus id="deletingTableRecord">
                            <apex:facet name="start">
                                <apex:outputPanel>
                                    <img src="/img/loading32.gif" width="25" height="25" />
                                    <apex:outputLabel value="Deleting..." />
                                </apex:outputPanel>
                            </apex:facet>
                        </apex:actionStatus>
                    </apex:column>
                </apex:dataTable>
            </apex:outputPanel>
        </apex:actionRegion>

Server Side Required-field Error

ServerSide Error

ServerSide

Client Side Required-field Error

Code

  • Both are using <apex:slds/>
  • We have been following the same recipe for a all of the forms.

Server Side Form:

 <apex:component controller="HB_BS_AreasOfPracticeCont" allowDML="true">
    <apex:messages />
    <style>
        .errorMsg { color: red; }
    </style>

<div class="slds-scope">
    <article class="slds-card">
        <div class="slds-card__header slds-grid">
            <header class="slds-media slds-media_center slds-has-flexi-truncate">
                <div class="slds-media__figure"></div>
                <div class="slds-media__body">
                    <h2 class="slds-card__header-title">
                        <span>Areas of Practice History</span>
                    </h2>
                </div>
            </header>
        </div>
        <div class="slds-card__body slds-card__body_inner">
            
            <apex:form id="theDataTableForm">  
                <apex:outputPanel id="theOutPutPanel">
                <apex:dataTable value="{!aopTableRecords}" var="rec" id="theTable" rowClasses="odd,even" styleClass="slds-table slds-table_cell-buffer slds-table_bordered">
                    <apex:column >
                        <apex:facet name="header">Area</apex:facet>
                        <apex:outputText value="{!rec.Area__c}" />
                    </apex:column>
                    
                    <apex:column >
                        <apex:facet name="header">First Year</apex:facet>
                        <apex:outputText value="{!rec.First_Year_of_Practice__c}"/>               
                    </apex:column>
                    <apex:column >
                        <apex:facet name="header">Number of Years</apex:facet>
                        <apex:outputText value="{!rec.Number_of_Years__c}">
                        </apex:outputText>
                    </apex:column>
                    <apex:column >
                        <apex:facet name="header">Avg Appearances</apex:facet>
                        <apex:outputText value="{!rec.Average_Number_of_Court_Appearances_a_Yr__c}">
                        </apex:outputText>
                    </apex:column>
                    <apex:column >
                        <apex:commandButton  styleClass="slds-button slds-button_destructive" action="{!deleteAopRecord}" immediate="true" rerender="AreaOfPracticeFields,theOutPutPanel" value="Delete"  status="deletingTableRecord">
                            <apex:param name="aopRecordId" value="{!rec.Id}" assignTo="{!selectedAopId}"/>
                        </apex:commandButton>
                    <apex:actionStatus id="deletingTableRecord">
                        <apex:facet name="start">
                            <apex:outputPanel >
                                <img src="/img/loading32.gif" width="25" height="25" />
                                <apex:outputLabel value="Deleting..." />
                            </apex:outputPanel>
                        </apex:facet>
                    </apex:actionStatus>
                    </apex:column>
                </apex:dataTable>
            </apex:outputPanel>
                        <h2 class="slds-card__header-title slds-p-top_x-large">
                            <span>Add Areas of Practice</span>
                        </h2>
                    <div class="slds-card__body slds-card__body_inner">
                            <apex:commandButton styleClass="slds-button slds-button_brand" action="{!save}" value="Add" rerender="theDataTableForm" status="pageSavingStatus"/>
                            <apex:actionStatus id="pageSavingStatus">
                                <apex:facet name="start">
                                    <apex:outputPanel >
                                        <img src="/img/loading32.gif" width="25" height="25" />
                                        <apex:outputLabel value="Saving..."/>
                                    </apex:outputPanel>            
                                </apex:facet>
                            </apex:actionStatus>
                            <apex:outputPanel id="AreaOfPracticeFields">
                                <apex:repeat value="{!fields}" var="f">
                                    <div class="slds-form-element">
                                        <label class="slds-form-element__label" for="text-input-id-53">{!f.label}<apex:outputText value=" *" style="color:red;" rendered="{!f.Required}"/></label>
                                        <div class="slds-form-element__control slds-input-has-icon slds-input-has-icon_right">
                                            <apex:inputField styleClass="slds-input " value="{!AopFormRecord[f.fieldPath]}" required="{!OR(f.required, f.dbrequired)}"/> 
                                        </div>
                                    </div>
                                </apex:repeat>
                            </apex:outputPanel>
                    </div>
            </apex:form>
        </div>
    </article>
</div>
</apex:component>

Client Side Form

<apex:component controller="HB_BS_BarAdmissionsCont" allowDML="true">

<apex:messages/>
<div class="slds-scope">
    <article class="slds-card">
        <div class="slds-card__header slds-grid">
            <header class="slds-media slds-media_center slds-has-flexi-truncate">
                <div class="slds-media__figure"></div>
                <div class="slds-media__body">
                    <h2 class="slds-card__header-title">
                        <span>Bar Admissions</span>
                    </h2>
                </div>
            </header>
        </div>
        <div class="slds-card__body slds-card__body_inner">

        <apex:form id="theBarAdmissionsTableForm">
            <apex:outputPanel id="barAdmissionsPanel">
                <apex:dataTable value="{!barAdmissionsTableRecords}" var="rec" id="theTable" rowClasses="odd,even" styleClass="slds-table slds-table_cell-buffer slds-table_bordered">
                    <apex:column>
                        <apex:facet name="header">Jurisdiction</apex:facet>
                        <apex:outputText value="{!rec.Jurisdiction__c}" />
                    </apex:column>
                    <apex:column>
                        <apex:facet name="header">Admission Year</apex:facet>
                        <apex:outputText value="{!rec.Admission_Year__c}" />
                    </apex:column>
                    <apex:column>
                        <apex:facet name="header">Bar</apex:facet>
                        <apex:outputText value="{!rec.Bar__c}" />
                    </apex:column>
                    <apex:column>
                        <apex:commandButton styleClass="slds-button slds-button_destructive" action="{!deleteBarRecord}" immediate="true" value="Delete"
                            rerender="BarAdmissionsEditFields, barAdmissionsPanel" status="deletingTableRecord">
                            <apex:param name="barRecordId" value="{!rec.Id}" assignTo="{!selectedBarRecId}" />
                        </apex:commandButton>
                        <apex:commandButton  styleClass="slds-button slds-button_destructive" action="{!deleteBarRecord}" immediate="true" rerender="BarAdmissionsEditFields, barAdmissionsPanel" value="Test"  status="deletingTableRecord">
                            <apex:param name="barRecordId" value="{!rec.Id}" assignTo="{!selectedBarRecId}"/>
                        </apex:commandButton>
                        <apex:actionStatus id="deletingTableRecord">
                            <apex:facet name="start">
                                <apex:outputPanel>
                                    <img src="/img/loading32.gif" width="25" height="25" />
                                    <apex:outputLabel value="Deleting..." />
                                </apex:outputPanel>
                            </apex:facet>
                        </apex:actionStatus>
                    </apex:column>
                </apex:dataTable>
            </apex:outputPanel>

             
             <h2 class="slds-card__header-title slds-p-top_x-large">
                Add Bar Admissions
            </h2>
            <div class="slds-card__body slds-card__body_inner">
             <apex:commandButton styleClass="slds-button slds-button_brand" action="{!save}" value="Add" rerender="theBarAdmissionsTableForm" status="pageSavingStatus"/>
             <apex:actionStatus id="pageSavingStatus">
                <apex:facet name="start">
                    <apex:outputPanel >
                        <img src="/img/loading32.gif" width="25" height="25" />
                        <apex:outputLabel value="Saving..."/>
                    </apex:outputPanel>            
                </apex:facet>
            </apex:actionStatus>
             <apex:outputPanel id="BarAdmissionsEditFields">
                     <apex:repeat value="{!fields}" var="f">
                         <div class="slds-form-element">
                            <label class="slds-form-element__label" for="text-input-id-53">{!f.label}<apex:outputText value=" *" style="color:red;" rendered="{!f.Required}"/></label>
                             <div class="slds-form-element__control slds-input-has-icon slds-input-has-icon_right">
                                 <apex:inputField styleClass="slds-input " value="{!barAdmissionsFormRecord[f.fieldPath]}" required="{!OR(f.required, f.dbrequired)}" />
                               
                             </div>
                         </div>
                     </apex:repeat>
                 </apex:outputPanel>     
            </div>
            </apex:form>
        </div>
    </article>
</div>               
</apex:component>

2 Answers 2

4

To keep client-side validations from blocking your actions, use an apex:actionRegion.

<apex:actionRegion renderRegionOnly="false">
    <apex:commandButton  styleClass="slds-button slds-button_destructive" action="{!deleteAopRecord}" rerender="AreaOfPracticeFields,theOutPutPanel" value="Delete"  status="deletingTableRecord">
        <apex:param name="aopRecordId" value="{!rec.Id}" assignTo="{!selectedAopId}"/>
    </apex:commandButton>
</apex:actionRegion>

Do not use immediate="true", because it also causes getters and setters to be skipped, which severely limits what you can do within your controller.

8
  • I attempted do this but apex action region was still triggering the validation on the serverside. I have been wrapping the table with <apex:actionRegion/> but it looks like you wrap the button with apex action region. Seems counterintuitive...
    – thinker
    Jan 11, 2022 at 18:51
  • 1
    @thinker The action region restricts validation to whatever is inside it. So, we put it immediately around the button, so there are no validations to run. As far as the server-side validations, it will only trigger whatever method you call, so it sounds like you might need to fix your Apex, too.
    – sfdcfox
    Jan 11, 2022 at 18:55
  • Ok, I'll experiment with that. Do you know why we are getting inconsistent javascript validation versus server side validations?
    – thinker
    Jan 11, 2022 at 18:57
  • 2
    @thinker Either apex:actionRegion is placed in the wrong place, the reRender targets are not correct, or immediate="true". You can't use immediate="true" if you expect that you need to modify the view state.
    – sfdcfox
    Jan 11, 2022 at 18:59
  • 1
    @thinker the action region should be immediately surrounding the button. You can still target elements outside the region for reRendering.
    – sfdcfox
    Jan 11, 2022 at 20:25
0

I attempted to implement @sfdcfox solution, but for some reason it is still firing the clientSide JS from salesforce (We don't have any JS on the page). So we had to opt for the controller validating the fields that are required in the fieldset in the save method.

Code

List<Schema.FieldSetMember> fields = getFields(); 
            for (Schema.FieldSetMember f : fields) {
                System.debug(f.fieldPath);
                if (f.getRequired() && barAdmissionsFormRecord.get(f.fieldpath) == null ) {
                    
                    ApexPages.addmessage(new ApexPages.message(ApexPages.severity.FATAL,'Please enter value for ' + f.label));
                    return null; 
                }
            }

HTML Visualforce Component:

  <apex:commandButton styleClass="slds-button slds-button_brand" action="{!save}" value="Add" rerender="theBarAdmissionsTableForm" status="pageSavingStatus"/>
             <apex:actionStatus id="pageSavingStatus">
                <apex:facet name="start">
                    <apex:outputPanel >
                        <img src="/img/loading32.gif" width="25" height="25" />
                        <apex:outputLabel value="Saving..."/>
                    </apex:outputPanel>            
                </apex:facet>
            </apex:actionStatus>
            <apex:messages styleClass="errorMsg"/>
             <apex:outputPanel id="BarAdmissionsEditFields">
                     <apex:repeat value="{!fields}" var="f">
                         <div class="slds-form-element">
                            <label class="slds-form-element__label" for="text-input-id-53">{!f.label}<apex:outputText value=" *" style="color:red;" rendered="{!f.Required}"/></label>
                             <div class="slds-form-element__control slds-input-has-icon slds-input-has-icon_right">
                                 <apex:inputField  styleClass="slds-input " value="{!barAdmissionsFormRecord[f.fieldPath]}" />
                             </div>
                         </div>
                     </apex:repeat>
                 </apex:outputPanel>     
            </div>

Lastly, we added some tags to customize the red warning color for the apex:messages element.

I'm sure there is a better way to do this, but every iteration I have tried with ApexRegion does not deactivate what appears to be slds JS validations.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .