15

When working on a visualforce page, I encountered this error:

"apex:inputFile can not be used in conjunction with an action component, apex:commandButton or apex:commandLink that specifies a rerender or oncomplete attribute"

This happens when I'm trying to delete a row from PageBlockTable.

In the visual source page, i first half of the screen i have component to upload the documents, below which i'm displaying the document. when i'm trying to delete the document in a particular row i'm getting the above issue.

enter image description here

Visualforce Page

<apex:page controller="FileUploadController" sidebar="false" showHeader="false">
   <apex:form enctype="multipart/form-data" id="docform">
    <apex:pageBlock title="Document" id="docpb">
        <table>
            <tr>
                <td> <apex:outputLabel value="Doc Code" for="docCode"/></td>
                <td>
                    <apex:selectList value="{!docCode}" id="docCode" multiselect="false" size="1">
                        <apex:selectOptions value="{!docCodes}"/>
                    </apex:selectList>
                </td>
            </tr>

            <tr>
                <td>
                     <apex:outputLabel value="File" for="file"/>
                </td>
                <td>
                    <apex:inputFile value="{!document.body}" filename="{!document.name}" id="file"/>
                </td>
            </tr>
            <tr>
                <td colspan='1'>
                    <apex:commandButton action="{!upload}" value="Save"/>
                </td>
            </tr>        
       </table> 
    </apex:pageBlock>
    <apex:pageBlock title="Documents" id="dpb">

        <apex:pageBlockTable value="{!documentList}" var="documentItem" id="documentTable">
            <apex:column headerValue="Document Code">
                <apex:outputField value="{!documentItem.Name}"/>
            </apex:column>
            <apex:column headerValue="Date">
                <apex:outputField value="{!documentItem.LastReferencedDate}"/>
            </apex:column>
            <apex:column headerValue="User">
                <apex:outputField value="{!documentItem.AuthorId}"/>
            </apex:column>
             <apex:column headerValue="Delete">
                 <apex:commandLink action="{!deleteDocument}" reRender="dpb"> 
                      <apex:image url="{!URLFOR($Resource.packaging, 'images/deleteicon.gif')}" />
                      <apex:param value="{!documentItem.Name}" name="docCodeParam" />
                 </apex:commandLink>
            </apex:column>
        </apex:pageBlockTable> 
        <br/>

    </apex:pageBlock>
  </apex:form>
</apex:page>

Apex Code:

public class FileUploadController {

    public PageReference deleteDocument() {

        String docCodeParam = apexpages.currentpage().getparameters().get('docCodeParam');

        System.debug('delete a document :: '+docCodeParam);

        return null;
    }


   public String docCode {get; set;}

   public List<Document> documentList {get; set;}

   public List<Folder> folderList {get; set;}

   public Document document {
        get {
          if (document == null)
            document = new Document();
          return document;
        }
        set;
   }

   public FileUploadController(){


      folderList = [SELECT Id,Name FROM Folder WHERE Name = 'IMAGE DOCS' ];

      if(folderList!=null && !folderList.isEmpty()){

          Folder folder = folderList.get(0);

          documentList = [SELECT Name , Type , Url, AuthorId, Body , 
                            LastReferencedDate , LastViewedDate, FolderId  FROM Document  
                            document where FolderId IN:folderList ];

      }


  }

  public PageReference upload() {

     Boolean isSuccess = true;

     if(folderList!=null && !folderList.isEmpty()){

          Folder folder = folderList.get(0);
          document.AuthorId = UserInfo.getUserId();
          document.FolderId =  folder.Id;// put it in running user's folder
          document.name = docCode+'-'+document.name;
    }

    try {

      insert document;

    } catch (DMLException e) {

      isSuccess = false;
      ApexPages.addMessage(new ApexPages.message(ApexPages.severity.ERROR,'Error uploading file'));

    } finally {

      document.body = null; // clears the viewstate
      document = new Document();

    }

    if(isSuccess){
        ApexPages.addMessage(new ApexPages.message(ApexPages.severity.INFO,'File uploaded successfully'));
    }

    return null;
  }

  public List<SelectOption> getDocCodes(){

     List<SelectOption> docCodeList = new List<SelectOption>();
     docCodeList.add(new SelectOption('','')); 
     docCodeList.add(new SelectOption('PHOTO','PHOTO - PHOTO')); 
     docCodeList.add(new SelectOption('DOC','DOC - DOC')); 

     return docCodeList;
  } 

}
1

4 Answers 4

9

You can use an actionRegion to define a scope for a commandButton or commandLink. This is sufficient to allow inputFile to happily co-exist with reRender attributes. Note that the actionRegion must contain all the information necessary to update the page, because only the components inside the actionRegion is processed by the server. Here's a simple example:

Controller

public with sharing class renderFile {
    public transient Blob fileBody { get; set; }
    public String contentType { get; set; }
    public String fileName { get; set; }
    public string[] items { get; set; }

    public renderFile() {
        items = new string[0];
    }

    public void save() {
        Document d = new Document(FolderId=UserInfo.getUserId(), Name=fileName, Body=fileBody, ContentType=contentType);
        insert d;
        items.add('Uploaded '+d.Name+' as ID '+d.Id);
    }
    public void reRender() {
        items.add('Item: '+(items.size()+1));
    }
}

