1

I am very new to VisualForce and Apex, and was hoping I can ask for help. I created a VF page, and a Custom Controller. All of the code is compiling and working as expected. I have created a list in the VF page, that will update when a Filter button is selected. What I am trying to accomplish is to default the list to have a default list of records, before the button is selected. Once it is selected, the filters should take affect and overwrite the list. I thought I could accomplish that by defining my PageBlockTable {!getStoryTasks } to have 2 values, but I do not think that would work. So not really sure how I can approach this.

VF Page

        <apex:PageBlock title="Current Sprint" id="current_sprint">

                <apex:pageBlockTable value="{! getCurrentSprint }" var="cs">

                        <apex:column value="{!cs.id  }"/>   
                        <apex:column value="{!cs.Name  }"/>
                        <apex:column value="{!cs.Start_Date__c  }"/>
                        <apex:column value="{!cs.End_Date__c  }"/>
                        <apex:column value="{!cs.Story_Points__c }"/>
                        <apex:column value="{!cs.Velocity__c  }"/>

                </apex:pageBlockTable>

        </apex:PageBlock>


<apex:form >

    <apex:pageBlock title="Story Tasks" id="story_tasks">

            <apex:pageBlockSection title="Filters" columns="1">

           Current Sprint: {target_date} <br/>
                <apex:inputField value="{!stt.Owner__c}"/>
         <!-- <apex:inputField value="{!stt.Story__c}"/> -->
                <apex:inputField value="{!s.Sprint__c}"/> 
                <apex:commandButton action="{!filterStoryTask }" value="FILTER" />

            </apex:pageBlockSection>
        <!--Story Task List  -->
            <apex:PageBlockTable value="{! getStoryTasks }" var="st">

                <apex:column value="{! st.Name }"/>
                <apex:column value="{! st.Subject__c }"/>
                <apex:column value="{! st.Owner__c }"/>
                <apex:column value="{! st.Story__c }"/>
                <apex:column value="{! st.Story__r.Sprint__r.Name }"/>

            </apex:PageBlockTable>

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

Controller

public with Sharing class SprintTasksCon{
private String sortOrder = 'Name';


        public Story__c s { get;set; }
        public Sprint__c sp { get;set; }
        public Story_Task__c stt { get;set; }
        Date target_date = Date.today();

        public List <Story_Task__c> getStoryTasks{get;set;}
        public List <Sprint__c> getCurrentSprint{get;set;}



            public void filterStoryTask(){
            getStoryTasks = new List<Story_Task__c>();
                  if(stt.Owner__c !=null && s.Sprint__c!=null){
                      getStoryTasks = [Select Id, Name, Owner__r.Name, Story__r.Name, Subject__c, Story__r.Sprint__r.Name
                                     FROM Story_Task__c 
                                     WHERE Owner__c=:stt.Owner__c AND Story__r.Sprint__c=:s.Sprint__c Limit 100];
                  } 
                  else if(s.Sprint__c == null){
                     getStoryTasks = [Select Id, Name, Owner__r.Name, Story__r.Name, Subject__c, Story__r.Sprint__r.Name
                                     FROM Story_Task__c 
                                     WHERE Owner__c=:stt.Owner__c Limit 100];
                  } 
                  else if(stt.Owner__c == null){
                     getStoryTasks = [Select Id, Name, Owner__r.Name, Story__r.Name, Subject__c, Story__r.Sprint__r.Name
                                     FROM Story_Task__c 
                                     WHERE Story__r.Sprint__c=:s.Sprint__c Limit 100];
                  }

                  else{
                      getStoryTasks = [Select Id, Name, Owner__r.Name, Story__r.Name, Subject__c, Story__r.Sprint__r.Name
                                     FROM Story_Task__c Limit 100];
                  }   
            }




        public SprintTasksCon(){
            stt = new Story_Task__c();
            s = new Story__c();

            getCurrentSprint = [Select Id, Name, Start_Date__c, End_Date__c, Story_Points__c, Velocity__c
                                FROM Sprint__c target_date
                                WHERE Start_Date__c <: target_date AND End_Date__c >: target_date Limit 1]; 

        }   

}
5
  • You can populate your list in constructor to get default list Commented Apr 7, 2017 at 15:27
  • Thank you for your feedback. Yea, I can definitely create another query for a default list view in the controller, but not sure I can include it in the <PageBlockTale> because I already have value="{! getStoryTasks}". Can I add another value="{test}"? Thanks
    – Ivo Dimov
    Commented Apr 7, 2017 at 15:46
  • Just want to clear. You want to display default list and on clicking filter button you want that list to update. Right ? If yes the you dont need to use another variable. First initialize that list in constructor with default values using SOQL. And once user clicks the button, You just have to rerender that section as that list will update according to your logic. Commented Apr 7, 2017 at 15:50
  • I'd wrapper my sobject and then manipulate the wrapped object based on the filters, You don't need re query each time.
    – EricSSH
    Commented Apr 7, 2017 at 15:54
  • Yeah. That's a good solution @EricSSH Commented Apr 7, 2017 at 16:14

