+ Start a Discussion
ClaiborneClaiborne 

Data Grid

I have a simple visual force data grid that is trying to collect data on a single column.

This is the visual force page:
Code:
<apex:page controller="AssignSlabs" tabstyle="Orders__c">
<apex:form id="assignSlabs">
<apex:pageBlock mode="edit">
<p>This is a list of all available {!orderline.slab_color__r.Name} slabs for 
{!orderline.order__r.Name}. This order line requires {!quantityRemaining}  
more slabs to complete the order of {!orderQuantity} slabs.</p>
<p/>
<br/>
<apex:pageblockSection id="Slabs">
   <apex:pageblocktable align="center" value="{!slabs}" var="slab" title="Slabs" width="100%">
         <apex:column headerValue="Slab" width="15%"><apex:outputLink value="/{!slab.ID}">{!slab.Name}</apex:outputlink></apex:column>
         <apex:column headerValue="Status" width="15%" value="{!slab.Status__c}"/>
         <apex:column headerValue="Quality Rating" width="20%" value="{!slab.Quality_Rating__c}"/>
         <apex:column headerValue="Finished Date" width="10%" value="{!slab.Finished_Date__c}"/>
         <apex:column headerValue="Comments" width="30%" value="{!slab.Comments__c}"/>
         <apex:column headerValue="Assign Slab—" width="10%"><apex:inputfield value="{!slab.Assigned__c}"/></apex:column>
   </apex:pageblockTable>
</apex:pageblockSection>
<p/>
<apex:pageBlockSection id="Status">
   <apex:commandButton action="{!assign}" value="Assign Slabs" rerender="Status" id="assignSlabsButton"/>
   <apex:commandButton action="{!cancel}" value="Cancel" id="cancelButton"/>
   <apex:outputLabel value="{!errormessage}"></apex:outputLabel>
</apex:pageblockSection>
</apex:pageBlock>
</apex:form>
</apex:page>

This is the controller:

Code:
global class AssignSlabs {
    List<Slab__c> slabs;
    Order_Line__c orderline;
    Integer orderQuantity;
    Integer quantityRemaining;
    String errormessage;
    String testMessage;
    List<String> headers;
    
    public AssignSlabs() {
        slabs = getSlabs();
        orderline = getOrderLine();
        quantityRemaining = getQuantityRemaining();
        orderQuantity = getOrderQuantity();
        headers = getHeaders();
    }
    
    public List<Slab__c> getSlabs() {
        List <Slab__c> slabs = [
            select Name, comments__c, slab_color__c, status__c, 
                finished_date__c, assigned__c, order_line__c, 
                quality_rating__c from slab__c
            where slab_color__c = :ApexPages.currentPage().getParameters().get('slabcolor') 
                AND Order_Line__c = NULL 
            order by slab_color__c, finished_date__c];
        return slabs;
    }

    public Order_Line__c getOrderLine() {
        Order_Line__c orderline = [
            select id, slab_color__r.Name, Order__r.Name, quantity__c, assigned__c 
            from order_line__c 
            where id = :ApexPages.currentPage().getParameters().get('olid')]; 
        return orderLine;
    }
    
    public Integer getQuantityRemaining() {
        Integer QuantityRemaining = 0;
        orderline.assigned__c = [select count() 
            from slab__c 
            where order_line__c = :ApexPages.currentPage().getParameters().get('olid')];
        quantityRemaining = orderLine.quantity__c.intValue() - orderline.assigned__c.intValue();
        return quantityRemaining;
    }
    
    public Integer getOrderQuantity() {
        Integer OrderQuantity = 0;
        orderQuantity = orderline.quantity__c.intValue();
        return orderQuantity;
    }
    
    public string[] getheaders() { 
         return new string [] 
              {'Slab','Status','Quality Rating','Finished Date', 
               'Comments', 'Assign Slab —'} ; 
    }    

    public String geterrorMessage() {
        return errormessage;
    }

    public PageReference assign(){ 
        Integer entries = slabs.size();
        Integer assigned = 0; // integer must be initialized or it will be null
        Boolean isError = false;
        PageReference orderlinepage = new PageReference('/' + orderLine.id);
        PageReference assignSlabs = new PageReference('/apex/AssignSlabs');                
        
        for (Integer e = 0; e < entries; e++){
            if (slabs[e].assigned__c) {
                slabs[e].order_line__c = orderline.ID;
                assigned++;
            }
        }
        if (assigned <= quantityremaining) {
                // update slab records
            try {
                update slabs;
            } catch(System.DMLException e) {
                isError = true;
                ApexPages.addMessages(e);
            }
                // update assigned count in orderline record
            orderline.assigned__c = [select count() 
                from slab__c 
                where order_line__c = :ApexPages.currentPage().getParameters().get('olid')];
            try {
                update orderline;
            } catch(System.DMLException e) {
                isError = true;
                ApexPages.addMessages(e);
                
            }
            
            return (isError) – assignSlabs:orderlinepage;
        }
        else {
            errormessage = 'You have selected too many slabs for assignment.  Your selections would assign ' + assigned + ' slabs to this order, which only requires ' + quantityRemaining + ' slabs.';
            return assignSlabs;
        }
 
    } 
        

    public PageReference cancel() {
        PageReference orderlinepage = new PageReference('/' + orderline.id);
        return orderlinepage;
    }
}

 
For whatever, reason, the data is never written back.

