1

I'm running into an issue where a custom component inside an apex:repeat isn't updating the component controller binding.

Here's where the component is called:'

<apex:repeat value="{!providersMatchingAll}" var="match"> 
    <c:providerblock record="{!match.Provider__r}"/>
</apex:repeat>

Here is a trimmed down component definition:

<apex:component controller="ProviderBlockDetailController">
  <apex:attribute name="record" type="Account" description="The provider account to display." assignTo="{!provider}" required="true"></apex:attribute>
  <a name="{!record.Id}"/>
  <c:panel title="{!record.Name}" type="primary" styleclass="providerblock">
            <p>Mon-Fri {!earliestWeekdayStart}-{!latestWeekdayEnd}  <br/>
            Sat-Sun {!earliestWeekendStart}-{!latestWeekendEnd}</p>
            <p><apex:outputField value="{!record.Yearly_Schedule__c}"/><apex:outputText value=" -- " rendered="{!NOT(ISBLANK(record.Schedules_And_Programs__c))}"/><apex:outputField value="{!record.Schedules_and_Programs__c}"/></p>
  </c:panel>
</apex:component>

and here is the detail block controller:

public with sharing class ProviderBlockDetailController {
    public Account provider {get;set;}

    public String getEarliestWeekdayStart() {
        try {
            List<DateTime> startTimes = new List<DateTime> {
                getDateTimeFromTimePicklist(provider.Schedule_Monday_Earliest_Start__c),
                getDateTimeFromTimePicklist(provider.Schedule_Tuesday_Earliest_Start__c),
                getDateTimeFromTimePicklist(provider.Schedule_Wednesday_Earliest_Start__c),
                getDateTimeFromTimePicklist(provider.Schedule_Thursday_Earliest_Start__c),
                getDateTimeFromTimePicklist(provider.Schedule_Friday_Earliest_Start__c)
            };
            startTimes.sort();
            return startTimes[0].format('h:mm a');
        } catch(Exception e) {
            return 'Unknown and not migrated';
        }
    }

    public String getLatestWeekdayEnd() {
        try {
            List<DateTime> endTimes = new List<DateTime> {
                getDateTimeFromTimePicklist(provider.Schedule_Monday_Latest_End__c),
                getDateTimeFromTimePicklist(provider.Schedule_Tuesday_Latest_End__c),
                getDateTimeFromTimePicklist(provider.Schedule_Wednesday_Latest_End__c),
                getDateTimeFromTimePicklist(provider.Schedule_Thursday_Latest_End__c),
                getDateTimeFromTimePicklist(provider.Schedule_Friday_Latest_End__c)
            };
            endTimes.sort();
            return endTimes[4].format('h:mm a');
        } catch(Exception e) {
            return 'Unknown and not migrated';
        }
    }

    public String getLatestWeekendEnd() {
        try {
            List<DateTime> endTimes = new List<DateTime> {
                getDateTimeFromTimePicklist(provider.Schedule_Sunday_Latest_End__c),
                getDateTimeFromTimePicklist(provider.Schedule_Sunday_Latest_End__c)
            };
            endTimes.sort();
            return endTimes[1].format('h:mm a');
        } catch(Exception e) {
            return 'Unknown and not migrated';
        }
    }

    public String getEarliestWeekendStart() {
        try {
            List<DateTime> endTimes = new List<DateTime> {
                getDateTimeFromTimePicklist(provider.Schedule_Saturday_Earliest_Start__c),
                getDateTimeFromTimePicklist(provider.Schedule_Sunday_Earliest_Start__c)
            };
            endTimes.sort();
            return endTimes[0].format('h:mm a');
        } catch(Exception e) {
            return 'Unknown and not migrated';
        }
    }

    public DateTime getDateTimeFromTimePicklist(String picklistValue) {
        return DateTime.parse('1/1/2015 '+picklistValue);
    }
}

For every display of the component, the earliestWeekdayStart/earliestWeekdayEnd pair show the value of the first record in the repeat set, instead of the record they are on.

If I call the component outside of an apex:repeat

    <c:providerblock record="{!providersMatchingAll[0].Provider__r}"/>
    <c:providerblock record="{!providersMatchingAll[1].Provider__r}"/>