2 Answers 2

2

I think you're pretty close to making this work, and you're right in thinking that you can't simply specify the values attribute on your <apex:pageBlockTable> twice.

Getting on to the core of your question now, I think I'll break things down into two sections.

How do I specify/get default values?

Right now, your getStoryTasks property is empty when your page first loads. You aren't doing anything to put data into this List before you press your "FILTER" button.

To get some default values into this List (so it has something to display on page load), you'll need to run a query, and store the results in your List.

You're actually closer to accomplishing this than you might think. Your filterStoryTask() method already contains the queries, and can handle situations where your filter value, stored in s.Sprint__c is null. You simply need to call filterStoryTask() right after the page loads (technically, the controller constructor is called before the page loads, but that detail isn't too important here).

You can do this either in the constructor for your controller, or by manually creating the getter method for getStoryTasks. I'll illustrate both options below (choose one or the other)

public with Sharing class SprintTasksCon{
    // If you want to manually implement the getter, just put 'set;' inside the
    //   curly brackets instead of 'get; set;'
    List<Story_Task__c> getStoryTasks {set;}

    // This is the method signature for a constructor.
    // It only specifies the access modifier (public) and the method name,
    //   which in the case of constructors is an exact match to the name of the class.
    public SprintTasksCon(){
        // A constructor doesn't return anything, and can't perform DML.
        // It's basically a place for setting up default values.
        // Running filterStoryTask() here will cause one of your queries to run,
        //   which will cause some default data to appear in your pageBlockTable
        filterStoryTask();
    }

    public List<Story_Task__c> getGetStoryTasks(){
        // The code you'll see below follows a pattern called 'lazy-loading'.
        // Basically, we don't do any work to try to populate getStoryTasks until
        //   until the first time that someone/something asks for the data
        if(getStoryTasks == null){
            // In here, we know someone has asked for getStoryTasks, but it's currently
            //   empty.
            // We can fix that by calling filterStoryTasks()
            filterStoryTasks();
        }

        // The final part of the getter, return the data currently in getStoryTasks.
        return getStoryTasks();
    }

    /*
      The rest of your existing controller resides below here.
      This includes the filterStoryTask() method.
      I've simply omitted your other code for brevity.
    */
}

If you want to specify a different default filter for the Story Tasks, you could easily specify that in your constructor. This will be important if you wanted to perform a full page refresh when a filter was selected, but for now, I'll just leave it at this.

How do I update the data in my pageBlockTable after I apply my filter?

You're already part of the way there. The first part is to get your filter value into your controller, which is taken care of for you by using <apex:inputField value="{!s.Sprint__c}">

The second part is getting the updated data in getStoryTasks into your pageBlockTable. There are two ways you could do this, both of them are pretty easy.

  1. Perform a partial page refresh by specifying the rerender attribute on an <apex:actionSupport> tag
  2. Refresh the entire page, and pass the information about your filter through the viewstate or through a URL parameter