This used to work with a pageBlockList, but this tag is no longer available.

Also, the pageBlockTable seems to ignore all column width information.

Ideas ?
Ron HessRon Hess
If you put a system.debug ( slabs) ; satement just before the update is performed, is this statement reached?

after adding debug statements , then running the system log window with apex code category set to debug, you should see messages that will allow you to understand why the data is now not writing to the database.
ClaiborneClaiborne
Ron,

Thanks for replying.

I put the debug statements in. There is a snippet below:
Code:
Element assignSlabsButton called method {!assign} returned type PageReference:/a003000000BMHlXAAX
20080614144953.972:Class.VTRZ.AssignSlabs.getSlabs: line 18, column 32: SOQL query with 9 rows finished in 35 ms 
20080614144953.972:External entry point: returning LIST:SOBJECT:VTRZ__Slab__c from method public LIST:SOBJECT:VTRZ__Slab__c getSlabs() in 35 ms 
20080614144953.972:Class.VTRZ.AssignSlabs.assign: line 64, column 9: 0 
20080614144953.972:Class.VTRZ.AssignSlabs.assign: line 68, column 17: 0 
20080614144953.972:Class.VTRZ.AssignSlabs.assign: line 78, column 17: (VTRZ__Slab__c:{VTRZ__Status__c=Inventory, VTRZ__Order_Line__c=a0H30000000bqwOEAQ, VTRZ__Finished_Date__c=2008-06-11 00:00:00, VTRZ__Assigned__c=true, Name=Slab 000353, VTRZ__Quality_Rating__c=1, Id=a023000000A7BidAAF, VTRZ__Slab_Color__c=a013000000I0dodAAB}, VTRZ__Slab__c:{VTRZ__Status__c=WIP, VTRZ__Order_Line__c=null, VTRZ__Assigned__c=false, Name=Slab 000354, VTRZ__Quality_Rating__c=1, Id=a023000000A7C1lAAF, VTRZ__Slab_Color__c=a013000000I0dodAAB}, VTRZ__Slab__c:{VTRZ__Status__c=Inventory, VTRZ__Order_Line__c=null, VTRZ__Finished_Date__c=2008-06-11 00:00:00, VTRZ__Assigned__c=false, Name=Slab 000355, VTRZ__Quality_Rating__c=1, Id=a023000000A7C1mAAF, VTRZ__Slab_Color__c=a013000000I0dodAAB}, VTRZ__Slab__c:{VTRZ__Status__c=Inventory, VTRZ__Order_Line__c=null, VTRZ__Finished_Date__c=2008-06-11 00:00:00, VTRZ__Assigned__c=false, Name=Slab 000356, VTRZ__Quality_Rating__c=1, Id=a023000000A7C1nAAF, VTRZ__Slab_Color__c=a013000000I0dodAAB}, VTRZ__Slab__c:{VTRZ__Status__c=Inventory, VTRZ__Order_Line__c=null, VTRZ__Finished_Date__c=2008-06-11 00:00:00, VTRZ__Assigned__c=false, Name=Slab 000357, VTRZ__Quality_Rating__c=1, Id=a023000000A7C1oAAF, VTRZ__Slab_Color__c=a013000000I0dodAAB}, VTRZ__Slab__c:{VTRZ__Status__c=Inventory, VTRZ__Order_Line__c=null, VTRZ__Finished_Date__c=2008-06-11 00:00:00, VTRZ__Assigned__c=false, Name=Slab 000358, VTRZ__Quality_Rating__c=1, Id=a023000000A7C1pAAF, VTRZ__Slab_Color__c=a013000000I0dodAAB}, VTRZ__Slab__c:{VTRZ__Status__c=Inventory, VTRZ__Order_Line__c=null, VTRZ__Finished_Date__c=2008-06-11 00:00:00, VTRZ__Assigned__c=false, Name=Slab 000359, VTRZ__Quality_Rating__c=1, Id=a023000000A7C1qAAF, VTRZ__Slab_Color__c=a013000000I0dodAAB}, VTRZ__Slab__c:{VTRZ__Status__c=Inventory, VTRZ__Order_Line__c=null, VTRZ__Finished_Date__c=2008-06-11 00:00:00, VTRZ__Assigned__c=false, Name=Slab 000360, VTRZ__Quality_Rating__c=1, Id=a023000000A7C1rAAF, VTRZ__Slab_Color__c=a013000000I0dodAAB}, VTRZ__Slab__c:{VTRZ__Status__c=Inventory, VTRZ__Order_Line__c=null, VTRZ__Finished_Date__c=2008-06-11 00:00:00, VTRZ__Assigned__c=false, Name=Slab 000361, VTRZ__Quality_Rating__c=1, Id=a023000000A7C1sAAF, VTRZ__Slab_Color__c=a013000000I0dodAAB}) 
20080614144953.972:Class.VTRZ.AssignSlabs.assign: line 79, column 17: Update: LIST:SOBJECT:VTRZ__Slab__c 
20080614144953.972:Class.VTRZ.AssignSlabs.assign: line 79, column 17: DML Operation executed in 275 ms 
20080614144953.972:Class.VTRZ.AssignSlabs.assign: line 85, column 37: SOQL count() query with 1 row finished in 4 ms 
20080614144953.972:Class.VTRZ.AssignSlabs.assign: line 89, column 17: Update: SOBJECT:VTRZ__Order_Line__c 
20080614144953.972:Class.VTRZ.AssignSlabs.assign: line 89, column 17: DML Operation executed in 42 ms 
20080614144953.972:External entry point: returning System.PageReference from method public System.PageReference assign() in 326 ms Cumulative profiling information:

