function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
VintaraVintara 

Get VF page name in controller class?

Any way to get the Name or Id of the current Visual Force page from within the controller class?
Message Edited by Vintara on 08-13-2009 01:06 PM
Best Answer chosen by Admin (Salesforce Developers) 
IanRIanR

Am not an expert in Command pattern, but I *believe* so, yes...

 

So, we do not implement the components dynamically, rather we have a method called 'ResetWizardSteps' that dynamically determines which wizard steps to include, based on user input thus far...

 

public void ResetWizardSteps() { WizardStep[] steps = new WizardStep[] { };

 

steps.Add( CreateWizardStep('Start Page', Page.StartPage) ); if (sharedState.isMaster == true) { steps.Add( CreateWizardStep('Master Page', Page.MasterPage) ); } else { steps.Add( CreateWizardStep('Slave Page', Page.SlavePage) ); } this.Wizard.steps = steps; }

 

And let's assume the start page contains a component that passes through the attribute; the user then ticks a checkbox to select Master or Slave, which is assigned to the boolean flag in the SharedState object, and will be used in the method to help build the wizard... 

 

 

then in the MasterPage :

 

<apex:page Controller="wizardController"> <c:WizardBanner Title="{!Wizard.CurrentPageName}" /> <c:MasterComponent sharedState="{!SharedState}" /> </apex:page>

 

 

and finally, the component used in the Master page:

 

 

<apex:component> <apex:attribute name="sharedState" Type="SharedState" assignTo="sharedState" required="true" /> <!-- use pageBlock, Section, SectionItem, etc, for presentation --> <apex:inputField value="{!sharedState.Account.Name}" /> </apex:component>

 

Again - this is the *shape* of it; I've ommitted a lot of the fine detail to actually implement it (and I've probably got the exact syntax wrong, I'm doing it from memory)

 

Not sure if it is something that would fit your needs, but hopefully it's given you something to think about!

 

 

All the Best :)

Ian Randall

All Answers

ehartyeehartye

I'm guessing you mean visual force page name. The page reference class may be what you're looking for. Here's the link to the manual: http://www.salesforce.com/us/developer/docs/pages/Content/apex_pages_pagereference.htm 

Message Edited by ehartye on 08-13-2009 12:46 PM
VintaraVintara

Well I can get the page with ApexPages.currentPage()

 

But there is no method or property to actualy get the Id or Name of the page (and yes I mean a visual force page). 

VintaraVintara

I also can not parse out the name form the url returned by ApexPages.currentPage().getURL() because it includes the org' prifix which the user never otherwise sees.

 

So if the user is at

http://na1.salesforce.com/apex/MyPage

then ApexPages.currentPage().GetURL() returns 

http://na1.salesforce.com/apex/MyPrifix__MyPage

Message Edited by Vintara on 08-13-2009 02:49 PM
esbium99esbium99

I was just trying to do this exact thing and noticed there was no getName() method or something similar on the PageReference.  However, I did notice that on the $CurrentPage global variable, there is a Name field.  This is inconsistent at best!

 

I am thinking about using an assignTo on my custom Component and passing in the $CurrentPage.name as a param but this isn't very DRY if I have to do it on each page...

 

Parsing the URL is, of course, feasible but not ideal!  Come on SDFC, give us some consistent API's.... (end rant)

IanRIanR

In an MVC pattern, the View should be binding to the Controller; the Controller's concern is merely the application of business rules and subsequent provision of data; it should not be aware of the View.

 

I'm not sure of the exact requirement in your case: are you able to expose a string from within your Controller (or Extension) that contains the page's name?

esbium99esbium99

IanR,

 

While I generally agree with you, I disagree on one point.

A Controller should not be aware of the view in the sense that it doesn't care whether the data from the models is exposed via html, xml, JSON, etc... However, it is aware of the view in the sense that it directs the flow of the application in which case, it has to know "Which" view to forward/redirect to.  This is where my problem lies. 

 