Of those, the first option is probably the simplest (and is the one I'd recommend using). I'll only go over the first option unless specifically asked (my answer is getting long enough as-is).

There are only a few changes that you need to make, and they're all in your visualforce markup.

I believe that re-rendering only works for tags inside of an <apex:outputPanel>, so, you'll first need two wrap your pageBlockTable inside of an <apex:outputPanel>, and give it a unique id.

<apex:outputPanel id="storyTaskList">
    <!--Story Task List  -->
    <apex:PageBlockTable value="{! getStoryTasks }" var="st">

        <apex:column value="{! st.Name }"/>
        <apex:column value="{! st.Subject__c }"/>
        <apex:column value="{! st.Owner__c }"/>
        <apex:column value="{! st.Story__c }"/>
        <apex:column value="{! st.Story__r.Sprint__r.Name }"/>

     </apex:PageBlockTable>
</apex:outputPanel>

Next, add an <apex:actionSupport> to your command button. The rerender attribute takes a comma-delimited set of tag ids that should be refreshed. Here is where we'll use that unique id from just above.

<apex:commandButton action="{!filterStoryTask }" value="FILTER">
    <apex:actionSupport event="onclick" rerender="storyTaskList"/>
</apex:commandButton>

The end result should be that clicking your "FILTER" button causes the story tasks to update.

If that doesn't work, it's because an <apex:commandButton> expects the method it calls (via the action attribute) to return a PageReference. If filterStoryTask() returns null, it might lead to refreshing the entire page (which may or may not overwrite the filter value that you selected.

To work around that, you would remove the action attribute from <apex:commandButton>, and place it in the <apex:actionSupport> instead like so

<apex:commandButton value="FILTER">
    <apex:actionSupport event="onclick" action="{!filterStoryTask }" rerender="storyTaskList"/>
</apex:commandButton>
1
  • Thank you Derek for taking the time and explaining all of the details. It makes a lot of sense, now, and it works. Thanks
    – Ivo Dimov
    Commented Apr 7, 2017 at 18:29
0

You can accomplish this by using your logic in constructor. I have tried using your logic. (You might get some error as I don't know your logic. just showing you how to do it).

public with Sharing class SprintTasksCon{
private String sortOrder = 'Name';


    public Story__c s { get;set; }
    public Sprint__c sp { get;set; }
    public Story_Task__c stt { get;set; }
    Date target_date = Date.today();

    public List <Story_Task__c> getStoryTasks{get;set;}
    public List <Sprint__c> getCurrentSprint{get;set;}

// Constructor
    public SprintTasksCon(){
        stt = new Story_Task__c();
        s = new Story__c();
        // Get the stt and s as per your requirement using SOQL i.e. default values .
        getStoryTasks = new List<Story_Task__c>();
        getCurrentSprint = [Select Id, Name, Start_Date__c, End_Date__c, Story_Points__c, Velocity__c
                            FROM Sprint__c target_date
                            WHERE Start_Date__c <: target_date AND End_Date__c >: target_date Limit 1]; 

                    getStoryTasks = new List<Story_Task__c>();
              if(stt.Owner__c !=null && s.Sprint__c!=null){
                  getStoryTasks = [Select Id, Name, Owner__r.Name, Story__r.Name, Subject__c, Story__r.Sprint__r.Name
                                 FROM Story_Task__c 
                                 WHERE Owner__c=:stt.Owner__c AND Story__r.Sprint__c=:s.Sprint__c Limit 100];
              } 
              else if(s.Sprint__c == null){
                 getStoryTasks = [Select Id, Name, Owner__r.Name, Story__r.Name, Subject__c, Story__r.Sprint__r.Name
                                 FROM Story_Task__c 
                                 WHERE Owner__c=:stt.Owner__c Limit 100];
              } 
              else if(stt.Owner__c == null){
                 getStoryTasks = [Select Id, Name, Owner__r.Name, Story__r.Name, Subject__c, Story__r.Sprint__r.Name
                                 FROM Story_Task__c 
                                 WHERE Story__r.Sprint__c=:s.Sprint__c Limit 100];
              }

              else{
                  getStoryTasks = [Select Id, Name, Owner__r.Name, Story__r.Name, Subject__c, Story__r.Sprint__r.Name
                                 FROM Story_Task__c Limit 100];
              }   


    }   




        public void filterStoryTask(){
        getStoryTasks.clear();
              if(stt.Owner__c !=null && s.Sprint__c!=null){
                  getStoryTasks = [Select Id, Name, Owner__r.Name, Story__r.Name, Subject__c, Story__r.Sprint__r.Name
                                 FROM Story_Task__c 
                                 WHERE Owner__c=:stt.Owner__c AND Story__r.Sprint__c=:s.Sprint__c Limit 100];
              } 
              else if(s.Sprint__c == null){
                 getStoryTasks = [Select Id, Name, Owner__r.Name, Story__r.Name, Subject__c, Story__r.Sprint__r.Name
                                 FROM Story_Task__c 
                                 WHERE Owner__c=:stt.Owner__c Limit 100];
              } 
              else if(stt.Owner__c == null){
                 getStoryTasks = [Select Id, Name, Owner__r.Name, Story__r.Name, Subject__c, Story__r.Sprint__r.Name
                                 FROM Story_Task__c 
                                 WHERE Story__r.Sprint__c=:s.Sprint__c Limit 100];
              }

              else{
                  getStoryTasks = [Select Id, Name, Owner__r.Name, Story__r.Name, Subject__c, Story__r.Sprint__r.Name
                                 FROM Story_Task__c Limit 100];
              }   
        }





    }
4
  • Thank you for all of the feedback and the controller help. The actual problem I was trying to resolve is in the VF page. I already have a pageBlockTable that is set to display the list, which shows data retrieved by clicking the button. What I am trying to accomplish is that when first the page loads, I would like that same PageBlockTable to be already prepopulated with a default list. Once the button is clicked, then the same PageBlocktable would update with the data from my lookup fields.
    – Ivo Dimov
    Commented Apr 7, 2017 at 16:13
  • Did you tried the above code ? Commented Apr 7, 2017 at 16:16
  • Yep, I applied your solution and it worked.
    – Ivo Dimov
    Commented Apr 7, 2017 at 18:29
  • If this solution works please accept the answer and upvote Commented Apr 7, 2017 at 18:33

You must log in to answer this question.

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