What appears to be happening is that when the method assign is called from the VisualForce page, it is retrieving the slabs list from storage rather than passing the slabs object from the VisualForce page.

I think I am missing something here, but I have no idea what.

jwetzlerjwetzler
I think there's something going on here with regards to how you are getting your information.  You're calling all of your getters in your constructor, which I don't really understand.

You're basically running all of your queries right away and saving them off.  Then your page is going to call all of your getters and re-run the queries.
Then in your update function you're using what you saved off in your constructor, before any buttons were pressed, so you're not going to have any updated values.

I think what you want to do is instead of calling your getters in your constructor, turn all of your getters into lazy loads, like
Code:
    List<Slab__c> slabs;
    
    public AssignSlabs() {
    }
    
    public List<Slab__c> getSlabs() {
      if (slabs == null) {
        slabs = [
            select Name, comments__c, slab_color__c, status__c, 
                finished_date__c, assigned__c, order_line__c, 
                quality_rating__c from slab__c
            where slab_color__c = :ApexPages.currentPage().getParameters().get('slabcolor') 
                AND Order_Line__c = NULL 
            order by slab_color__c, finished_date__c];
      }
      return slabs;
    }

I'm not totally sure but I think this might help you because you won't be performing the query more than once, to ensure you're using the same data in your action method that your page was using.

Try that with all of your getters and see if it works.  It is better for your page performance anyway since you won't be running queries you have already run.

ClaiborneClaiborne
I am very confused.

I tried to turn my getters into lazy getters, but I had to rename the variable to get anything out.

But still nothing is getting to the save function (called assign).

I have dumbed down the function trying to get anything to work.

