+ Start a Discussion
Jon KeenerJon Keener 

Use of the actionSupport tag with the "onchange" event

I've been running into some issues with the "onchange" event triggering so I've been working on a simplified version of a form based on another thread (Toggle display based on Picklist selection ) in the Visualforce forum.  I have run into a number of issues and questions surrounding the code below.
 
General Description: The code, as it is right now, does not work as intended. 
The goal of this code is that:
When the LeadSource dropdown is changed to "Web", the "Details" outputPanel should become visible.  Also, when an account is added, the "Details2" outputPanel should appear.  if the LeadSource is changed to something other than "Web", the "Details" outputPanel should disappear, and if the account is removed, the "Details2" outputPanel should disappear.
 
 
1.  Currently, if both InputFields are on the form, neither of the actionSupports appear to be firing correctly.  If the LeadSource is changed to "Web", the "Details" outputPanel does not appear.
 
2.  If I comment out the section for Test 2, between the comments <!-- Test 2 - Using Account --> and <!-- End of Test 2 - Using Account field --> The LeadSource picklist works correctly in Firefox.
 
3.  When number 2 is done, and things are working correctly in Firefox, they do not work in IE6.  (I haven't tried it in IE7 yet)
 
4.  I have not gotten Test 2 to work successfully, even when commenting out Test 1.  My specific concern about Test 2 is when does the "onchange" event fire for a Lookup Field?  I am hoping that it is when the field value changes, and not when I leave to field.  In my real code, I am trying to enable/disable command buttons based on this being populated or not populated.
 
 
Thanks for any assistance!
Jon Keener
 
 
The Page is:
Code:
<apex:page id="step4" controller="testpage" tabstyle="Contact">
<apex:form>

<apex:pageBlock title="test">

<!-- Test 1 - Using Picklist -->
<apex:pageBlockSection title="test 1">
<apex:panelGrid columns="2" columnClasses="labelCol,dataCol">
<apex:outputLabel value="Picklist 1" for="picklist1" />
<apex:outputPanel>
<apex:actionSupport event="onchange" action="{!ToggleTrue}" rerender="details" />
<apex:inputField id="picklist1" value="{!testing.LeadSource}" />
</apex:outputPanel>
</apex:panelGrid>
</apex:pageBlockSection>
<!-- End of Test 1 - Using Picklist -->


<!-- Test 2 - Using Account -->
<apex:pageBlockSection title="test 2">
<apex:panelGrid columns="2" columnClasses="labelCol,dataCol">
<apex:outputLabel value="Parent Account" for="Acct" />
<apex:outputPanel>
<apex:actionSupport event="onchange" action="{!ToggleTrue2}" rerender="details2" />
<apex:inputField id="Acct" value="{!testing.Account}" />
</apex:outputPanel>
</apex:panelGrid>
</apex:pageBlockSection>
<!-- End of Test 2 - Using Account field -->


<apex:outputPanel id="details">
<apex:pageBlockSection title="Details" rendered="{!hideshow}">
<apex:panelGrid columns="2" columnClasses="labelCol,dataCol">
<apex:outputLabel value="Name" for="personname" />
<apex:inputField id="personname" value="{!testing.Ownerid}" required="false" />
</apex:panelGrid>
</apex:pageBlockSection>
</apex:outputPanel>

<apex:outputPanel id="details2">
<apex:pageBlockSection title="Details 2" rendered="{!hideshow2}">
<apex:panelGrid columns="2" columnClasses="labelCol,dataCol">
<apex:outputLabel value="Email" for="Email" />
<apex:inputField id="Email" value="{!testing.Email}" required="false" />
</apex:panelGrid>
</apex:pageBlockSection>
</apex:outputPanel>


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

 
The Controller Code is:
 
Code:
public class testpage {

Contact testing;

boolean testval = false;
boolean testval2 = false;

public String ToggleTrue() {
if ( testing.LeadSource =='Web') {
testval = true;
} else { testval = false; }
return null;
}

public Contact gettesting() {
if (testing == null) testing = new Contact();
return testing;
}

public Boolean gethideshow() {
return testval;
}

public String ToggleTrue2() {
if ( testing.Account != null) {
testval2 = true;
} else { testval2 = false; }
return null;
}

public Boolean gethideshow2() {
return testval2;
}


}

 
 
 


Message Edited by Jon Keener on 12-12-2007 11:14 AM
dchasmandchasman
OK there are a few things going wrong below:

- <apex:actionSupport> is a child helper component that needs to be a direct child of the component you are trying to hook which means your placement below is wiring up the onchange event handler for the <apex:outputPanel> and not the <apex:inputField> which is not what you want. BTW this "works" in FF because of differences in default event handling/bubbling but it is still not going to be what you want.

- <apex:inputField> in Winter '08 has very limited support for <apex:actionSupport> - only onblur can currently be wired up (yes I know how weird/arbitrary that seems :-). We have already fixed that in Spring' 08 and will also be introducing <apex:actionFunction> that will let you really take total control of things.



Message Edited by dchasman on 12-12-2007 11:37 AM
Jon KeenerJon Keener

I moved the actionSupports below the InputFields and changed the event to onblur.  Overall, I'm seeing similar results as I had previously.

1.  If both Test 1 and Test 2 are uncommented, neither Test 1 or Test 2 work correctly in Firefox or IE.

2.  If Test 2 is commented out, Test 1 works correctly (slightly different due to using onchange vs. onblur) in Firefox.  Test 1 still does not work correctly in IE6.

3.  Still not able to get Test 2 to work at all in IE or Firefox, with or without Test 1 commented out.  (This could be an issue in my controller code, if it's firing, but the code is pretty simple, so at the moment, I'm assuming it is not firing)

If you think I should wait for Spring 08 release for this, let me know.  What I'm trying to enable in my real app is the following:

 

This wizard will replace the "New" functionality, and may start with any of the three lookup fields on top autopopulated (based on parameters passed in).  If the Account is populated, the "Use Account Address" button is enabled.  If the Contact is populated, then the "Use Contact Address is populated.".  Otherwise, the user can click "Manually Enter Address" to bring up and edit form for the address fields, vs. the display fields shown in the screenshot above.

Everything works fine, except for capturing the onchange events on Account and Contact.  When either of these change, I need to make sure the buttons reflect the correct "disabled" flag, and also I need to make sure I refresh my internal account and contact object variables with a new query of data so that if a user changes the account or contact, the variables are updated so that the user clicking the buttons gets the correct address.  (I probably could perform a refresh of the account and contact object variables in the button press)

If I can't get these to work at the moment, I may just make the fields read only and force users to start at the correct point so data is populated correctly, and if they don't start from the right place, then they will need to restart the wizard.

Jon Keener
dchasmandchasman
Test2 is a problem for sure (the very thing I was just digging into a bit more) - the binding you're attempting there does not make sense and should have been cause when you tried to save the page definition:

<apex:inputField id="Acct" value="{!testing.Account}" />

is an attempt to bind inputField to an object instead of a field on an object.
Also, I just tried out test 1 on Spring '08 (internal test system) using onchange and its working nicely there.




Message Edited by dchasman on 12-12-2007 12:19 PM
Jon KeenerJon Keener
The field "testing" refers to a contact object and the Account field is the standard lookup field on the Contact that refers to the Account the contact is related to.  So it is a field on the Contact record. 
 
 
 
 


Message Edited by Jon Keener on 12-12-2007 12:31 PM
dchasmandchasman
Well not exactly - in visualforce we follow references through the relationship name and bind to the underlying object. If you want to refer to the field you need to use the field's api name which is accountId in thie case - try this out instead and you should be good to go (note the use of the recently released <apex:pageBlockSection> autolayout of <apex:inputField> feature too):

<!-- Test 2 - Using Account -->
<apex:pageBlockSection title="test 2">
  <apex:inputField id="Acct" value="{!testing.Accountid}">
    <apex:actionSupport event="onblur" action="{!ToggleTrue2}" rerender="details2" />
  </apex:inputField>
</apex:pageBlockSection>
<!-- End of Test 2 - Using Account field -->
Jon KeenerJon Keener
Ok I made that change, and the Test 2 sort of works.  I made the mistake putting the tests together of looking at the field in salesforce directly for the fieldname, vs. the wsdl.  The Field Name is shown incorrecty on the contact field screen for Account.  In my primary app, I'm using the PageLayoutSection tag.  For this testing, I started off with the code from the previous thread that Ron Hess was helping with. (referred to in the initial message)
 
 
 
After using AccountId instead of Account, Test 2 performed a little bit better.  It's interesting though that the actual inputfield tag did work with Account though.  Below are my results:
 
1.  With both Test 1 and Test 2 enabled, the "onblur" works.  However, for Test 2, the onblur appears to be associated with only the "Magnifier Lookup" icon link that looks up the account.  If I key into the input box, and then click on another input box, the "onblur" never fires.  If after keying into the input box, I hit tab twice, it tabs through the magnifier and as I leave the magnifier, the onblur event does fire and the visibility of the section changes correctly.
 
2.  Nether of the Tests work in IE6.  It doesn't appear that the "onblur" event is firing in IE6.  Does this fall into the category of "expected to be fixed in Spring 08?"
 
Jon Keener
BaigBaig

HI dchasman,

          I am trying to hide pageBlockSection in my VisualForce page based on the value selected by the user, an I have added the actionSupport to refresh the pageBlockSection so that based on the values the BlockSection hides and unhide.

Whenever I change the value in the field the pageBlockSections gets refreshed but it does't hide and unhide because the field value doesn't change. is that I am doing some thing wrong in setting/getting the current value on my VF page?

<apex:page StandardController="Lead"  extensions="LeadPageBeforeConvert" tabstyle="lead"  >
<apex:sectionHeader title="Convert Lead"subtitle="{!lead.owner.name}"/>
           Leads can be converted to Accounts, Contacts, Opportunities, and followup tasks.
          <br>You should only convert a lead once you have identified it as qualified.</br>
           <br>After this lead has been converted, it can no longer be viewed or edited as a lead, but can be viewed in lead reports.</br>
    <script type="text/javascript">
   function hideMPAN(input, Compid) {  
        if(input){
     document.getElementById(Compid).style.display="block";
    }
    else {
     document.getElementById(Compid).style.display="none";
    }
   }
 </script> 
        <apex:form >
          <apex:pageBlock title="Bussiness Advisory Services Lead" id="pageBlockItm" mode="edit">
      
           <apex:pageBlockButtons >
              <apex:commandButton action="{!Save}" value="Save"/>
              <apex:commandButton action="{!SaveNew}" value="SaveNew"/>
              <apex:commandButton action="{!Cancel}" value="Cancel"/>
           </apex:pageBlockButtons>
         
         <apex:pageBlockSection title="Lead Information" columns="3">
         
       </apex:pageBlockSection>
  
         <apex:pageBlockSection title="Lead Details" columns="3">
                  
             </apex:pageBlockSection>
          
              <apex:pageBlockSection title="Current Spend vs Renewal Spend(Electricity)" columns="3" rendered="{!OR(LeadRec.Related_Utility__c == 'Gas & Electricity', LeadRec.Related_Utility__c == 'Electricity')}">
              
                   
      </apex:pageBlock>
    
      <apex:outputPanel id="detail">
         <apex:detail subject="{!$CurrentPage.parameters.theList}" relatedList="true" title="true"/>
     </apex:outputPanel>  
        </apex:form>

</apex:page>

 
 
Controller:

public class LeadPageBeforeConvert {

public String LeadName {get; set;}
public Lead LeadRec {get; set;}
public String RelatedUtility {get; set;}
 
public void CustAddress(){
if(LeadRec.RA_Same_as_Bussiness_Address__c == true){
LeadRec.RA_Street__c = LeadRec.Street;
LeadRec.RA_Postal_Code__c = LeadRec.PostalCode;
LeadRec.RA_County__c = LeadRec.Country;
LeadRec.RA_City__c = LeadRec.City;
LeadRec.RA_County__c = LeadRec.State;
}

if(LeadRec.Site_Same_As_Bussiness_Address__c == true){
LeadRec.Site_Street__c = LeadRec.Street;
LeadRec.SITE_Postal_Code__c = LeadRec.PostalCode;
LeadRec.Site_Country__c = LeadRec.Country;
LeadRec.Site_City__c = LeadRec.City;
LeadRec.Site_County__c = LeadRec.State;

}
}

public List<SelectOption> getRelatedUtilities() {
List<SelectOption> options = new List<SelectOption>();
if(LeadRec.Related_Utility__c == null){
/* Add a null option to force the user to make a selection. */
options.add(new SelectOption('','- None -'));
}else{
/* Add a null option to force the user to make a selection. */
options.add(new SelectOption(LeadRec.Related_Utility__c,LeadRec.Related_Utility__c));
}

/* Loop through the feature_category__c records creating a selectOption
for each result with the record ID as the value and the name as the label
displayed in the selectList */
for (Utility__c Util : [select name from Utility__c where name !=: LeadRec.Related_Utility__c order by Name]){
options.add(new SelectOption(Util.name,Util.name));
}
return options;
}
 
public String RelatedUtilityVal() {
if(LeadRec.Related_Utility__c == null){this.LeadRec.Related_Utility__c = LeadRec.Related_Utility__c;}
return null;
}

public String getLeadName() {
return LeadRec.FirstName + ' ' + LeadRec.LastName;
}
 
public static Id whoId;
// The extension constructor initializes the private member
// variable tsk by using the getRecord method from the standard
// controller.
public LeadPageBeforeConvert(ApexPages.StandardController stdController) {
init();
this.LeadRec = (Lead)stdController.getRecord();

}
public void init(){
Id LeadId = ApexPages.currentPage().getParameters().get('id');
LeadRec = (LeadId == null) ? new Lead() : [Select <Fields> From Lead l where id = :LeadId limit 1];

if(LeadId == null){
RecordType recType = [Select r.SobjectType, r.Name, r.Id From RecordType r where SobjectType='Lead' and Name ='Unqualified'];
LeadRec.RecordTypeId = recType.Id;
}

}

public PageReference save(){
LeadValidations();
try {
CustAddress();
upsert LeadRec;
} catch(System.DMLException e) {
ApexPages.addMessages(e);
return null;
}

PageReference pageRef = new PageReference('/' + LeadRec.Id);
return pageRef;
}

public PageReference saveNew(){
LeadValidations();

try {
CustAddress();
upsert LeadRec;
} catch(System.DMLException e) {
ApexPages.addMessages(e);
return null;
}
PageReference pageRef = ApexPages.currentPage();
return pageRef;
}

public void LeadValidations(){
if(LeadRec.Is_Multi_Site__c == true && (LeadRec.Number_of_Sites__c == null || LeadRec.Number_of_Sites__c <= 1)){
LeadRec.Number_of_Sites__c.addError('Please enter valid Number of Sites');
}

if(LeadRec.Reaction__c == 'Alternative Broker' && LeadRec.Alternative_Broker_Name__c ==NULL){
LeadRec.Alternative_Broker_Name__c.addError('Please enter the Alternative Broker Name');
}

if(LeadRec.RecordTypeId == '012200000008r2P' && !((LeadRec.Annual_Spend__c == '£0 - £1000') ||
(LeadRec.Annual_Spend__c == '£1000 - £5000') || (LeadRec.Annual_Spend__c == '£5000 - £20,000') || (LeadRec.Annual_Spend__c =='£20,000+')))
{
LeadRec.Annual_Spend__c.addError('The value in this field is not one of the picklists values');
}
}

public PageReference convert() {
PageReference ConvertPage = new PageReference('/apex/LeadConvertPage?Leadid=' + LeadRec.id);
ConvertPage.setRedirect(true);

return ConvertPage;

}
 
public static testmethod void basicSaveTest() {

/* Construct the standard controller for quote. */
ApexPages.StandardController con = new ApexPages.StandardController(new Lead());

/* Switch to runtime context */
Test.startTest();

/* Construct the quoteExt class */
LeadPageBeforeConvert Led = new LeadPageBeforeConvert(con);
/* Call save on the ext */
// PageReference result = Led.save();
PageReference result1 = Led.saveNew();

/* Switch back to test context */
Test.stopTest();

/* Verify the navigation outcome is as expected */
System.assertEquals(result1.getUrl(), con.view().getUrl());
// System.assertEquals(result.getUrl(), con.view().getUrl());

}

}