1

I have an embedded visualforce page on the Product2 page layout and an extension class for the Product2 standard controller that is used by this VF page. It's a little product photo management widget I've been working on for the past couple of days.

It works fairly well; it uploads photos, it displays them, and it deletes them. However, I have one problem: When I click the button to delete the photo, the widget seems to reload prematurely before some fields are updated on the Product object.

I am rendering one of two widgets in the VF page (inputSection and previewSection) with a simple condition in the "rendered" attribute to check if the Product_Image_ID__c field is blank. That field is supposed to get set to a blank value as soon as the product photo is deleted; the apex method that handles the deletion of that record also updates the product object.

For whatever reason, when the PageReference deletePhoto() method returns, the VF page will still render "previewSection" as if Product_Image_ID__c is not blank (it should already be blank at this point.) If I reload the entire page again, it works properly and renders inputSection instead of previewSection.

Sorry, I know that's a mouthful. Here is my code to help you better understand:

The visualforce widget:

<apex:page standardController="Product2" extensions="ProductPhotosExtension">
    <apex:stylesheet value="{!$Resource.photouploaderstyles}"/>
    <apex:form id="photoUploaderForm" styleClass="photoUploaderForm">
        <apex:pageBlock mode="maindetail">
            <apex:pageBlockSection columns="1">
                <apex:pageBlockSectionItem id="inputSection" rendered="{!ISBLANK(ProductImageID)}">
                    <apex:outputLabel value="Product Image" for="fileImageId"/>
                    <apex:inputFile id="fileImageId" value="{!fileBody}" styleClass="inputFile"
                                    filename="{!fileName}" onchange="uploadFile(event);"/>
                </apex:pageBlockSectionItem>
                <div id="waitMessage" class="waitMessage">
                    Uploading file... Please wait.
                </div>
            </apex:pageBlockSection>
            <apex:pageBlockSection columns="1" rendered="{!NOT(ISBLANK(ProductImageID))}">
                <apex:pageBlockSectionItem id="previewSection">
                    <apex:outputLabel value="Product Image" for="previewImageId"/>
                    <img id="imagePreview" src="{!ProductImage}" class="previewImage"/>
                    <a href="javascript:deleteFile();" class="deleteButton">X</a>
                </apex:pageBlockSectionItem>
            </apex:pageBlockSection>
        </apex:pageBlock>
        <apex:actionFunction name="callUploadMethod" action="{!upload}"/>   
        <apex:actionFunction name="callDeleteMethod" action="{!deletePhoto}"/>  
    </apex:form>

    <script>
        var input_divs = document.getElementsByClassName('inputFile');
        var input_div = input_divs[0];
        var wait_div = document.getElementById('waitMessage'); 

        function getOffset(el) {
            el = el.getBoundingClientRect();
            return {
                left: el.left + window.scrollX,
                top: el.top + window.scrollY
            }
        }

        function deleteFile(event) {
            callDeleteMethod();
        }

        function uploadFile(event) {
            var file = event.target.files[0];
            var reader = new FileReader();
            reader.addEventListener("load", function () {
                dataurl = event.target.result;
                callUploadMethod(dataurl);
                input_div.setAttribute('type', 'hidden');
                input_rect = getOffset(input_div);
                wait_div.style.left = input_rect.left + 'px';
                wait_div.style.top = input_rect.top + 'px';
                wait_div.style.display = 'block';
            }, false);
            reader.readAsDataURL(file);            
        } 
    </script>
</apex:page>

The Apex class:

public class ProductPhotosExtension {
    private final Product2 prod;
    private String imageURL;

    public ProductPhotosExtension(ApexPages.StandardController stdController) {
        stdController.addFields(new String[] {
            'Id',
            'Name',
            'Product_Image_ID__c',
            'Product_Image__c'
        });
        this.prod = (Product2)stdController.getRecord();
    }

    public Blob fileBody {
        get; set;
    }

    public String fileName {
        get; set;
    }

    public PageReference deletePhoto() {
        try { 
            Document document = [SELECT Id FROM Document WHERE Id = :prod.Product_Image_ID__c];
            prod.Product_Image_ID__c = '';
            prod.Product_Image__c = '';
            update prod;
            delete document;
        } catch (DMLException e) {
            ApexPages.addMessage(new ApexPages.message(
                ApexPages.severity.ERROR, 'Error deleting file')
            ); return null;            
        }

        return null;
    }    

    public PageReference upload() {
        Document document = new Document();
        document.Body = fileBody;
        document.Name = fileName;
        document.FolderId = '00l33000001ko9v';
        try {
            imageURL = '/servlet/servlet.FileDownload?file=';
            insert document;
            imageURL = imageURL + document.id;
            prod.Product_Image_ID__c = document.id;
            prod.Product_Image__c = imageURL;
            update prod;            
        } catch (DMLException e) { 
            ApexPages.addMessage(new ApexPages.message(
                ApexPages.severity.ERROR, 'Error uploading file')
            ); return null;
        }

        return null;
    }

    public Blob getFileBody() {
        return fileBody;
    }

    public String getFileName() {
        return fileName;
    }

    public String getProductImage() {
        return prod.Product_Image__c;
    }

    public String getProductImageID() {
        return prod.Product_Image_ID__c;
    }

}

And I'll throw in the stylesheet too just for good measure, even though it's probably not relevant:

.photoUploaderForm {
    width: 94%; 
    margin-left: 17%;
    position: fixed;
    overflow: hidden;
}

@media (max-width: 563px) {
    .photoUploaderForm {
        width: 155%;
        margin-left: 4%;
    }
}

.inputFile {
    width: 80%;
}

@media (max-width: 563px) {
    .inputFile {
        width: 45%;
    }
}

.waitMessage {
    position: absolute;
    text-align: center;
    padding: 5px 2% 5px 2%;
    background: #ffffff;
    border: 1px solid;
    border-color: #000000;
    display: none;
}

.previewImage {
    border: 1px dashed;
    border-color: #cccccc;
    width: 50px;
    height: 50px;
    padding: 2px;
}

.deleteButton {
    top: 10px;
    font-size: 0.6em;
    font-weight: 800;
    position: absolute; 
    background: #ff8989;
    margin: -1px 0px 0px 2px;
    padding: 1px 5px 1px 5px;
    text-decoration: none;
    border: 1px solid;
    border-color: #ff0000;
}

.deleteButton:link, .deleteButton:visited { 
    color: #ffffff;
}

.deleteButton:hover {
    background: #ff0000;
    text-decoration: none;
}

.deleteButton:active {
    color: #ff0000;
    background: #ffa0a0;
}

Extra note: I really don't want to do anything with rich text fields, please do not suggest that I use them. I've noticed a lot of people suggest them for this type of functionality, but I find that their solutions cumbersome and unnecessary, as my method works without ever touching any rich text fields.

The only problem is the premature widget reload on the document deletion. For some reason, Product_Image_ID__c is not being seen as blank by the VF page after the deletePhoto() method returns.

Also here are some screenshots illustrating the problem in action:

Widget when no image is uploaded

Widget when no image is uploaded

Widget after uploading an image

Widget after uploading an image

Widget after clicking the delete button

Widget after clicking the delete button

Widget after refreshing the entire page again

Widget after refreshing the entire page again

1

Well, after much head-scratching, I tried changing

prod.Product_Image_ID__c = '';
prod.Product_Image__c = '';

to

prod.Product_Image_ID__c = null;
prod.Product_Image__c = null;

And that fixed it. Why it has to be null and not '' to update properly after the PageReference method returns, I don't know. I guess this is just another one of salesforce's myriad confusing "features"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.