Here is the class:

Code:
public class AssignSlabs {
    List<Slab__c> slabs;
    Order_Line__c orderline;
    String headerMessage;
    String errorMessage;
    
    public AssignSlabs() {
        orderline = getOrderLine();
    }   

    public List<Slab__c> getSlabs() {
        if (slabs == null) {
            List <Slab__c> newSlabs = [
                select Id, Name, comments__c, slab_color__c, status__c, 
                    finished_date__c, assigned__c, order_line__c, 
                    quality_rating__c, order__c
                from slab__c
                where slab_color__c = :ApexPages.currentPage().getParameters().get('slabcolor')
                    AND Status__c <> 'Scrapped' 
                    AND (Order_Line__c = NULL
                        OR Order_Line__c = :ApexPages.currentPage().getParameters().get('olid')) 
                order by Name];
            return newSlabs;
        }
        else {
            return slabs;
        }
    }

    public Order_Line__c getOrderLine() {
        Order_Line__c orderline = [
            select id, Name, slab_color__r.Name, order__c, 
                quantity__c, assigned__c, unassigned__c
            from order_line__c 
            where id = :ApexPages.currentPage().getParameters().get('olid')]; 
        return orderLine;
    }

    public String getHeaderMessage() {
         String headerMessage = 'This is a list of all available ' +
             orderline.slab_color__r.Name + ' slabs for ' +
             orderline.Name + '. This order line requires ' +
             orderline.unassigned__c.intValue() + ' more slabs to ' +
             'complete the order of ' + orderline.quantity__c + ' slabs.';
         return headerMessage;        
    }
    
    public String getErrorMessage() {
        return errorMessage;
    }
    
    public PageReference assign() {
        update slabs;
        PageReference orderlinepage = new PageReference('/' + orderline.id);
        return orderlinepage;
    }
    
    public PageReference cancel() {
        PageReference orderlinepage = new PageReference('/' + orderline.id);
        return orderlinepage;
    }

}

 Here it the page:
Code:
<apex:page controller="AssignSlabs" tabstyle="Orders__c">
<apex:form id="assignSlabs">
<apex:pageBlock mode="edit">
<apex:outputText value="{!headerMessage}" />
<br/><br/>
<apex:pageblockSection id="Slabs">
   <apex:pageblocktable align="center" value="{!slabs}" var="slab" title="Slabs" width="100%">
         <apex:column headerValue="Slab" width="15%"><apex:inputHidden value="{!slab.id}"/><apex:outputLink value="/{!slab.ID}">{!slab.Name}</apex:outputlink></apex:column>
         <apex:column headerValue="Status" width="15%"><apex:outputField value="{!slab.Status__c}"/></apex:column>
         <apex:column headerValue="Quality Rating" width="20%"><apex:outputField value="{!slab.Quality_Rating__c}"/></apex:column>
         <apex:column headerValue="Finished Date" width="10%"><apex:outputField value="{!slab.Finished_Date__c}"/></apex:column>
         <apex:column headerValue="Comments" width="30%"><apex:outputField value="{!slab.Comments__c}"/></apex:column>
         <apex:column headerValue="Assign Slab—" width="10%"><apex:inputfield value="{!slab.Assigned__c}"/></apex:column>         
   </apex:pageblockTable>
</apex:pageblockSection>
<p/>
<apex:pageBlockSection id="Status">
   <apex:commandButton action="{!assign}" value="Assign Slabs" rerender="Status" id="assignSlabsButton"/>
   <apex:commandButton action="{!cancel}" value="Cancel" id="cancelButton"/>
   <apex:outputLabel value="{!errormessage}"></apex:outputLabel>
</apex:pageblockSection>
</apex:pageBlock>
</apex:form>
</apex:page>

 This code displays the values OK, but when I try to "assign" it gives me

Attempt to de-reference a null object.

 From what I can see, it looks like the slabs list object is returned empty to the "assign" function.

EnderEnder
Well, looks to me like you are trying to reference a null object.  And therefore getting a null pointer exception.

Short version (which may or may not work)
-------------------

change line 2 from:
List<Slab__c> slabs;

to:

List<Slab__c> slabs = new List<Slab__c>();

----------------------------------------------

Long Version:

