function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
mwille64mwille64 

Rerendering a visualforce page from apex controller implementing Database.Batchable

I have a Visualforce page that manages the generation, update and removal of records for annual processing of allocation processes. This is a once a year mannual process in which a user decides to delete older records (previous years), generate records for coming years or simply add records for new allocatable objects.

The controller is using the Database.batchable class to handle the rather large amount of records deleted or created in one go. This means that a simple Apex:ActionFunction to rerender the page isn't doing the trick, as the process triggered by the button is asyncronous and might complete only after several minutes.

I'm passing the controller object to the class that handles the batch calls, but I have not figured out a way to trigger via the page controller a rerendering of the page, once the batch processing is completed.

Any suggestions on how to achieve that?
Best Answer chosen by mwille64
Shri RajShri Raj

You can use a combination of JavaScript and Apex to rerender the page after the batch processing is completed. You can use an apex action function to call an apex method in your page controller, which will update a boolean flag indicating that the batch process has completed. In your JavaScript code, you can then use setInterval method to repeatedly check the value of this flag and rerender the page once the value changes to true.
<apex:page controller="MyPageController">
apex:form
<apex:actionFunction name="checkBatchStatus" action="{!checkBatchStatus}" rerender="pageBlock" />
</apex:form>
<apex:outputPanel id="pageBlock">
apex:pageBlock
<!-- Your page content -->
<apex:commandButton value="Start Batch" action="{!startBatch}" oncomplete="checkBatchStatus();"/>
</apex:pageBlock>
</apex:outputPanel>
</apex:page>
 
var intervalId = setInterval(function() {
checkBatchStatus();
}, 1000);

function checkBatchStatus() {
if ({!batchProcessCompleted}) {
clearInterval(intervalId);
location.reload();
}
}
VF controller
public class MyPageController {
public Boolean batchProcessCompleted { get; set; }

public void startBatch() {
Database.executeBatch(new MyBatchable());
// set the batchProcessCompleted flag to false
batchProcessCompleted = false;
}

public void checkBatchStatus() {
// check if the batch process has completed
// set the batchProcessCompleted flag to true if completed
batchProcessCompleted = true;
}
}
Batch
global class MyBatchable implements Database.Batchable<sObject> {
global Database.QueryLocator start(Database.BatchableContext bc) {
// Your query to retrieve the records
}

global void execute(Database.BatchableContext bc, List<sObject> scope) {
// Your processing logic
}

global void finish(Database.BatchableContext bc) {
// Update the batchProcessCompleted flag in your page controller
}
}

 

All Answers

Shri RajShri Raj

You can use a combination of JavaScript and Apex to rerender the page after the batch processing is completed. You can use an apex action function to call an apex method in your page controller, which will update a boolean flag indicating that the batch process has completed. In your JavaScript code, you can then use setInterval method to repeatedly check the value of this flag and rerender the page once the value changes to true.
<apex:page controller="MyPageController">
apex:form
<apex:actionFunction name="checkBatchStatus" action="{!checkBatchStatus}" rerender="pageBlock" />
</apex:form>
<apex:outputPanel id="pageBlock">
apex:pageBlock
<!-- Your page content -->
<apex:commandButton value="Start Batch" action="{!startBatch}" oncomplete="checkBatchStatus();"/>
</apex:pageBlock>
</apex:outputPanel>
</apex:page>
 
var intervalId = setInterval(function() {
checkBatchStatus();
}, 1000);

function checkBatchStatus() {
if ({!batchProcessCompleted}) {
clearInterval(intervalId);
location.reload();
}
}
VF controller
public class MyPageController {
public Boolean batchProcessCompleted { get; set; }

public void startBatch() {
Database.executeBatch(new MyBatchable());
// set the batchProcessCompleted flag to false
batchProcessCompleted = false;
}

public void checkBatchStatus() {
// check if the batch process has completed
// set the batchProcessCompleted flag to true if completed
batchProcessCompleted = true;
}
}
Batch
global class MyBatchable implements Database.Batchable<sObject> {
global Database.QueryLocator start(Database.BatchableContext bc) {
// Your query to retrieve the records
}

global void execute(Database.BatchableContext bc, List<sObject> scope) {
// Your processing logic
}

global void finish(Database.BatchableContext bc) {
// Update the batchProcessCompleted flag in your page controller
}
}

 
This was selected as the best answer
mwille64mwille64
@Shri Raj, thanks for your reply. I tried implementing your suggestion, but it doesn't seem to work. So I have two questions:

- Where exactly in the Visualforce Page should the Javascript be placed? Currently I placed it in the PageBlock to be rendered.
- When exactly is the timer initiated?



 
mwille64mwille64
OK, the time is working for. I modified it slightly not to be activated on page loading, but on clicking an action button that triggers the bacth calls.

So far so good. What I have not managed to implement is the following...

global void finish(Database.BatchableContext bc)
{
    // Update the batchProcessCompleted flag in your page controller
}


I have tried to pass on the Controller object to the batch class on instantiation, but setting the flag has zero effect. The flag remains set to false...

public with sharing class ManageRoomOccupationsController 
{
...
    public void ResetRoomOccupations()
    {
            batchProcessCompleted = false;
            Database.executeBatch(new resetRoomOccupationsBatch(this, Integer.valueOf(YearSelected),50000,false), 5000);                
    }
...
}

global class resetRoomOccupationsBatch implements Database.Batchable<sObject>, Database.Stateful 
{
    integer YearSelected;
    integer maxBatchSize;
    boolean isTestRun;
    long deletecount;
    
    ManageRoomOccupationsController parentctrl;
        
    public resetRoomOccupationsBatch(ManageRoomOccupationsController pctrl, integer year, integer recnum, boolean istest)
    {
        YearSelected = year;
        maxBatchSize = recnum;
        isTestRun    = istest;
        parentctrl   = pctrl;   
        deletecount  = 0;
    }

...

    global void finish(Database.BatchableContext BC) 
    {   
            parentctrl.render_Processing = 'false';  
            parentctrl.batchProcessCompleted = true;
            ...
    }
}