it behaves as expected.

4
  • That seems very odd. What do you see if you put the account id in an output text in the repeat block? Commented Sep 9, 2015 at 23:36
  • @LaceySnr -- just normally in the repeat referencing the repeat variable? it works as expected. And using the passed attribute in the component (like the Id anchor and the panel title) works fine. Commented Sep 10, 2015 at 4:24
  • Sorry - have been offline (travelling). Definitely sounds like an order of execution thing, what would happen if the variable wasn't set in the controller, is what you see the result of that? Commented Sep 10, 2015 at 15:02
  • Don't be sorry! Thank you for helping. When not using the component controller variable and just using expressions based on the attribute, all works fine. But it's a stupid sort of calculation to do without apex, so I'm hoping there's a way to get this to work. I agree that it's an order of execution thing, however I can't seem to identify what makes it different than @javanoob's very working code. Commented Sep 10, 2015 at 15:51

2 Answers 2

1

I tried something simple and looks like it is working for me unless I understood the problem wrong :)

custom component Controller:

public class myComponentController {

  public String controllerValue;

  public void setControllerValue (String s) {
    controllerValue = s.toUpperCase() + ' updated';
  }

  public String getControllerValue() {
    return controllerValue;
  } 
}

custom Component:

<apex:component controller="myComponentController">
  <apex:attribute name="componentValue" description="Attribute on the component."
                  type="String" required="required" assignTo="{!controllerValue}"/>
        <code>componentValue</code> is "{!componentValue}"

        <code>controllerValue</code> is "{!controllerValue}"
        <br/>
</apex:component>

custom controller for the page:

public class repeatCon {

    public String[] getStrings() {
        return new String[]{'one','two','three'};
    }

}

apex page:

<apex:page controller="repeatCon" id="thePage">

    <apex:repeat value="{!strings}" var="string" id="theRepeat">

        <c:customComponentExample componentValue="{!string}"/>

    </apex:repeat>

</apex:page>

and here is the output I am getting:

enter image description here

5
  • The only difference I can see that MIGHT be causing an issue is that the surrounding page I am using is using the standard controller, and the collection is created in a controller extension. Commented Sep 9, 2015 at 22:55
  • I tried with standard controller and controller extension and it is still working for me..I think you might have problem somewhere. Commented Sep 9, 2015 at 23:01
  • Yep, I've been toying with your code (changed it to use standard controller, changed it to use objects instead of strings) and it continues to work. Something else is happening here. That said, my component works as is when not in a repeat, so it's hard to know where to go to debug. Commented Sep 9, 2015 at 23:06
  • can you replace the component inside the repeat block with output text and see if it works..This may sound crazy..but who knows? Commented Sep 9, 2015 at 23:13
  • Well, the component itself is rendering just fine, and the component attribute is binding and working just fine (in the component, the panel title name totally works). But the parts of the component that depend on further controller logic don't. So the issue seems to be around assignTo. Commented Sep 9, 2015 at 23:16
0

I am suspecting order of execution of things happening on the page. Your page is trying to load custom components constructor right after page's controller constructor. If your binding object "providersMatchingAll" is not ready by then the component will not render.

As a workaround I would suggest you get the binding variable ready in page's controller constructor or try to rerender the apex repeat area when you are sure your variable is populated (via a boolean value in controller that controls the page).

4
  • Hi Amr, I adjusted the controller extension of the page to set providersMatchingSome and providersMatchingAll in the constructor method. Same error condition. The component renders for each Account record, but the hours of operation calculated in the component controller are the same for the entire set. Commented Sep 9, 2015 at 22:25
  • Indeed, even after ditching custom getter/setters and now using explicit methods called in the extension constructor, still no luck. Commented Sep 9, 2015 at 22:31
  • How about you try the repeater without the component and see if it renders correctly? Commented Sep 9, 2015 at 22:39
  • Yep, the repeat works just fine if I don't use the component. And, to clarify, most of the component renders just fine. It's only the part of the component driven by the component controller that isn't repeating properly. Commented Sep 9, 2015 at 22:45

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.