You're getter is taking the second path of your if statement. Therefore you are returning "newSlabs" rather than slabs (the check for slabs == null should have tipped you off). Now on your command link you are trying to update a null object with values.

That's just null baby.

Really, you should never create the newSlabs variable, and only use slabs in your getter. That would make things go to their happy place.


jwetzlerjwetzler
Yep, you're never actually assigning the variable.  Not really a lazy load.  Please take a look at my example above again.

The idea is that you check for null in your local class variable, and then assign.  You don't need to create a new variable in your method.

Code:
public class AssignSlabs {
    List<Slab__c> slabs;
    Order_Line__c orderline;
    String errorMessage;
    
    public AssignSlabs() {
getSlabs();
getOrderLine();
 } public List<Slab__c> getSlabs() { if (slabs == null) { slabs = [ select Id, Name, comments__c, slab_color__c, status__c, finished_date__c, assigned__c, order_line__c, quality_rating__c, order__c from slab__c where slab_color__c = :ApexPages.currentPage().getParameters().get('slabcolor') AND Status__c <> 'Scrapped' AND (Order_Line__c = NULL OR Order_Line__c = :ApexPages.currentPage().getParameters().get('olid')) order by Name]; } return slabs; } public Order_Line__c getOrderLine() {
if (orderLine == null) {
 orderline = [ select id, Name, slab_color__r.Name, order__c, quantity__c, assigned__c, unassigned__c from order_line__c where id = :ApexPages.currentPage().getParameters().get('olid')];
} return orderLine; } public String getHeaderMessage() { String headerMessage = 'This is a list of all available ' + orderline.slab_color__r.Name + ' slabs for ' + orderline.Name + '. This order line requires ' + orderline.unassigned__c.intValue() + ' more slabs to ' + 'complete the order of ' + orderline.quantity__c + ' slabs.'; return headerMessage; } public String getErrorMessage() { return errorMessage; } public PageReference assign() { update slabs; PageReference orderlinepage = new PageReference('/' + orderline.id); return orderlinepage; } public PageReference cancel() { PageReference orderlinepage = new PageReference('/' + orderline.id); return orderlinepage; } }

 
So now whenever you ask for slabs or orderLine, it's going to see if it's null and if so, do your query.  Otherwise it's going to use the value you've saved off.   I added calls to getSlabs() and getOrderLine() in your constructor just to initialize them.  Optional, depending on if your going to reference them before your page asks for them.  I just want to illustrate what you need to do to get it to load once and only once.  Notice that you don't need to assign them to anything because your lazy loading method does the assignment for you.  Generally instead of referring to slabs or orderline, you want to refer to getSlabs() and getOrderLine(), which will protect you against Null Pointers and other errors that have to do with the order you load things in.  In that case you don't need your constructor.  I will leave that part to you though.

I hope this gives you a better idea of what's going wrong.
ClaiborneClaiborne
Jwetzler and Ender,

Thank you both for the suggestions, but neither one has any impact on the result.

The code is still not working.

When I use the test - if (slabs == null), it always fails, even when slabs is null, so I get no slabs. For whatever reason, "lazy loading" does not work.

Any other ideas?
jwetzlerjwetzler
How can (slabs == null) fail "even when slabs is null"?  Just curious as to how you figured that out. Make sure you replace all references to slabs with getSlabs() to ensure that it is getting loaded before you try to reference it.  Did you copy the exact code I posted?

I'm sure there's something very obvious going wrong, it just has not jumped out at us yet.
ClaiborneClaiborne
OK, I got it work, but I am not sure why. I am able to get the page to display properly by changing the slabs getter to NOT use "slabs" anywhere, and by initially creating slabs in the constructor.

This did not work -

Code:
public class AssignSlabs {
    List<Slab__c> slabs;
    Order_Line__c orderline;
    String headerMessage;
    String errorMessage;
    
    public AssignSlabs() {
        orderline = getOrderLine();
        slabs = getSlabs();
    }   

