+ Start a Discussion
sean.gorman@ipc.comsean.gorman@ipc.com 

Using a VisualForce page to Mass select referential data

I have a custom object called Demo Booking (DB).

I have another custom object as a related list called Demo Booking Object (DBO).

 

DBO is related to DB and to another table called Demo Object (DO). Demo objects are items that we use in our demo. They have a type and a minutes.

 

For clarity:

DBs are the BOOKINGS. DO are the OBJECTS and DBOs are the link between the two (like Opportunities, Products and Opportuntiy Products)

 

The user has to create a new Booking (DB). Once they have done that they have to select the Objects (DO) using the DBO related list. This uses the standard SFDC "Search" then "Save and New" method. The issue is that most Sales people do not really know these objects and searching for them is clunky.

 

I created a new visulaforce page on top of the DO object which displays the Objects along with their name and a checkbox.

 

The idea being that they open the new page from their new Booking and can just check the box next to the items to associate Booking#1 with Objects #1,2,3,5,6,9.

 

Finally: DBO rolls up the selected DOs' minutes to give us an approximate time for the demo.

 

If I go to the apex page this display correctly.

 

 

public class DemoObjectsShowAll{
     
        //Collection of the class/wrapper objects cObject
        public List<cObjects> objectList {get; set;}
     
        //This method uses a simple SOQL query to return a List of Objects
        public List<cObjects> getAllDemoObjects() {
            if(objectList == null) {
                objectList = new List<cObjects>();
                for(SE_Demo_Objects__c d :    [SELECT Name, Minutes_for_Demo__c, Product_Type__c FROM SE_Demo_Objects__c LIMIT 50]) {
                    // As each object is processed we create a new demo object and add it to the ObjectList
                    objectList.add(new cObjects(d));
                }
            }
            return objectList;
        }
     
     
        public PageReference processSelected() {
     
                    //We create a new list of Objects that we be populated only with Objects if they are selected
            List<SE_Demo_Objects__c> selectedObjects = new List<SE_Demo_Objects__c>();
     
            //We will cycle through our list of cObjects and will check to see if the selected property is set to true, if it is we add the Objects to the selectedObjects list
            for(cObjects cObj : getAllDemoObjects()) {
                if(cObj.selected == true) {
                    selectedObjects.add(cObj.Obj);
                }
            }
     
            // Now we have our list of selected objects
            System.debug('These are the selected Objects...');
            for(SE_Demo_Objects__c Obj : selectedObjects) {
                system.debug(Obj);
            }
            return null;
        }
     
     
        // This is our wrapper/container class. 
        public class cObjects {
            public SE_Demo_Objects__c Obj{get; set;}
            public Boolean selected {get; set;}
     
            //This is the contructor method. 
            public cObjects(SE_Demo_Objects__c d) {
                Obj = d;
                selected = false;
            }
        }
    }

 

<apex:page Controller="DemoObjectsShowAll" sidebar="false">
    <apex:form >
        <apex:pageBlock >
            <apex:pageMessages />
            <apex:pageBlockButtons >
                <apex:commandButton value="Add items to your Demo" action="{!processSelected}" rerender="table"/>
            </apex:pageBlockButtons>

            <apex:pageBlockTable value="{!allDemoObjects}" var="demo">

                <!-- This is our selected Boolean property in our wrapper class -->
                <apex:column >
                    <apex:inputCheckbox value="{!demo.selected}"/>
                </apex:column>
                <!-- This is how we access the values within our container/wrapper -->

                <apex:column value="{!demo.obj.name}"/>
                <apex:column value="{!demo.obj.Minutes_for_Demo__c}"/>
                <apex:column value="{!demo.obj.Product_Type__c}"/>
            </apex:pageBlockTable>      
        </apex:pageBlock>
    </apex:form>
</apex:page>

 

Now - I want to associate this with my related list and, of course, it doesn't associate because it is not a standard controller. How do I fix this?

 

The way I see it I have 2 issues:

#1 is that I need to write the the DBO rows from this page so I will need to associate them with the DB ID. (I have not done this as I wanted to do #2 first)

#2 This page needs to plug into the NEW button on the DBO related list on DB but it is related to DO.

 

 

Best Answer chosen by Admin (Salesforce Developers) 
SFDCMattSFDCMatt

