+ Start a Discussion
Scott.MScott.M 

Set <apex:param> value with Javascript

Hi,

Is it possible to set the value of a param object in visual force. For example if I defined a action function as follows:

Code:
 <apex:actionFunction name="testAction" action="{!testAction}" rerender="ListTable" immediate="true">
  <apex:param name="reference" id="update_reference" value="0" />
 </apex:actionFunction>

and I've loaded the prototype library should I be able to do something like this

Code:
$('update_reference).value = '1'; 

Is there a different way of setting the value of the parameter in javascript. Also it'd be nice if in the object reference examples the source code (javascript and html) generated for the tag was shown.
 


jwetzlerjwetzler
You will need to use $Component to get the full id, since the actual DOM ID is built up by us in the background.

'{!$Component.update_reference}' should work depending on where you reference it in your page.  There is some good doc around $Component's usage that should help you.
Scott.MScott.M
Thanks,

It sounds like it's exactly what I was looking for unfortunately it's not working for the param object in the actionFunction. How are the paramters in action functions handled. I had a look at the documentation in the pages document but it's very brief and doesn't specifically talk about using the global $component object with the actionFunction tag.

onchange="$('{!$Component.updateId}').value= '1'";

gives the javascript error:

 $("j_id0:j_id3:updateId") is null

When actionFunction tag gets rendered is it part of the DOM? Is there a better way to pass paramters to an actionFunction?

jwetzlerjwetzler
Duh okay I was not thinking about how special actionFunction is.

I don't think there's a way for you to dynamically set the value of the param through javascript.  You could use an expression and get it from you controller, but that seems to be it.
Scott.MScott.M
Cool thanks,

I settled on passing the parameter by binding it to a variable in the controller. The only problem now is that I need the action to be immediate, validation rules shouldn't be processed. The reason it's a problem is that if I set the action to immediate = "true" the controller variables don't get updated before I reference them. Is there a way to force the setters to happen before i use the variable. It works fine if immediate is set to false and there are no errors.

Code:
<apex:form>
<apex:actionFunction name="myaction" action="{!myaction}" rerender="mytable" id="update_function" />
<apex:inputHidden id="stg_reg1" value="{!StgReg}" />

<apex:inputField value="{!myobject}" onchange="$('{!$Component.stg_reg1}').value = '{!myobject.index}'; UpdatePrice();" />
</apex:form>



dchasmandchasman
Hold on everyone - there is a misunderstanding about how you use apex:actionFunction w.r.t apex:param - all you can actually do is provide a value via javascript - the apex:param's in this case are there solely to provide a way for you to define the set of inputs into the generated javascript function and their corresponding assignTo attributes are really something you'll want to leverage. There are no hidden input elements created by apex:actionFunction so this discussion of dom ids is a bit off track if all you want is to be able to pass values into your controller so you can access them in an action method. Take a closer look at the usage example in the component reference to see details on <apex:param assignTo>

BTW I have seen quite a few folks dropping to apex:actionFunction when in most cases apex:actionSupport is a much better option - can't tell from your posted code if this is the case for you or not - can you post your entire page/controller code here?


Message Edited by dchasman on 07-02-2008 03:01 PM
Scott.MScott.M
Funny you should mention actionSupport :).  I've actually switched to it; but, I still have the problem of needing the setters to get triggered so that the partId is set once the action executes using immediate. The goal is to get the Unit_Price field to update to the value of the parts recommended price once a part is selected in a new row or a different part is selected in an existing row but still allowing the recommended price to be overridden if needed.

Code:
<apex:page standardController="Case" extensions="addPartExt">
 <script type="text/javascript" src="{!$Resource.Prototype16}"></script>
 <apex:sectionHeader title="Workorder" subtitle="Add Parts" />
 <apex:form id="part_form">

 <apex:pageBlock title="Workorder Parts" >
 <apex:pageBlockButtons >
  <apex:commandButton action="{!addWOPart}"  value="Add a Part" reRender="PartsListTable" immediate="true" />
  <apex:commandButton action="{!save}"  value="Save" />
  <apex:commandButton action="{!cancel}"  value="Cancel" immediate="true" />
 </apex:pageBlockButtons>
 <apex:pageBlockTable value="{!PartsList}" var="part" id="PartsListTable">
        <apex:column >
         <apex:facet name="header">Part</apex:facet>
            <apex:inputField value="{!part.case_part.Part__c}">
     <apex:actionSupport event="onchange" action="{!setUnitPrice}" rerender="PartsListTable" immediate="true">
    <apex:param name="update_index" value="{!part.index}" />
     </apex:actionSupport>
            </apex:inputField>
        </apex:column>
        <apex:column >
         <apex:facet name="header">Quantity</apex:facet>
            <apex:inputField value="{!part.case_part.Quantity__c}" />
        </apex:column>
        <apex:column >
         <apex:facet name="header">Unit Price</apex:facet>
            <apex:inputField value="{!part.case_part.Unit_Price__c}" />
        </apex:column>
        <apex:column >
         <apex:facet name="header">Warranty</apex:facet>
            <apex:inputField value="{!part.case_part.Warranty__c}" />
        </apex:column>
        <apex:column >
         <apex:commandButton action="{!removeWOPart}" value="Remove" reRender="PartsListTable" immediate="true">
          <apex:param name="ref" value="{!part.index}"/>
         </apex:commandButton>
        </apex:column>
 </apex:pageBlockTable>
 </apex:pageBlock>
 </apex:form>
