Refresh datatable after saving form in Visualforce with CustomController

apexdatatablererendervisualforcevisualforce-component

I'm trying to create a apex:form with an input section where several fields are rendered and the user can save the record. Below this form, is an apex:datatable. I want to rerender the input fields and the datatable in after clicking the save Apex:commandbutton.

Problem

I can only get the input fields to rerender, the table data will not rerender.

Bonus points

Why is rerendering a datable so much harder than rerendering other components?

Attempted Solutions

  1. apex:actionsupport with apex:actionRegion wrapping the dataTable components.
  2. apex:outputpanel and targeting it for the rerender from the save button.
  3. setting id on the visualforce component and attempting to rerender the whole component to simulate a page reload.

Goal

This is a single visualforce page with several visualforce components in a list. Our goal is to rerender the form and datatable without rerendering the page so that we can make a reusuable component.

Component w/ table and form.

<apex:component id='theComponent' controller="HB_BS_EducationCardCont" allowDML="true">
    <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>Education History</span>
                        </h2>
                    </div>
                </header>
            </div>
            <div class="slds-card__body slds-card__body_inner">
                <apex:form id="theDataTableForm">
                    <apex:commandButton id="EduSaveButton" styleClass="slds-button slds-button_brand" action="{!save}" value="Save" rerender="EducationEditFields, eduRecsPanel"
                    status="pageSavingStatus" />
    
    
                <apex:outputPanel id="EducationEditFields">
                    <apex:repeat value="{!fields}" var="f">
                        <div class="slds-form-element">
                            <label class="slds-form-element__label" for="text-input-id-53">{!f.label}</label>
                            <div class="slds-form-element__control slds-input-has-icon slds-input-has-icon_right">
                                <apex:inputField styleClass="slds-input" value="{!educationForm[f.fieldPath]}" />
                            </div>
                        </div>
                    </apex:repeat>
                </apex:outputPanel>
                <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="eduRecsPanel">
                    <apex:dataTable value="{!EduRecords}" var="edu" id="theTable" rowClasses="odd,even" styleClass="slds-table slds-table_cell-buffer slds-table_bordered">
                        <apex:column>
                            <apex:facet name="header">School</apex:facet>
                            <apex:outputText value="{!edu.Other_School__c}" />
                        </apex:column>
                        <apex:column>
                            <apex:facet name="header">Degree</apex:facet>
                            <apex:outputText value="{!edu.Degree__c}" />
                        </apex:column>
                            <apex:commandButton styleClass="slds-button slds-button_brand" action="{!deleteEdu}" value="Delete" reRender="theComponent" status="deletingTableRecord">
                                <apex:param name="eduRecordId" value="{!edu.Id}" assignTo="{!selectedEduId}"/>
                            </apex:commandButton>
                        </apex:column>
                    </apex:dataTable>
                </apex:outputPanel>
                    <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:form>
            </div>
        </article>
    </div>

Controller

public without sharing class HB_BS_EducationCardCont {
    // Declare variables for methods. 
    
    public  List<Contact_Details__c> eduRecords;
    public Contact_Details__c educationForm {get;set;} 
    public String selectedEduId {get; set;}
    private User user;
    private Id eduRecordTypeId = Schema.SObjectType.Contact_Details__c.getRecordTypeInfosByDeveloperName().get('Education').getRecordTypeId();
    private Id contactId = [SELECT Id, ContactId FROM User WHERE Id =: UserInfo.getUserId()].ContactId;    

    //Constructor - runs first. 
    public HB_BS_EducationCardCont() {
        // this.eduRecords = getEduRecords();
        this.educationForm = getFormEduRecord(); 
    }

    public void deleteEdu(){
        String siteURL  = Site.getBaseUrl();
        PageReference pageRef = new PageReference(siteURL + '/apex/HB_BS_EducationPage');
        //Check for null to make sure we don't bomb
        if (selectedEduId == null) {
            // return pageRef;
        }
        //Get the record from my collection
        Contact_Details__c recordToDelete = null;
        for (Contact_Details__c myRecToDelete : eduRecords) {
            if (myRecToDelete.Id == selectedEduId) {
                recordToDelete = myRecToDelete;
                break;
            }
        }
        if (recordToDelete != null) {
            delete recordToDelete;
            // eduRecords = getEduRecords(); 
        }
        // return pageRef;
    }
    
    public List<Contact_Details__c> getEduRecords(){
        if (eduRecords == null) {
            eduRecords = [
            SELECT 
                Id, 
                Other_School__c, 
                Degree__c, 
                Degree_Type__c, 
                Grad_Year__c
            FROM Contact_Details__c
            WHERE RecordTypeId =: eduRecordTypeId AND Contact__c =: contactId
            ORDER BY Grad_Year__c DESC
            ];    
        }
        System.debug(eduRecords.size());

        return eduRecords;         
    }
 

    // Get Edu Field Set 
    public List<Schema.FieldSetMember> getFields(){
        
        return SObjectType.Contact_Details__c.FieldSets.HB_BS_EducationCardFields.getFields();
    }

    

    public Contact_Details__c getFormEduRecord(){
        Contact_Details__c educationForm = new Contact_Details__c(
            Contact__c = contactId, 
            RecordTypeId = eduRecordTypeId
        );        

        return educationForm; 
    }

    public void save(){
        try {
            insert educationForm;
            //Reset form values 
            this.educationForm = getFormEduRecord();
            getEduRecords();

             
            System.debug(eduRecords.size());
            
        } catch (Exception e) {
            System.debug(e.getMessage());
        }
        String siteURL  = Site.getBaseUrl();
        PageReference pageRef = new PageReference(siteURL + '/apex/HB_BS_EducationPage');
        // return pageRef; 
    }

    
}

Best Answer

In your getEduRecords() method you only query the records if you haven't yet. This means that the rerender works with the old data.

Remove the if (eduRecords == null) check and it should work fine.

Related Topic