Hey Sean - You're on the right track it appears. Code looks good, I've written this concept several times. You've got to main options:

 

  1. Make it a standard controller extension of the junction object -> <apex:page standardController="Demo_Booking_Object__c" extension="DemoObjectsShowAll" sidebar="false">

  2. Create a custom button on the junction object, do a very simple redirect to your custom page, as it's written, then put that button on the related list, and remove the standard New button. This can be done through the page layout editor.

I usually go with option 2, for what it's worth.

All Answers

SFDCMattSFDCMatt

Hey Sean - You're on the right track it appears. Code looks good, I've written this concept several times. You've got to main options:

 

  1. Make it a standard controller extension of the junction object -> <apex:page standardController="Demo_Booking_Object__c" extension="DemoObjectsShowAll" sidebar="false">

  2. Create a custom button on the junction object, do a very simple redirect to your custom page, as it's written, then put that button on the related list, and remove the standard New button. This can be done through the page layout editor.

I usually go with option 2, for what it's worth.

This was selected as the best answer
sean.gorman@ipc.comsean.gorman@ipc.com

Hi Matt,

 

Hope the vacay was good.

 

Thanks for the reply. I am working to do option 2 now..

 

Sean

sean.gorman@ipc.comsean.gorman@ipc.com

Now I have to pass the ID of the Booking into the new page so that I can insert a number of records into the connecting table.

 

How can I get the ID of the booking into the visualforce page?

 

Where I am now: Button on the related list of Demo_Booking_Objects opens the Demo_Objects visualforce page as there is a new custom button that looks like this:

{!requireScript("/soap/ajax/26.0/apex.js")}

window.location.href =" https://c.cs7.visual.force.com/apex/AddDemoObjects"

 

 

 

 

sean.gorman@ipc.comsean.gorman@ipc.com

Just in case someone finds this and wants the full solution. I went with Matt's second choice: I passed the Parent Objects ID in the URL. This button is on the JOINING OBJECT:

 

{!requireScript("/soap/ajax/26.0/apex.js")}

var strURL = window.location.href

strURL = strURL.substr(-15,15)

window.location.href ="https://c.cs7.visual.force.com/apex/AddDemoObjects?ID="+strURL

 This is a bit wrong as the ID could be 18 characters but my users are unlikely to do that. (I plan on fixing it later though)

 

Then the code for the page:

<apex:page Controller="DemoObjectsShowAll" sidebar="false" id="SEBook">
    <apex:form >
        <apex:pageBlock >
            <apex:pageMessages />
            <apex:pageBlockButtons >
                <apex:commandButton value="Add items to Demo" action="{!processSelected}" rerender="table"/>
                <apex:commandButton id="btnCancel" value="Cancel"/>
            </apex:pageBlockButtons>

            <apex:pageBlockTable value="{!allDemoObjects}" var="demo">

                <!-- This is our selected Boolean property in our wrapper class -->
                <apex:column >
                    <apex:inputCheckbox value="{!demo.selected}"/>
                </apex:column>
                <!-- This is how we access the contact values within our cContact container/wrapper -->

                <apex:column value="{!demo.obj.name}"/>
                <apex:column value="{!demo.obj.Minutes_for_Demo__c}"/>
                <apex:column value="{!demo.obj.Product_Type__c}"/>
                <apex:column value="{!strBookingID}" rendered="FALSE"/> 

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

 

I am using the strBookingID in the controller so that I can pull the data into the save function.

 

 

 

The controller:

 

public class DemoObjectsShowAll{
     
        //Our collection of the class/wrapper objects cContact
        public List<cObjects> objectList {get; set;}
        public integer nCount;
        public map<id,id> dbos = new map<id,id>();
        public string strBookingID {get;set;}
        public boolean bAdd;
        public DemoObjectsShowAll()
        {
                strBookingID = ApexPages.currentPage().getParameters().get('ID');
        }

