creating multi-page data entry controllers

33
Building multi-page data entry Controllers Beyond Oz - What to do when a simple Wizard is not enough Stuart Greenberg, OppenheimerFunds, Inc., Technical Lead @Stu_GuiGumdrops

Upload: salesforce-developers

Post on 24-Jun-2015

461 views

Category:

Technology


1 download

DESCRIPTION

You've built a sample wizard with Visualforce, but what if your business requirements are more complex: saving across multiple objects, including Tasks and Events? Join us to learn how one customer solved this with a custom framework, using as few Apex controllers as possible, and dividing code classes into reusable modules.

TRANSCRIPT

Page 1: Creating Multi-Page Data Entry Controllers

Building multi-page data entry ControllersBeyond Oz - What to do when a simple Wizard is not enoughStuart Greenberg, OppenheimerFunds, Inc., Technical Lead@Stu_GuiGumdrops

Page 2: Creating Multi-Page Data Entry Controllers

All about OppenheimerFunds, Inc.

Click to add brief company overview here. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed lectus tortor, pulvinar sit amet blandit ac, bibendum vitae sapien.

▪ Click to add implementation highlights; no more than four

▪ Click to add implementation highlights; no more than four. Lorem ipsum dolor sit amet, consectetur adipiscing elit.

▪ Click to add implementation highlights; no more than four

▪ Click to add implementation highlights; no more than four

Since the original Oppenheimer fund was first offered to the public in 1959, OppenheimerFunds, Inc. (OFI) has grown into one of the largest and most reputable investment management firms in the country. Today, a subsidiary of Massachusetts Mutual Life Insurance Company (MassMutual), OFI and its subsidiaries offer a broad array of products and services to individuals, institutional investors and corporations worldwide. OFI provides advisory services to the Oppenheimer mutual funds and OFI Global Asset Management provides services to institutional clients. OFI, including subsidiaries, managed more than $222 billion in assets for more than 12 million shareholder accounts, including sub-accounts, as of September 30, 2013.

For more than 50 years, OFI has embraced an investment culture that has produced results, is sustainable, and reflects its commitment to being effective stewards of capital. A high conviction asset manager, OFI has a history of providing innovative investment strategies to its investors. Four core beliefs lie at the heart of the OFI investment culture: active management can deliver better outcomes, independent investment boutiques lead to better ideas, a global perspective is critical, and knowing the difference between risk & risky.

OFI’s investment professionals are a collection of distinct, yet collaborative, teams built to enable a free exchange of ideas and to uncover opportunity wherever it lies; even where others see a disparate set of facts, events or trends. OFI is redefining what the world can expect from an asset manager, and stands united on its mission to turn its unconventional wisdom into value for investors.

Page 3: Creating Multi-Page Data Entry Controllers

Who’s who and what’s whatStuart Greenberg

▪ Professional programmer since 1975▪ Past Senior Programmer, PC Magazine Labs ▪ Currently Tech Lead, OppenheimerFunds, Inc. ▪ Pennsylvania Railroad engineer

And you?▪ Visualforce developer?▪ Written wizards or multi-page data entry?▪ It’s late, all the other sessions were full

and I just wanted to sit down?

Page 4: Creating Multi-Page Data Entry Controllers

Single vs. wizard vs. multi-page data entrySingle page

▪ Enter data / click save

Page 5: Creating Multi-Page Data Entry Controllers

Single vs. wizard vs. multi-page data entryWizard

▪ Enter data for step 1 / click next▪ Enter data for step 2 / click next▪ Etc.

123

Page 6: Creating Multi-Page Data Entry Controllers

Single vs. wizard vs. multi-page data entryMulti-page

▪ Enter data on main page / click option▪ Select option / Enter data on new page / click return▪ Select option / Enter data on another page / click return▪ Click save

Page 7: Creating Multi-Page Data Entry Controllers

The problem▪ Break the limit of one Contact/Lead and/or one other object

attached to Events and Tasks (Multi-Who / Multi-What)▪ Allow users to select objects in any order and skip selection of

objects they don’t want▪ Do not require the saving of Events and Tasks prior to

selecting objects

Page 8: Creating Multi-Page Data Entry Controllers

The solution▪ Custom object (ActivitySidecar) connected to Events and

Tasks using the WhatId▪ Selection pages for all associated objects▪ Multi-page data entry to keep all data in memory and save at