</apex:page>


public class addPartExt {

 public class WOPart{
  public Case_Part__c case_part {get; set;}
  public Integer index {get; set;}

  public WOPart(Case_Part__c cp, Integer i) {
   this.case_part = cp;
   this.index = i;
  }
 }


 public Case case_ext {get; set;}
 public WOPart[] PartsList {get; set;}

 public addPartExt(ApexPages.StandardController stdController){
  this.case_ext = (Case) stdController.getRecord();
  //get a list of case parts
  Case_Part__c[] cps = [SELECT Id, Name, Part__c, Quantity__c, Unit_Price__c, Warranty__c FROM Case_Part__c WHERE Case__c = :this.case_ext.Id];

  this.PartsList = new List<WOPart>();
  for(Case_Part__c cp: cps) {
   this.PartsList.add(new WOPart(cp, 0));
  }
  this.updateWOPartIndex();
 }

 public void updateWOPartIndex() {
  Integer i = 0;
  for(WOPart p: this.PartsList) {
   p.index = i;
   ++i;
  }
 }

 public PageReference removeWOPart() {
  Integer ref = Integer.valueOf(System.currentPageReference().getParameters().get('ref'));
  this.PartsList.remove(ref);
  this.updateWOPartIndex();
  return null;
 }

 public PageReference addWOPart() {
  this.PartsList.add(new WOPart(new Case_Part__c(), 0));
  this.updateWOPartIndex();
  return null;
 }

 public PageReference setUnitPrice() {
  Integer i = Integer.valueOf(System.currentPageReference().getParameters().get('update_index'));
  if(PartsList[i].case_part.Part__c != null) {
   Part__c p = [SELECT Recommended_Price__c FROM Part__c WHERE Id = :PartsList[i].case_part.Part__c ];
   PartsList[i].case_part.Unit_Price__c = p.Recommended_Price__c;
  }
  return null;
 }

}


 

dchasmandchasman
Your use of immediate is going to make that impossible currently - part of the behavior of immediate is to not execute the assign values phase of the lifecycle and this includes assignTo evaluation (we're looking at a more refined level of control for this kind of thing someday BTW). I assume you're using immediate to avoid requireness checks and such. If that is the case you should try wrapping just the apex:inputFIeld and its children inside an apex:actionRegion and removing the immedaite="true". apex:actionRegion gives you finer graine control over what will actually be sent up to the server as part of invoking an action and I suspect this will do the trick for you. You'll also have to add in and assignTo="{!part.index}" to your apex:inputField's apex:param child if you want the value binding mechanism to kick in. You're other choice would be to leverage the current page reference inside your controller and access the update_index param via the params collection although I always try to use the value binding instead to keep things cleaner.
Scott.MScott.M
Thanks for the help, I've managed to get the desired effect. I went back to the actionFunction with an extra parameter running in immediate mode. So the code ends up looking like this:

Code:
Javascript function
--------------------------------------
<apex:actionFunction name="updateUnitPrice" action="{!setUnitPrice}" rerender="PartsListTable" immediate="true" >
  <apex:param name="update_index" value="" />
  <apex:param name="new_part" value="" />
</apex:actionFunction>


Input Element
-------------------------------------
<apex:column >
  <apex:facet name="header">Part</apex:facet>
  <apex:inputField value="{!part.case_part.Part__c}" onfocus="if(this.value != '' && this.value != '{!part.case_part.Part__r.Name}' ){updateUnitPrice('{!part.index}', this.value);}"/>
</apex:column>

Controller Function ------------------------------------- public PageReference setUnitPrice() { Integer i = Integer.valueOf(System.currentPageReference().getParameters().get('update_index')); String partname = System.currentPageReference().getParameters().get('new_part'); try{ Part__c p = [SELECT Recommended_Price__c FROM Part__c WHERE Name = :partname LIMIT 1]; PartsList[i].case_part.Part__c = p.Id; PartsList[i].case_part.Unit_Price__c = p.Recommended_Price__c; } catch(Exception e) { } return null; }

 
The key thing I was missing was that the params defined inside the actionFunction can be passed through the Javascript function call and they're assigned in the order that they're defined.



Message Edited by Scott.M on 07-03-2008 07:52 AM