    public List<Slab__c> getSlabs() {
        if (slabs == null) {
            List<Slab__c> slabs = [
                select Id, Name, comments__c, slab_color__c, status__c, 
                    finished_date__c, assigned__c, order_line__c, 
                    quality_rating__c, order__c
                from slab__c
                where slab_color__c = :ApexPages.currentPage().getParameters().get('slabcolor')
                    AND Status__c <> 'Scrapped' 
                    AND (Order_Line__c = NULL
                        OR Order_Line__c = :ApexPages.currentPage().getParameters().get('olid')) 
                order by Name];
        }
        return slabs;
    }

This worked:

Code:
public class AssignSlabs {
    List<Slab__c> slabs;
    Order_Line__c orderline;
    String headerMessage;
    String errorMessage;
    
    public AssignSlabs() {
        orderline = getOrderLine();
        slabs = getSlabs();
    }   

    public List<Slab__c> getSlabs() {
        if (slabs == null) {
            return [
                select Id, Name, comments__c, slab_color__c, status__c, 
                    finished_date__c, assigned__c, order_line__c, 
                    quality_rating__c, order__c
                from slab__c
                where slab_color__c = :ApexPages.currentPage().getParameters().get('slabcolor')
                    AND Status__c <> 'Scrapped' 
                    AND (Order_Line__c = NULL
                        OR Order_Line__c = :ApexPages.currentPage().getParameters().get('olid')) 
                order by Name];
        }
        else {
            return slabs;
        }
    }

So, as Tennyson said, " . . . Their's not to reason why . . . " but it works.
jwetzlerjwetzler
You did not fix the problem in your first code.  You are still creating a new variable in there which is why it continues to be null.
And in your second code snippet there's still an issue because you are never assigning a value to slabs, which means that it's always going to be null, which means that it's always going to run your query.  It might work, but it's not efficient.

This (your first example) is wrong:
Code:
public class AssignSlabs {
List<Slab__c> slabs;

public List<Slab__c> getSlabs() {
if (slabs == null) {
List<Slab__c> slabs = [
select Id, Name, comments__c, slab_color__c, status__c,
finished_date__c, assigned__c, order_line__c,
quality_rating__c, order__c
from slab__c
where slab_color__c = :ApexPages.currentPage().getParameters().get('slabcolor')
AND Status__c <> 'Scrapped'
AND (Order_Line__c = NULL
OR Order_Line__c = :ApexPages.currentPage().getParameters().get('olid'))
order by Name];
}
return slabs;
}

This is right:
Code:
public class AssignSlabs {
List<Slab__c> slabs;
Order_Line__c orderline;
String headerMessage;
String errorMessage;

public AssignSlabs() {
orderline = getOrderLine();
slabs = getSlabs();
}

public List<Slab__c> getSlabs() {
if (slabs == null) {
slabs = [
select Id, Name, comments__c, slab_color__c, status__c,
finished_date__c, assigned__c, order_line__c,
quality_rating__c, order__c
from slab__c
where slab_color__c = :ApexPages.currentPage().getParameters().get('slabcolor')
AND Status__c <> 'Scrapped'
AND (Order_Line__c = NULL
OR Order_Line__c = :ApexPages.currentPage().getParameters().get('olid'))
order by Name];
}
return slabs;
}

 
Do you see the difference?  One assigns the variable within the scope of the method (the first example) and the other assigns the variable within the scope of the class, which is what you want if you are going to use that variable outside of your class.

This (your second example) is causing your query to get re-run because you never assign slabs:
Code:
public class AssignSlabs {
List<Slab__c> slabs;

public List<Slab__c> getSlabs() {
if (slabs == null) {
// we return our query but never set slabs equal to the results of the query?
return [
select Id, Name, comments__c, slab_color__c, status__c,
finished_date__c, assigned__c, order_line__c,
quality_rating__c, order__c
from slab__c
where slab_color__c = :ApexPages.currentPage().getParameters().get('slabcolor')
AND Status__c <> 'Scrapped'
AND (Order_Line__c = NULL
OR Order_Line__c = :ApexPages.currentPage().getParameters().get('olid'))
order by Name];
}
else {
return slabs; //How could we ever reach this block? slabs has not been set anywhere on this page!
}
}


I highly suggest the second block of code that I posted.  Not just because it's cleaner or even faster (because for all I know it's negligible to you whether or not your query gets run two times or ten), but also because it really only makes sense for it to get run once.  If in between your queries someone changes the slabs out from under you, your data might not do what you expect it to.