the end

Page 9: Creating Multi-Page Data Entry Controllers

Object structure

Page 10: Creating Multi-Page Data Entry Controllers

Overall structure

Controllers must be the same across pages.

Classes instantiated as needed.

Interfaces allow polymorphic functionality

Page 11: Creating Multi-Page Data Entry Controllers

DEMO

Page 12: Creating Multi-Page Data Entry Controllers

GottasWachagottadew…

▪ Selections must be queued▪ All queued data must be saved at the same time▪ Page code must be setup correctly

Page 13: Creating Multi-Page Data Entry Controllers

Selectable Objectpublic class OFI_SelectableObject { public Boolean isSelected {set;get;} public Boolean isEnabled {set;get;} public Boolean isInactive {set;get;} public Boolean isPrimary {set;get;} public String tag {set;get;} public Object obj {set;get;}

Page 14: Creating Multi-Page Data Entry Controllers

Queueing selections// Lists Selectable Objects of the related objects// The tag property is used to indicate:// an existing object (tag = '')// an object to be added (tag = 'A')// an object to be deleted (tag = 'D')public List<OFI_SelectableObject> selProducts {get;set;}public List<OFI_SelectableObject> selContacts {get;set;}public List<OFI_SelectableObject> selFirms {get;set;}public List<OFI_SelectableObject> selUsers {get;set;}public List<OFI_SelectableObject> selFinancialAccounts {get;set;}public List<OFI_SelectableObject> selOpportunities {get;set;}public List<OFI_SelectableObject> selCampaigns {get;set;}public List<OFI_SelectableObject> selCases {get;set;}

Page 15: Creating Multi-Page Data Entry Controllers

Selecting itemspublic PageReference selectItems() { Integer i; for (OFI_SelectableObject record: recordsToDisplay) { if (record.isSelected) { if (!existingContactIds.contains(record.getContact().Id)) { // Not an existing selection for (i = 0; i < currentSelections.size(); i++) { // Check for undelete if (currentSelections[i].getActivityContact().Contact__c == record.getContact().Id && currentSelections[i].tag == 'D') { currentSelections[i].tag = ''; break; } } if (i == currentSelections.size()) { // Add new selection Activity_Contact__c actContact = new Activity_Contact__c(); // Set the Contact Id and Object. The Sidecar will be set when the Activity is saved. actContact.Contact__c = record.getContact().Id; actContact.Contact__r = record.getContact(); OFI_SelectableObject selObj = new OFI_SelectableObject(actContact); selObj.Tag = 'A'; currentSelections.add(selObj); } existingContactIds.add(record.getContact().Id); // Add to existing Ids to enable checkmark } } } return null;}

Page 16: Creating Multi-Page Data Entry Controllers

Savingtry { upsert evt;} catch (DMLException e) { ApexPages.addMessage(new ApexPages.Message(ApexPages.SEVERITY.ERROR, e.getLineNumber() + e.getMessage())); return false;}

If (sidecarId == null) { Activity_Sidecar__c sidecar = [SELECT Id FROM Activity_sidecar__c WHERE Activity_Id__c = :evt.Id LIMIT 1]; evt.WhatId = sidecar.Id;}

// Add / Delete Products

List<Activity_Product__c> delProds = new List<Activity_Product__c>();List<Activity_Product__c> addProds = new List<Activity_Product__c>();for (OFI_SelectableObject o : sidecarEditor.selProducts) { if (o.tag == 'D') { delProds.add(o.getActivityProduct } else { if (o.tag == 'A') { o.getActivityProduct().Activity_Sidecar__c = evt.WhatId; addProds.add(o.getActivityProduct()); } }}

if (delProds.size() > 0) { delete(delProds);}

if (addProds.size() > 0) { insert(addProds);}

Page 17: Creating Multi-Page Data Entry Controllers

▪ An Action must be added if any initialization is necessarywhen the page opens.

▪ All controllers must be the same to keep their constructors from being called when a new page is opened.

Event Edit Page

Task Edit Page

Selection Page

<apex:page controller="ActivityEditPage2Controller" extensions="ActivityObjectSelController,VFFileUpload2" title="Calendar Event Edit">

Page code

<apex:page controller="ActivityEditPage2Controller" extensions="ActivityObjectSelController,VFFileUpload2" title="Calendar Task Edit">

<apex:page controller="ActivityEditPage2Controller" extensions="ActivityObjectSelController,VFFileUpload2" action="{!initObjectSelector}" title="Activity Contact Selection">

Page 18: Creating Multi-Page Data Entry Controllers

Force instantiation on new sessionYou need to use the following when you wish to start a new multi-page session:

PageReference page = new PageReference('/apex/TaskEditPage'); page.setRedirect(true);

Page 19: Creating Multi-Page Data Entry Controllers

GotchasGotchasgonnagetcha…

Page 20: Creating Multi-Page Data Entry Controllers

You can’t leave the pageThe view state is maintained as long as the user doesn’t navigate to a page that has a different set of controllers.The solution: Javascript

Page 21: Creating Multi-Page Data Entry Controllers

Avoid the message on “Good” clicksFor example, the following was added to the Page Block Buttons:

<apex:pageBlockButtons location="top" >

<apex:commandButton value="Select and Return"

action="{!SelectAndReturn}" onclick="setShowExitMessage(false);"/>

<apex:commandButton value="Select and Add More"

action="{!SelectItems}" onclick="setShowExitMessage(false);" rerender="thePage"/>

<apex:commandButton value="Return" action="{!cancelSelection}"

onclick="setShowExitMessage(false);"/>

</apex:pageBlockButtons>

Page 22: Creating Multi-Page Data Entry Controllers

<script type="text/javascript">

var isExitMessageTurnedOn = true; var showExitMessage = true; var isChrome = false; var isSafari = false; var ua = navigator.userAgent.toLowerCase();

if (ua.indexOf('safari') != -1){ if(ua.indexOf('chrome') > -1){ isChrome = true; } else { isSafari = true; } }

function setShowExitMessage(val) { showExitMessage = val; }

function isJavascriptElement() { if (isChrome || isSafari) return false;

var elem = document.activeElement; var e;

if (elem.outerHTML) { e = elem.outerHTML; } else { if (XMLSerializer) { e = new XMLSerializer().serializeToString(elem); } } if (e.indexOf('javascript') > -1) return true; return false; }

window.onbeforeunload = function(event) { if(isExitMessageTurnedOn == true) { if(showExitMessage == true && isJavascriptElement() == false) { return 'Any edits will be lost.'; } else { showExitMessage = true; } } }

</script>

Page 23: Creating Multi-Page Data Entry Controllers

Be careful using classes▪ Classes called from the controllers can be instantiated

multiple times.▪ Be sure to set the variables using the classes to null when

finished.

Page 24: Creating Multi-Page Data Entry Controllers

Example

Destroy Selectors after each accessOf a selection page.

Page 25: Creating Multi-Page Data Entry Controllers

Return URLs▪ Do not use the Return URL to go back to the initial page.▪ Use the URL of the page itself.▪ For example:

// Cancel selection and return to the previous page.public pageReference cancelSelection() { destroySelector(); if (mainController.isTask) { return page.TaskEditPage2; } else { return page.EventEditPage2; }}

Page 26: Creating Multi-Page Data Entry Controllers

FugetaboutitsPatient: Doc, it hurts when I do this.Doctor: Don’t do that.

Page 27: Creating Multi-Page Data Entry Controllers

The View State size▪ The View State is the data that is held between pages.▪ Up to 135K of data. ▪ An exception is thrown if the limit is exceeded.▪ Cannot hold attachments, for example.

Page 28: Creating Multi-Page Data Entry Controllers

Time outs▪ Sessions sitting too long can be timed out and data lost.▪ Use multi-page data entry only for quick tasks.

Page 29: Creating Multi-Page Data Entry Controllers

AlternativesUse multi-page data entry when you really have toHere are some alternatives:

▪ One page with hidden or collapsible sections▪ A wizard flow based on initial selections▪ Visual Workflow▪ Dynamically generated search and select components

Page 30: Creating Multi-Page Data Entry Controllers

Conclusion▪ Multi-page data entry is possible with the proper care▪ Use multi-page data entry once you’ve exhausted more

mainstream options▪ Nothing is impossible… Impossible just takes a bit longer

Page 32: Creating Multi-Page Data Entry Controllers

Stuart Greenberg

OppenheimerFunds, Inc., Technical Lead

@Stu_GuiGumdrops

Page 33: Creating Multi-Page Data Entry Controllers