After some further trials yesterday, I found out that the $CurrentPage.name variable is not actually using the current page, instead, it seems to be parsing the current URL.  This becomes a problem when you do a post and forward as the url doesn't change...  

 

With all that said, what I am really after is switching some CSS classes based on the current page to handle Navigation.  I want to show a progression through an application, visually, and have many controllers and pages that the navigation state needs to be persisted across.   My current path is to store the navigation state in the database and just move on.

 

Thoughts?

IanRIanR

Looks like we *mostly* agree on patterns, and can focus on the issue in hand ;)

 

It looks to me that your issue is around application flow; I have been working on a large VisualForce project for the last year; We have a substantial amount of data entry, and have created a number of wizards to direct the user through the (complex) process - have you investigated this approach? I think there are sample how-to guides on creating wizards...
 

Something else you can investigate is to create a 'SharedState' object and pass it around to the various controllers that sit behind your pages/components; so in our example, we have a WizardEngine class that holds a collection of 'WizardPage' objects (which store Name and URL as properties). The WizardEngine understands the order of the pages, and where to go if the user clicks Next or Prev;

 

 

Cheers,
Ian Randall

esbium99esbium99

Ian,

 

Thanks for replying and keeping this going.

 

I have looked at the Wizard approach and my understanding is that you have to use the same controller througout the 

wizard steps in order to maintain the state (viewstate) between requests.  This also relies on POSTS and not GET's.

 

In our situation, we cannot have 1 controller but still need a Wizard approach and since you cannot do POST's across controllers, we end up with GET requests and have to pass as much state as possible in the URL, hence no more viewstate.

 

So how do you currently pass the "SharedState" object between pages and controllers without a Classic Session object?  Store it in the DB?  That is what we are doing currently but its not "Smart" at the moment and just maintains a list of pages of where the user has been.

 

I would love to hear how you do it.  Maybe I am missing something very basic here... At least I hope I am!

 

Thanks again Ian!

IanRIanR

Hi,

It looks (at a very high level) like this:

 

IWizardListener - interface to describe the contract a WizardListener must fulfill, contains:

- Boolean NextPage() - true allows the user to navigate to the next page, false does not.

- Boolean PrevPage()

- Boolean CancelWizard()

- Boolean FinishWizard()

- Boolean FinishAndNew()

 

 

WizardListener - concrete class that implements the IWizardListener interface; this is the controller for your wizard pages, and will implement wizard-specific logic such as page validation (eg, enforce the user to complete this page before going to the next), etc...

- Create an instance of WizardEngine, and add 'this' as a listener...

 

WizardEngine - class that controls the flow of the wizard, contains

- IWizardListener[] listeners

- WizardStep[] steps

- Boolean NextPage() - method that invokes listener.NextPage for each listener in the collection...

- also, PrevPage, Cancel, Finish, etc... (as above) 

 

WizardStep - class to describe the individual step (page), contains

 - string Url

 - string Name

 

 

 

Now there's a whole heap of implementation detail I've not included here, but hopefully this gives you the shape of it...

 

In the pages, we have a 'WizardBanner' component that binds to the WizardEngine exposed by the controller, so we can render the current page Name, and expose 'Next', 'Prev' buttons as required.

 

Now, if you have a substantial amount of data you want to pass into various pages, you can create an object (e.g. 'SharedState') which contains that data. Then we can put the UI into components, which have dedicated controllers; we can pass the SharedState object as an attribute of the component and render them on on the page...

 

 

It's non-trivial, but once the framework is established, it's very powerful, and we've done some groovy things with rendering the wizards in pseudo-modal popups (using div tags) to provide a nicer UX...

 

 

HTH,

 

Ian Randall

esbium99esbium99

Ian,

 

Thanks for taking the time to write that up!  The only thing I still don't understand is how you pass the "state" between controllers (other than as request params)?  Is that possible?  The details you provided appear to point to the use of One Controller, not multiple.

 

Thanks again!

IanRIanR

Hi,

 