Page

<apex:page controller="renderFile">
    <apex:form id="form">
        <apex:pageBlock >
            <apex:pageBlockButtons location="bottom">
                <apex:actionRegion id="area51" renderRegionOnly="false">
                    <apex:commandButton value="Something" reRender="form" action="{!reRender}"/>
                </apex:actionRegion>
                <apex:commandButton value="Save" action="{!save}"/>
            </apex:pageBlockButtons>
            <apex:pageBlockSection >
                <apex:repeat value="{!items}" var="value">
                    <apex:outputText >{!value}</apex:outputText>
                </apex:repeat>
                <apex:inputFile contentType="{!contentType}" fileName="{!fileName}" value="{!fileBody}" />
            </apex:pageBlockSection>
        </apex:pageBlock>
    </apex:form>
</apex:page>
4
  • 1
    The problem of rendering is solved but there is an another issue. We are not able to get any form values. Just made a try items.add('Item: '+fileName+(items.size()+1)); here we are getting the fileaname as null. Try out with some input value with all the above conditions. Kindly let me know any further details.
    – Arun
    Oct 12, 2013 at 6:38
  • I have had the same problem. In my case, I was wrapping an apex:actionFunction in the apex:actionRegion. This prevented the error from occurring, but it doesn't upload the file to the server, thereby rendering this solution useless. Nov 27, 2013 at 21:15
  • @Arun Don't know how I didn't notice this comment when you posted, but to answer your question, yes, it is expected that filename would be null in code that calls the rerender area. This is because the inputfile element can't be inside this area, and so therefore can't be submitted. You can only access filename when used in a non-rerender area (a full page submit).
    – sfdcfox
    Nov 27, 2013 at 23:45
  • 2
    @NathanWallace To upload a file to the server, you must not use a reRender. A full-page submit is the only legitimate means of uploading a file in Visualforce. The actionRegion must have all of the necessary elements in the view state for the actionFunction to execute successfully. In other words, it's tricky business, but this solution does work. It requires careful design in order to make it work correctly.
    – sfdcfox
    Nov 27, 2013 at 23:48
5

Try wrapping this in a actionregtion

<td colspan='1'>
<apex:actionregion>
                    <apex:commandButton action="{!upload}" value="Save"/>
</apex:actionregion>
</td>

The same problem was asked a bit different here :

apex:inputFile - cannot be used with ---- that specifies a rerender or oncomplete attribute

UPDATE : Try something like below :

<apex:pageblockbuttons location="bottom">
<apex:actionRegion >
<apex:commandbutton action="{!go}" value="Attach file">
</apex:commandbutton>
</apex:actionRegion>
</apex:pageblockbuttons>
4
  • @ Rao :- still facing the same issue "apex:inputFile can not be used in conjunction with an action component, apex:commandButton or apex:commandLink that specifies a rerender or oncomplete attribute"
    – Arun
    Sep 19, 2013 at 18:01
  • Are you using the save method for any other action method in the page?If not try wrapping the command button inside a <apex:pagebuttons> tag
    – Rao
    Sep 19, 2013 at 18:34
  • @ Rao :- OOPS!! still the same issue. I not using the save method for another action. The save functionality is working fine. The above problem cause while delete a row which i highlighted above.
    – Arun
    Sep 19, 2013 at 18:54
  • You almost had it correct. You have to wrap everything else inside the apex:actionRegion. I added an answer that illustrates this (and works).
    – sfdcfox
    Sep 19, 2013 at 19:01
3

There really is no great workaround to this as the platform simply does not allow Rerendering when you have an apex:inputFile component.

That being said, you could put your apex:inputfile into an iFrame, which would 'seperate' it from the page, and then allow for the rerender, but that gets pretty clunky .

Unfortunately, this is just a limitation of the platform.

EDIT:

After thinking a bit more, I suppose you could use jQuery to show/hide certain elements based on user actions. This is possible, but you would likely have to abandon the use of most of the VF components and go with more of a straight html, css, javascript approach, and that doesn't sound like what you are looking for

3
  • Is this a limitation only when the apex:inputFile and the apex:commandLink/apex:commandButton are within the same apex:form tag? If they are in separate form tags, will it work?
    – Mark Pond
    Sep 17, 2013 at 19:00
  • Thats a great question. I can;t say for certain, but my guess would be that no, having two separate forms would not get around this limitation. I would have to test to confirm though Sep 17, 2013 at 19:03
  • apex:actionRegion will suffice. Added an answer that illustrates this.
    – sfdcfox
    Sep 19, 2013 at 19:00
1

My workaround is to have a second form on the page that contains the upload UI and then use Javascript to move it to the desired location on the page. This worked for me with the existing AJAX and was simpler than creating a separate visualforce page and using iFrames.

Note: nesting an apex:form inside another apex:form doesn't compile so this workaround was used instead.

You must log in to answer this question.

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