        //This method uses a simple SOQL query to return a List of Contacts
        public List<cObjects> getAllDemoObjects() {
            if(objectList == null) {
                objectList = new List<cObjects>();
                for(SE_Demo_Objects__c d : [SELECT Name, Minutes_for_Demo__c, Product_Type__c FROM SE_Demo_Objects__c LIMIT 50]) {
                    // As each object is processed we create a new demo object and add it to the ObjectList
                    objectList.add(new cObjects(d));
                }
            }
            return objectList;
        }
     
     
        public PageReference processSelected() {
     
                    //We create a new list of Objects that we be populated only with Objects if they are selected
            List<SE_Demo_Objects__c> selectedObjects = new List<SE_Demo_Objects__c>();
     
            //We will cycle through our list of cObjects and will check to see if the selected property is set to true, if it is we add the Objects to the selectedObjects list
            for(cObjects cObj : getAllDemoObjects()) {
                if(cObj.selected == true) {
                    selectedObjects.add(cObj.Obj);
                }
            }

            // Find the existing items
            List<SE_Demo_Booking_Object__c> SavedDBO = [Select SE_Demo_Object__c from SE_Demo_Booking_Object__c where SE_Demo_Booking__c = :strBookingID];

            // Now we have our list of selected objects and a list of existing ones
            // we can sort through top prevent multiple items of same type saved

            // therefore loop through them all
            List<SE_Demo_Booking_Object__c> SEDBO = new List <SE_Demo_Booking_Object__c>();
            for(SE_Demo_Objects__c Obj : selectedObjects) {
                // by default we want to save every record
                bAdd = TRUE;

                // need to find the ID of the SE_Demo_Booking Items
                for (SE_Demo_Booking_Object__c DBObj : SavedDBO) {

                    // If there are saved items check they are not already saved on this booking
                    //... otherwise move on
                    if (SavedDBO.size() > 0)
                    {
                        // if the IDs match
                        if (Obj.ID == DBObj.SE_Demo_Object__c) {
                           // Do not save
                            bAdd = FALSE;
                        }
                    }
                }

                if (bAdd) { 
                    // use SEDBO to add selected rows to object
                    // add one se_demo_booking_object__c row per se_demo_object record if bAdd = TRUE
                    sedbo.add(new Se_Demo_Booking_Object__c(SE_Demo_Object__c=Obj.ID, SE_Demo_Booking__c=strBookingID));
                }

            }
            insert sedbo;

            PageReference newPage;
            newPage = new PageReference('/' + strBookingID);
            return newPage.setRedirect(true);

        }

        // This is our wrapper/container class. I
        public class cObjects {
            public SE_Demo_Objects__c Obj{get; set;}
            public Boolean selected {get; set;}
     
            //This is the contructor method. When we create a new cObject object we pass a Object that is set to the con property. We also set the selected value to false
            public cObjects(SE_Demo_Objects__c d) {
                Obj = d;
                selected = false;
            }
        }
    }

 

 

PS - Thank you hugely Matt.

rajafurajafu

Hi Sean,

 

I have a doubt in the above code. I want to know are you updating all the selected Demo Object records to Demo Booking Objects?? if im wrong can you please send me the functionality.

 

Thanks in advance,

Rajiv.

sean.gorman@ipc.comsean.gorman@ipc.com

Hi Rajiv,

 

No - the page has a checkbox per object and when the user selects an object it is added to the related list on the booking. I have also added some code to prevent duplicates being added because you cannot have a demo of a widget twice (gets kinda boring)

 

The selection code is here:

        public PageReference processSelected() {
     
                    //We create a new list of Objects that we be populated only with Objects if they are selected
            List<SE_Demo_Objects__c> selectedObjects = new List<SE_Demo_Objects__c>();
     
            //We will cycle through our list of cObjects and will check to see if the selected property is set to true, if it is we add the Objects to the selectedObjects list
            for(cObjects cObj : getAllDemoObjects()) {
                if(cObj.selected == true) {
                    selectedObjects.add(cObj.Obj);
                }
            }

 

rajafurajafu

Thanks for the explanition Sean :) . So you have written Javascript for the related list button right .

Thanks for the post it has helped me a lot as im implementing the same scenario.

sean.gorman@ipc.comsean.gorman@ipc.com

Yes - Posted it in Post 4 of this discussion

Mayank_JoshiMayank_Joshi

Hey Sean ,Matt and Rajiv -

 

I am also doing similar kind of architecture development , and am already in mid of it . The reason , I am here because I was looking for something similar (where after Multi selection ,it will saved/Updated to Related List .

 

So , your implementation is really helping me in achieving it . And yes ,otherwise I had plans to create New VF Page where I can store selected records then I can put this second page into Object Page Layout (under Visual Force section ) as Related List . So , if everything will goes well (I can hope so :) .. ) then i will save one page +controller . 

 

Also , For calling VF Page with Parent Record ID ,we can use URL in Custom Button like this(if that helps you):

 

Custom Button or Link URL : /apex/ModelListPage?id={! Opportunity.Id }
pretty simple insn't it !!

So guys ,please hang in there if I need help from you .Also, I will post back once I finishes it off .

Mayank_JoshiMayank_Joshi
It's Done ,thank for all the above approach discussed . :)