4

I have a typical form with <apex:repeat> for some fields. The <apex:inputText> elements have an onChange attribute that references a script that then calls an <apex:actionFunction>, for example:

Repeat Fields:

<apex:repeat value="{!myList}" var="i">
    <apex:outputPanel id="rowOfMyList" styleClass="row field-row" layout="block">
        <apex:inputText id="inputId" value="{!i.obj.value}" html-data-index="{!i.obj.index}" html-data-render="{!$Component.rowOfMyList}" onChange="updateField(this)"/>
    </apex:outputPanel>
</apex:repeat>

JS Script:

function updateField(row) {
    myActionFunction(row.getAttribute('data-index'), row.value, row.getAttribute('data-render'));
}

Action Function:

<apex:actionFunction action="{!pageRefMethod}" name="myActionFunction" rerender="{!renderRow}" >
    <apex:param name="index" value=""/>
    <apex:param name="goal" value=""/>
    <apex:param name="render" assignTo="{!renderRow}" value=""/>
</apex:actionFunction>

When I debug my controller I get exactly what I would expect, the dynamic Id selector that salesforce puts on my managed <apex:outputPanel>. I even then take that debug and run a search on the html source code and get an exact match on the element I expected to rerender. Unfortunately it does not rerender.

If I concatenate another ID from outside of the repeat tag to my html-data-render attribute it too gets passed through to my rerender="{!renderRow}" and it does rerender

Any thoughts on this would be awesome!

7
  • Haha sounds like a complicated project you're working on...this must be the third or fourth question it has yielded by now? Is this at all related to Only rerender row in <apex:pageBlockTable> using <apex:actionRegion>?
    – Adrian Larson
    Commented Mar 18, 2016 at 1:59
  • 1
    Hah, actually 18, dating back to November. I've learned SO MUCH on this project but it is a beast. It is looking amazing though! The community is a huge part of my ability to learn SF. I hope to return the favor, for now I try to ask questions and format them cleanly in a way that benefits others, and when possible make sure the solution is clearly stated for future reference Commented Mar 18, 2016 at 2:04
  • That post is close but some differences. Not using <apex:actionRegion>. And right now each row is saving as needed just fine, but having issues with rendering. If I rerender the whole container then everything appears fine but this is a bad UX, because if I tab from one input to the next as I fill it in the entire container would rerender and lose its positioning. Commented Mar 18, 2016 at 2:11
  • Yeah I have the same advice as I posted there. If you want that granularity of rerendering, you need to roll your own Javascript.
    – Adrian Larson
    Commented Mar 18, 2016 at 2:16
  • Do you resource for setting up a framework in Salesforce. I know my way with jquery but never worked with Angular or React. Also, if I can pass the rerender the exact $Component.Id I don't understand why that wouldn't work Commented Mar 18, 2016 at 2:18

1 Answer 1

9

Through attempting this same problem on another project I was able to get a fresh look at the issue. What I learned through countless testing is that rerender attribute can be very tricky. For example:

  • As many already know, if you do not provided the rerender attribute in your actionFunction then your parameters will not be sent.
  • Rerender cannot be a dynamic value, meaning that EVEN IF you statically put the visualforce rendered ID (i.e. j_id0:j_id38), it will not rerender
  • Rerender cannot be bound to a controller instance variable. Along the lines of the previous statement, besides the trickyness of dynamically binding a component ID to a variable, even if you do it would not rerender

What it comes down to is, in order to provide a dynamic component Id to the rerender attribute you must use the $Component variable to dot notate through your ID.

Here is a barebones look at what I mean within the context of the OP:

Visualforce Repeat Fields:

<apex:repeat id="myRepeater" value="{!myList}" var="i">
    <apex:outputPanel id="myRow" layout="block">
        <apex:inputText id="inputId" value="{!i.obj.value}" html-data-index="{!i.obj.index}" html-data-render="" onChange="updateField(this)"/>
    </apex:outputPanel>
</apex:repeat>

NOTE: Added an ID to the repeat tag, AND remove the html-data-render attribute since there is no use attempting to dynamically PASS the component ID

Visualforce JS Script:

function updateField(row) {
    myActionFunction(row.getAttribute('data-index'), row.value);
}

Visualforce ActionFunction:

<apex:actionFunction action="{!pageRefMethod}" name="myActionFunction" rerender="{!$Component.myRepeater.myRow}" >
    <apex:param name="index" value=""/>
    <apex:param name="goal" value=""/>
</apex:actionFunction>

Notice that rather than PASSING the specific row ID through some html pass-through attribute, I instead call the ID within rerender attribute directly.

Now don't believe this ONLY rerenders my "affected/selected" row, pretty sure all rows that fall within that Component ID selector are being rerendered (I can't even tell), BUT on the plus side it only rerenders the rows themselves and not the container, which to the user makes no difference (considering there is no flickering or visual glitch). Really the best part is, it WORKS!

enter image description here

1
  • I've just been wrestling with some complex jQuery and apex:repeat stuff, and this just saved my bacon. Who'd have thought it was just a simple case of re-rendering the inner tag to refresh elements within a repeat rather than the whole thing. Makes complete sense, lovely simple solution, nice work. FYI, I didn't even use the Component tag in the end, just used the id of the panel, in your example {!myRow} Commented Dec 13, 2016 at 10:36

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .