[SalesForce] How to rerender an outputpanel after remoting completes

I have a repeat control that populates commandbuttons(view,edit,delete) for each record of an object. On click of a command button I want to delete the respective record and render the containing output panel. The code attached actually deletes the record but it looks like it postbacks the page and the panel doesn't show any record though it actually has records. I tried without the actionfunction, just by adding rerender="leftOPanel" in commandbutton, I could not see any change in the list, but if I refresh I could see the record deletion. The rerender occurs before java script remote method for deletion completes?! I don't understand what is going wrong here. The record is getting deleted successfully in all the cases, but only this rerender is not working fine.

global with sharing class ProjectController {

public static List<Project__c> lstproj{get; set;}
public Project__c prj{get; set;}


public ProjectController(ApexPages.StandardController controller)
{
    prj = new Project__c();
    lstproj = [SELECT Name,Proj_Name__c,Status__c,Abstract__c
               FROM Project__c ];
}


@RemoteAction
global static List<Project__c> DeleteProj(String projId) {

    Project__c[] proj;
    proj = [SELECT id,Name,Proj_Name__c,Status__c,Abstract__c
               FROM Project__c  WHERE Name = :projId];
    if(proj.size() > 0 || proj[0].Id != ''){
        delete proj;}
   lstproj = [SELECT Name,Proj_Name__c,Status__c,Abstract__c
               FROM Project__c ];

    return lstproj;

}}

VF page

<apex:page  sidebar="false"  standardController="Project__c" extensions="ProjectController" >
<apex:form >
    <apex:pageBlock mode="edit" >
        <apex:outputPanel id="leftOPanel" >   
            <ul class="slds-has-dividers--top">
                <apex:repeat value="{!lstproj}" var="p">
                    <li >
                        <h3 >{!p.Proj_Name__c}</h3>
                        <div>
                            <ul>
                                <li><apex:commandbutton value="View" /></li>
                                <li><apex:commandbutton value="Edit" /></li>
                                <li><apex:commandbutton value="Delete" onclick="DeleteProject('{!p.Name}');" /></li>
                            </ul>
                        </div>
                    </li> 
                </apex:repeat>
            </ul>   
        </apex:outputPanel>
        <apex:actionFunction name="rerenderleftOPanel" rerender="leftOPanel" />
    </apex:pageBlock>
</apex:form>
<script>
function DeleteProject(projId)
{
    Visualforce.remoting.Manager.invokeAction('{!$RemoteAction.ProjectController.DeleteProj}',projId,function(result,event){ var r=event.message;alert(result.length);},{escape:true});
    rerenderleftOPanel();
}
</script></apex:page>

Best Answer

java-script(VF) Remoting is an asynchronous call, you need to call your <apex:actionFunction> in the callback function of Remoting, means when remoting function finish with the execution. update your Remoting code to:

function DeleteProject(projId)
{
    Visualforce.remoting.Manager.invokeAction(
      '{!$RemoteAction.ProjectController.DeleteProj}',projId,
       function(result,event){
           if (event.status){// When call is succesfull with event.status = true                                  
              console.log(result.length); // print the result length on console              
              rerenderleftOPanel(); // call rerender function
           }
           else{
                console.log(event.message); // print error on console(if any)
           }               
       },
       {escape:true}
    );
    //rerenderleftOPanel();
}

UPDATE

I just seen your controller code, and get to know you are using static variable lstproj for rendering on VF page. That where you are making mistake. Static variables are transient variable, which didn't communicate with Visualforce View state. In order to to interact with VF page, make your variable as instance with getter-setter property. Change your class code as below:

Apex Class Change:

global with sharing class ProjectController {

public List<Project__c> lstproj{get; set;}// #### CHANGE #### ***** variable Changed to instance variable*****
public Project__c prj{get; set;}


public ProjectController(ApexPages.StandardController controller)
{
    prj = new Project__c();
    initProjList(); // initialize your list with data
}

public void initProjList(){ // #### CHANGE #### ***** method to get data list into your page variable*****
   lstproj = [SELECT Name,Proj_Name__c,Status__c,Abstract__c
               FROM Project__c ];
}    

@RemoteAction
global static List<Project__c> DeleteProj(String projId) {

    Project__c[] proj;
    proj = [SELECT id,Name,Proj_Name__c,Status__c,Abstract__c
               FROM Project__c  WHERE Name = :projId];
    if(proj.size() > 0 || proj[0].Id != ''){
        delete proj;}
   list<Project__c> lstproj = [SELECT Name,Proj_Name__c,Status__c,Abstract__c
               FROM Project__c ];   // #### CHANGE #### ***** initialize the list Object *****

    return lstproj;

}}

Page Change:

Also you need to change your page code and need to add following two changes:

First Change:

First change as @saroj suggested call an action method from <apex:actionfunction>, Well it is not required because of re-render will not work, re-render can work without Action also, but it is required to update your page list variable lstproj after deletion. so it can display the updated data list on page again. See change:

<apex:actionFunction action="{!initProjList}" name="rerenderleftOPanel" rerender="leftOPanel" />

Second Change:

Second change which I observed here, you have used <apex:commandButton> for initiate the action flow, <apex:commandButton> are act like submit button of html so they will refresh the page by deafult, for preventing them to reload the whole page, add one line of code return false after your VF Remoting Method call in onClick attribute, like: onclick="DeleteProject('{!p.Name}'); return false;"

Related Topic