Sorry - SharedState is a badly named variable...

 

We don't concern ourselves with viewstate, because the WizardEngine handles all navigation, so we monitor things like 'currentUrl', and build a picture of nextUrl, prevUrl as per the order of the WizardStep array.

 

The purpose of 'SharedState' is to pass large amounts of data from the *sole* controller (our implementation of IWizardListener) across to all the various pages, and through into the controllers that sit behind the components...

 

 

So yes - each collection of pages (wizard) has a single instance of a controller; but the various components within those pages can have their own business logic in their own controllers... 

 

 

Let's say we have a SharedState like

 

 

public class SharedState { public Account MainAccount { get; set; } public boolean isMaster { get; set; } public string[] extraInfo { get; set; } }

 

Now let's say our wizard has 6 pages, and each page is fairly empty, just contains the wizard banner for navigation, and a custom component to define the page. We can pass the SharedState object as an attribute, so the user can create/edit things, those changes get passed back via our object into the main controller, and can get passed into the next component for the next chunk of work...

 

Just to reiterate: We absolutely don't concern ourselves with viewstate, or POST or GET instructions; We just navigate between pages using the baked-in PageReference object.

 

Just to double-check: You know that you can add parameters to the PageReference object, such as retUrl (for the return URL); and that if your controller exposes a method to the view of return type PageReference, then the View will navigate to the returned URL (or stay where it is if null is returned) ? I'm sure you did...

 

 

 

Is any of this making sense? I'm tying to explain 3 months of fairly steep learning curve in a couple of forum posts!

 

Cheers,

Ian

esbium99esbium99

Ian,

 

I think I understand what you have described.  Its similar to a Context Object in a Command pattern right?  I was just hoping that you could pass "State" information between "Page" controllers, not component controllers.  Having said that, I didn't think of implementing the pages as components which is what it sounds like you have done.   If that is what you have done, how do you include components dynamically?

IanRIanR

Am not an expert in Command pattern, but I *believe* so, yes...

 

So, we do not implement the components dynamically, rather we have a method called 'ResetWizardSteps' that dynamically determines which wizard steps to include, based on user input thus far...

 

public void ResetWizardSteps() { WizardStep[] steps = new WizardStep[] { };

 

steps.Add( CreateWizardStep('Start Page', Page.StartPage) ); if (sharedState.isMaster == true) { steps.Add( CreateWizardStep('Master Page', Page.MasterPage) ); } else { steps.Add( CreateWizardStep('Slave Page', Page.SlavePage) ); } this.Wizard.steps = steps; }

 

And let's assume the start page contains a component that passes through the attribute; the user then ticks a checkbox to select Master or Slave, which is assigned to the boolean flag in the SharedState object, and will be used in the method to help build the wizard... 

 

 

then in the MasterPage :

 

<apex:page Controller="wizardController"> <c:WizardBanner Title="{!Wizard.CurrentPageName}" /> <c:MasterComponent sharedState="{!SharedState}" /> </apex:page>

 

 

and finally, the component used in the Master page:

 

 

<apex:component> <apex:attribute name="sharedState" Type="SharedState" assignTo="sharedState" required="true" /> <!-- use pageBlock, Section, SectionItem, etc, for presentation --> <apex:inputField value="{!sharedState.Account.Name}" /> </apex:component>

 

Again - this is the *shape* of it; I've ommitted a lot of the fine detail to actually implement it (and I've probably got the exact syntax wrong, I'm doing it from memory)

 

Not sure if it is something that would fit your needs, but hopefully it's given you something to think about!

 

 

All the Best :)

Ian Randall

This was selected as the best answer
IanRIanR

one last thing: remember that you can include a custom controller behind each component to implement page-specific business logic and/or access different parts of the database.

 

You *can* put everything into the one big controller, but experience has led us to split these out - there is a max code limit on a single Apex Class which we keep hitting for one or two of our older wizard Controllers - leads to fairly major refactoring exercises :(

 

 

Cheers,