2

I have a VF Page that uses a standard Account controller and a controller extension to show information from a related object (Contact) - similar in function to a related list.

I need to have multiple versions of this page for different related objects, for example Opportunity and a custom object. The various pages will be embedded in a standard page layout, so each must be a separate page (not a VF Component).

The VF page and the controller extension logic make heavy use of Fieldsets, so the difference between the various VF pages will be minimal: each will reference a different title, a different fieldset, and each may contain different command buttons. I would extract most of the common code into a custom component, however, I can't find a way to pass an arbitrary set of command buttons into a custom component. Instead, I will probably use apex:composition to allow the various VF pages to share a common template.

The problem is the controller extension - the logic is very generic, but I need to pass the related object type (e.g., Contact, Opportunity) into the controller extension. Here are solutions I've considered and the issues with each:

  1. Pass the object type as an URL param- This is a security risk; I want the object type to be hardcoded per page.

  2. Use a hidden input field to pass the object type name- This is a security risk; I want the object type to be encoded only in the VF markup and not to be settable from the client.

  3. Pass a value using apex:param. While I can add an apex:param to a VF page, values passed this way are ignored, even if using assignTo to reference a declared property. To correctly use an apex:param, I would need to move my code into an apex:component and use apex:attribute to match the param to a controller property. But as outlined above, I can't use a component, because I need to include an arbitrary set of command buttons in my page markup. If I could mix apex:insert with apex:component I'd be fine, but templates must be VF Pages.

  4. Use an existing attribute of the VF page. tabStyle would be perfect, since it is meant to refer to a standard or custom object type. However, I can find no way for the controller code to determine the tabStyle declared on the apex:page element.

  5. Render the whole page as Dynamic VF from my controller code. This is a non-starter for many reasons I won't cover here.

So I'm stuck. I need to pass a parameter to my controller extension, but it cannot be accessible from the client, leaving only the VF page markup. I cannot find any way to pass data from markup into the controller. Due to the nature of the templating I need to do (command buttons), I can find no workable way to move my markup into a custom VF component to enable the use of apex:param. Have I overlooked any options?

Note: While typing this up, I can up with one more (bad) idea:

  • Use a VF Page naming convention in combination with ApexPages.CurrentPage().getURL() to allow the controller to infer the related object name from the VF Page. Feels like bad design, but may get the job done. Will experiment but clearly not optimal solution.
1

How about using a naming convention, but including one level of separation by use of a static apex map, that maps a page name to a given controller? That way the page name need not adhere to a given standard as long as the map is maintained. You could even move that map into a custom setting if you desired.

Also you mention that using a hidden input field would be a security risk, is that really the case? Seems to me that although the client could tell the controller to load a different object type, that type would be incompatible with the fieldsets on the page, and if you're dealing with a particular record then the ID wouldn't match to boot.

Last but not least, if you are dealing with a particular record, why not drive the object from the object prefix in the record's ID? If not, using tabstyle should result in an object prefix being present in the URL (e.g. 006 for oppties) if memory serves.

  • Not ideal, but making the mapping of VF page to object type feels much better to me than a naming convention. I think a custom setting is right for this. – Jason Clark Jan 21 '14 at 22:53
  • I often feel that using naming conventions for code based things is sort of dirty, though EmberJS has turned me around on that recently. There it can make life much easier! – Matt Lacey Jan 21 '14 at 23:09
1

I need to have multiple versions of this page for different related objects, for example Opportunity and a custom object. The various pages will be embedded in a standard page layout, so each must be a separate page (not a VF Component).

Not necessarily true: Is it possible to create a reusable inline Visualforce page? (maybe you'll get some inspiration from other answers to this question). But that's a side note, if the pages will not contain much content beside the call to component - the maintenance overhead will be minimal.

I need to pass the related object type (e.g., Contact, Opportunity) into the controller extension.

Make an Id-based check? I mean - you have to pass the Id / whole object to the component, right?

Id i = '0037000000TWREI';
// (...)
if(i != null && i.getSobjectType() == Contact.SObjectType){
    // (...)
}

Bonus points for the fact that once you have the Contact.SObjectType you could fetch all fieldsets for it and access them in the Apex (of course it can be hardcoded too but well):

Map<String, Schema.FieldSet> FsMap = 
Schema.SObjectType.Account.fieldSets.getMap();

You can achieve similar result by cutting first 3 characters of Id and comparing with getKeyPrefix() but the Id way is somewhat cleaner just in case they'll ever, ever change prefixes ;) Plus you won't hardcode magic "001" anywhere.

  • I can't use the ID because that's the parent, which is always Account. I need one version of the page to get info on related contacts, another to get info on related opportunities, etc. But the passed ID will aways be an account. – Jason Clark Jan 20 '14 at 21:43
  • Uh, I've misread completely then :/ Back to the drawing board... – eyescream Jan 20 '14 at 21:44
  • 1. Use base virtual class (possibly even abstract one) and inherit it once for each ctrl extension? Could be very simple overrides, just call original constructor with selected sObjectType passed as param. 2. Use <apex:page action="{!applyContactFlavour}" (stupid idea because action is a security risk and determined user could fake it from JS). – eyescream Jan 20 '14 at 21:53

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.