GWT UiBinder – Passing Objects to Widgets

February 24th, 2010

A couple of weeks ago I wrote a GWT 2.0 tutorial for passing simple values to a widget and this is the (promised) follow up on how to pass an object to a widget. For more info on working with the new UiBinder, see Declarative Layout with UiBinder at the GWT site.

The Entry Point class is fairly simple; it creates a new MyPanel object and adds it to the RootPanel.

MyEntryPoint.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.jeffdouglas.client;
 
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.ui.RootPanel;
 
public class MyEntryPoint implements EntryPoint {
 
  @Override
  public void onModuleLoad() {
    MyPanel p = new MyPanel();
    RootPanel.get().add(p);
  }
 
}

In the constructor, the MyPanel owner class creates a new SomeObject object with some text and then initializes the widget. The @UiFactory annotation is how you provide arguments for the constructor of the SomeWidget widget. The UiBinder template simply sets up the name space and adds the widget to the HTMLPanel.

MyPanel.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.jeffdouglas.client;
 
import com.google.gwt.core.client.GWT;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiFactory;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Widget;
 
public class MyPanel extends Composite {
 
  private static MyPanelUiBinder uiBinder = GWT.create(MyPanelUiBinder.class);
 
  interface MyPanelUiBinder extends UiBinder<Widget, MyPanel> {
  }
 
  private SomeObject someObject;
 
  public MyPanel() {
    this.someObject = new SomeObject("My object text");
    initWidget(uiBinder.createAndBindUi(this));
  }
 
  // Add a UI Factory method for the sub-widget & pass object
  @UiFactory
  SomeWidget makeSomeWidget() {
    return new SomeWidget(someObject);
  }
 
}

MyPanel.ui.xml

1
2
3
4
5
6
7
8
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
	xmlns:g="urn:import:com.google.gwt.user.client.ui"
	xmlns:c="urn:import:com.jeffdouglas.client">
	<g:HTMLPanel>
	   <c:SomeWidget/>
	</g:HTMLPanel>
</ui:UiBinder>

The SomeWidget class is pretty simple also. The constructor accepts SomeObject, sets it to the class member, initializes the widget and then sets the text of the displayText UiField to the name value in the SomeObject.

SomeWidget.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.jeffdouglas.client;
 
import com.google.gwt.core.client.GWT;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Widget;
 
public class SomeWidget extends Composite {
 
  private static SomeWidgetUiBinder uiBinder = GWT
          .create(SomeWidgetUiBinder.class);
 
  interface SomeWidgetUiBinder extends UiBinder<Widget, SomeWidget> {
  }
 
  private SomeObject someObject;
 
  @UiField Label displayText; 
 
  public SomeWidget(SomeObject so) {
    this.someObject = so;
    initWidget(uiBinder.createAndBindUi(this));
    displayText.setText(someObject.getName());
  }
 
}

SomeWidget.ui.xml

1
2
3
4
5
6
7
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
    xmlns:g="urn:import:com.google.gwt.user.client.ui">
    <g:HTMLPanel>
        <g:Label ui:field="displayText"/>
    </g:HTMLPanel>
</ui:UiBinder>

SomeObject.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.jeffdouglas.client;
 
public class SomeObject {
 
  private String name;
 
  public SomeObject(String name) {
    this.name = name;
  }
 
  public String getName() {
    return name;
  }
 
  public void setName(String name) {
    this.name = name;
  }
 
}


  • Share/Bookmark

Categories: GWT

No Comments

Relationship Lookup Objects in Triggers are NULL?

February 23rd, 2010

I see this question once in awhile on the Salesforce.com message boards so I thought I’d put something together. So in this scenario you have a Sales_Order__c custom object which has a lookup relationship to Opportunity. When processing the Sales_Order__c records in your trigger, you want to access some fields on the Opportunity via the relationship. Your trigger might look something like this:

1
2
3
4
5
6
7
8
9
trigger SalesOrderUpdate on Sales_Order__c (before update) {
 
     for (Sales_Order__c so : Trigger.new) {
         System.debug('Opportuny Id: '+so.Opportunity__c);
          // Opportunity__r will be NULL
          System.debug('Opportuny: '+so.Opportunity__r.StageName);
     }
 
}

However, every time you run the Trigger, the Opportunity is null even though there is a valid Id in Opportunity__c. The reason is that for scalability, the Force.com platform doesn’t perform an in-memory lookup for each relationship in your object. You need to do that yourself. The good thing is that the solution is relatively painless and is safe for bulk transactions.

In the trigger below you first want to create a set of unique Opportunity Ids that are in the trigger context. You then use those Ids to query and create a map with the Opportunity Id as the key and the Opportunity object as the value. Then when you process your Sales_Order__c records you can access the Opportunity object from the map by its Id (line 14).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
trigger SalesOrderUpdate on Sales_Order__c (before update) {
 
     // create a set of all the unique opportunity ids for SOQL below
    Set<Id> oppIds = new Set<Id>();
    for (Sales_Order__c so : Trigger.new)
         oppIds.add(so.Opportunity__c);
 
     // create a map so that Opportunity is locatable by its Id (key)
     Map<Id, Opportunity> oppsMap = new Map<Id, Opportunity>(
          [SELECT Amount, StageName FROM Opportunity WHERE Id IN :oppIds]);
 
     for (Sales_Order__c so : Trigger.new) {
          // fetch the Opportunity from the map by its Id
          System.debug('Opportunity: '+oppsMap.get(so.Opportunity__c).StageName);
          // perform some type of business operation now...
     }
 
}


  • Share/Bookmark

Categories: Apex, Salesforce

1 Comment

SOQL – How I Query With Thee, Let Me Count the Ways

February 22nd, 2010

I’ve been wanting to write this post since the new aggregate functions were announced in Spring 10. With the new 18 version of the API in sandboxes I can finally do a high level overview of SOQL (Salesforce Object Query Language) for all of the newcomers to the Force.com platform. This is definitely not an all-encompassing look as SOQL but something to whet your appetite with examples and screenshots. The Salesforce SOQL docs have a lot of great detailed info, but one of the things that I think is missing is what the results of the SOQL queries look like. This article does not cover Dynamic SOQL.

Most developers are familiar with SQL (Structured Query Language) so SOQL isn’t much of a stretch. There are some really cool features of SOQL that you will find extremely productive but there are some restrictions that will make you want to pull your hair out. However, as with everything on the Force.com platform, SOQL is evolving and becoming better and better overtime. From a high level, there are a few important differences in the two languages:

With SQL you can retrieve and modify datasets directly. However, with SOQL you can only retrieve datasets. You can then use DML statements with these records to performs inserts, updates, deletes and upserts (creates new records and updates existing records within a single statement).In SQL joins are second-nature to retrieve data from multiple tables. However, with SOQL, one of my favorite features is its support for dot notation to join related objects implicitly. We’ll look at both child-to-parent and parent-to-child relationship shortly.The biggest thing to remember is that SOQL is not SQL. SOQL does not support full SQL syntax so you’ll have to rethink the way you query for data. With Spring 10 Saleforce.com has added some new aggregate functions (finally!!) and SOQL functions but there will be many times when you’ll have to break open the docs to see what you can and can not do with SOQL.

  • With SQL you can retrieve and modify datasets directly. However, with SOQL you can only retrieve datasets. You can then use DML statements with these records to performs inserts, updates, deletes and upserts (creates new records and updates existing records within a single statement).
  • In SQL joins are second-nature to retrieve data from multiple tables. However, with SOQL, one of my favorite features is its support for dot notation to join related objects implicitly. We’ll look at both child-to-parent and parent-to-child relationship shortly.
  • The biggest thing to remember is that SOQL is not SQL. SOQL does not support full SQL syntax so you’ll have to rethink the way you query for data. With Spring 10 Saleforce.com has added some new aggregate functions (finally!!) and SOQL functions but there will be many times when you’ll have to break open the docs to see what you can and can not do with SOQL.

Relationship Queries

Relationships are arguably the most powerful feature of SOQL. They allow you to perform joins on multiple objects and traverse the relationship chain to find and return objects. Relationships are available for standard and custom objects in Salesforce. However, their syntax is slightly different. The classic relationship example is accounts and contacts. An account (parent) can have multiple contacts (children).

Child-to-Parent Relationship
This type of query returns child objects (contact) containing parent (account) information:



For child-to-parent relationships with custom objects the syntax is slightly different but the results are essentially the same. In the query below there is a custom object (Affiliate__c) with a lookup relationship on the Account object. Notice in the query below we use the “__r” instead of the object’s name since this is the name of the relationship. This will be explained shortly in the parent-to-child section below.



For each relationship you can specify up to 5 levels. This makes for some really cool queries that let you reach way up the relationship chain to fetch data. For instance you can write a query like this which spans only 4 levels.



Parent-to-Child Relationships
These types of queries are almost always one-to-many. You specify the relationship using a subquery, where the initial member of the FROM clause is the subquery is related to the initial member of the outer query FROM clause. Here’s where the relationship comes into affect. With standard objects you use the plural name of the object in the subquery. Here’s how you would find the name of the relationship using Eclipse:



The following query returns the name for each account and then for each account another collection of contacts containing their first name, last name and email address.



For custom objects the relationships are slightly different. When you create a relationship between two objects, the Force.com platform generates a relationship name for you. So if your relationship field name is “Foo__c” the relationship name would be “Foo__r”. I’ve seen some really strange relationship names generated so you can change the name (here are instructions how) if you need to. So a sample query might look like:



You can get really crazy with relationships and where clauses in the subqueries and run something like the following.



Joins
SOQL also support semi-joins and anti-joins. Some examples are:

Semi-Joins with IN query



ID field Semi-Join



ID field Anti-Join



Multiple Semi-Joins or Anti-Joins



Aggregate Functions

Aggregate Functions are the long-awaited additions to SOQL for Spring 10. Not all fields are supported so check out this list for details.

COUNT() returns the number of rows that match the filtering conditions and COUNT() must be the only element in the select list. The resulting query result size field returns the number of rows and the records will returns null.



COUNT(fieldname) returns the number of rows that match the filtering conditions and have a non-null value. An AggregateResult object in the records field contains the number of rows. Do not use the size field for the resulting records.



COUNT_DISTINCT() returns the number of distinct non-null field values matching your query criteria.



SUM() returns the total sum of a numeric field based up your query criteria.



AVG() returns the average value of a numeric field based up your query criteria.



MIN() returns the minimum value for a field.



MAX() returns the maximum value for a field.



GROUP BY
As in SQL you can use GROUP BY to summarize and roll up your query results instead of processing records individually. There are a number of options, restrictions and conditions for group by, so check out the docs for more info.



You can also calculate subtotals for aggregate data in query results by using GROUP BY ROLLUP.



HAVING
Using HAVING you can filter the results returned by aggregate functions where you might normally want to use a WHERE clause. For instance, this query will fail:



This query will return the correct results:



Date Functions
The Force.com platform provides you with a number of date functions that can be used in SOQL to make your life easier. Check out the docs for a complete list.


  • Share/Bookmark

Categories: Salesforce

7 Comments

UiBinder with SuggestBox & MultiWordSuggestOracle

February 11th, 2010

I’ve been working on a somewhat large and complex GWT project using UiBinder over the past couple of weeks. I’ve built a number of widgets that use the SuggestBox and MultiWordSuggestOracle but I created a new UiBinder, populated the suggestions but they came up blank in the type-ahead. After about a half hour of scratching my head I looked back at some other code and figured it out. There are very few examples of the SuggestBox with UiBinder so I thought this might help someone out.

The UiBinder template (MyWidget.ui.xml) is fairly simple:

1
2
3
4
5
6
7
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
    xmlns:g="urn:import:com.google.gwt.user.client.ui">
    <g:HTMLPanel>
        <g:SuggestBox ui:field="mySuggestBox"/>
    </g:HTMLPanel>
</ui:UiBinder>

The owner class (MyWidget.java) is where you need to make sure you have things correct. On line #20 you need to ensure you have (provided = true) or your suggestions will not show in the type-ahead. You also need to create your SuggestBox before you initialize the UiBinder;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.jeffdouglas;
 
import com.google.gwt.core.client.GWT;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.MultiWordSuggestOracle;
import com.google.gwt.user.client.ui.SuggestBox;
import com.google.gwt.user.client.ui.Widget;
 
public class MyWidget extends Composite {
 
  private static MyWidgetUiBinder uiBinder = GWT.create(MyWidgetUiBinder.class);
 
  interface MyWidgetUiBinder extends UiBinder<Widget, MyWidget> {
  }
 
  private final MultiWordSuggestOracle mySuggestions = new MultiWordSuggestOracle();
 
  @UiField(provided = true) // MAKE SURE YOU HAVE THIS LINE
  SuggestBox mySuggestBox;
 
  public MyWidget() {
    mySuggestBox = new SuggestBox(mySuggestions);
    initWidget(uiBinder.createAndBindUi(this));
    getSuggestions();  
  }
 
  private void getSuggestions() {
    // call some service to load the suggestions
    mySuggestions.add("Suggestion #1");
    mySuggestions.add("Suggestion #2");
    mySuggestions.add("Suggestion #3");
  }
 
}


  • Share/Bookmark

Categories: GWT

2 Comments

Salesforce.com Nonprofit Starter Pack Kicks Butt!

February 9th, 2010

With Salesforce.com’s recent announcement that they will “donate free licenses for our technology to organizations headquartered in Haiti (for the next 10 years)”, I’m sure the guys over at the Salesforce.com Foundation are busier than ever. If you are interested in donating your time to a nonprofit, contact the Salesforce.com Foundation and see how you can help.

The Saleforce.com Foundation has developed a great set of open-source Salesforce.com customizations to support some common nonprofit business processes called the Nonprofit Starter Pack (NPSP). If you are a nonprofit looking to implement Salesforce.com or a developer looking to help out, you should seriously look into the NPSP.

I’ve implemented the NPSP at small nonprofits (10 employees) as well as large ones (3000+ employees) and it’s use cases are applicable to virtually all of them. Steve Andersen, the Solutions Architect responsible for the NPSP, has done a great job updating, enhancing and supporting the NPSP. The installation process is well documented and smooth and the packages are feature rich, flexible and robust. I’ve had the pleasure of meeting and interacting with Steve and he’s a great advocate for the Foundation and nonprofits in general. Steve and the Foundation have groomed a great culture of help and support within the Salesforce.com community. I’ve posted questions a number of times to the NPSP discussion group and received answers from Steve and other members within hours.

The NPSP is broken into the following packages (with my comments):

Nonprofit Starter Pack Contacts And Organizations – This is probably the most important package to install. It solves a major problem when dealing with individuals within Salesforce.com. With this package you can easily treat individuals as accounts to hook into existing Salesforce functionality.

Nonprofit Starter Pack Households – This is another custom object that allows you to group contacts into Households. We didn’t use this package at the last implementation that I worked on but it’s essential if you want to create mailings that limit the amount of correspondence to a single address.

Nonprofit Starter Pack Recurring Donations – This package is pretty slick for creating recurring donations, a common theme with pledges. For example, you may have an individual that has pledged to donate $1000 per month. When you create the donation, the NPSP will create a series of Opportunities providing pipeline forecasting.

Nonprofit Starter Pack Relationships – This package allows you to connects two contacts together in a relationship. With nonprofits, it’s not what you know but who you know. This package allows you to track who knows who and then leverage those relationships.

Nonprofit Starter Pack Affiliations – Another great package to track associations. However, in this case it tracks relationship of people to organizations (accounts). You can track when a person worked for a specific company or when they were a board member for a specific foundation. It also tracks dates so you can see their current status of affiliation.

Steve has put together a large number of “how to” videos for the NPSP. I used them for general help but also showed them directly to the customer to see if each package met their needs. These videos are great time-saver.

Installation – One thing that really perplexed me was installation. Initially I could not find out how to install each package. Each package has it’s own installation link and instructions. Click on one of the package links above and scroll down to the bottom of the page to find the installation link:





  • Share/Bookmark

Categories: Salesforce

6 Comments

Using Hierarchy Custom Settings in Salesforce.com

February 8th, 2010

Last month I posted on the new List Custom Settings feature released in Winter ‘10. I’ll finally round out the topic with the other flavor of custom settings: Hierarchy.

In Winter ‘10, Salesforce.com introduced Custom Settings which allow you to store custom data sets and associate them on an org-wide, profile or user basis. Custom settings are essentially custom objects that are exposed in the applications cache and are accessible via their own API. Using custom settings is much easier than rolling your own solution as they are much faster and easier to access (cached at the application level and accessible via their own interface) and do not count against SOQL limits.

Hierarchy settings allow you to personalize your application for different profiles and/or users. The interface has baked-in logic that drills down into the org, profile, and user level (based upon the current user) and returns the most specific or lowest value in the hierarchy. I’ve found hierarchy custom settings to be extremely useful for those “one off” occasions. Let’s suppose you want to authorize your sales teams to be able to offer a specific discount to customers. You might set up an org-wide custom setting of a 1% discount that everyone is authorized to offer. Now of course you have a set of high-producing sales people that are in their own profile and are able to offer a 5% discount. However (and here is the “one off”), there is that one sales person in that same profile that has lobbied the VP of Sales to be able to offer a 15% discount. With hierarchy custom settings you can accommodate all of these scenarios!!

As with list custom settings, when you create a new hierarchy custom setting, the platform creates a custom object in the background for you (notice the API Name field). You then add additional fields to the custom setting in the same manner that you do for custom objects (you are limited to checkbox, currency, date, date/time, email, number, percent, phone, text, text area and URL fields). I found the management interface for custom settings a little confusing at first and got lost a number of times trying to add fields and/or data.


After you’ve set up your custom settings and added your fields, you can select the Manage link on the custom settings page to add add/edit/delete values for the entire org, a specific profile or individual users. The image below shows the 1% discount for the entire org and then specific settings for the Standard Employees profile and a specific user.


Selecting the View link for the profile displays the settings that apply to users that are part of the “Standard Employees” profile.


Selecting the View link for the “one off” user, displays the settings that are applicable for that user only.




Accessing Custom Settings Programmatically

The cool thing is now you can programmatically access these custom settings based upon the running user and return their most appropriate value (org-wide, profile or user) in formula fields, validation rules, Apex and the Force.com Web Services API with the following interface. Be sure to check out the custom settings docs for more detailed usage.


You can only access hierarchy custom settings in formula field and validation rules. Here is the generic syntax:

{!$Setup.CustomSettingName__c.CustomFieldName__c}


For Apex code there are a number of good examples in the docs so make sure you take a look at them.


  • Share/Bookmark

Categories: Salesforce

7 Comments

GWT UiBinder – Passing Parameters to Widgets

February 5th, 2010

I received a number of emails regarding my last post, GWT UiBinder Hello World Tutorial, specifically how to pass values into widgets using the new GWT 2.0 UiBinder. Here’s a small tutorial on one of the ways in which you could do that. I plan on another tutorial on passing multiple objects using the @UiFactory method.

So here is the Entry Point class. When the module loads, it creates a new MyPanel object, passes the text “Random Text” to the constructor and then adds the panel to the RootPanel.

MyEntryPoint.java

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.jeffdouglas.client;
 
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.ui.RootPanel;
 
public class MyEntryPoint implements EntryPoint {
 
	public void onModuleLoad() {
		MyPanel p = new MyPanel("Random Text");
		RootPanel.get().add(p);
	}
 
}

The MyPanel owner class defines a new constructor that accepts the passed string value (i.e. “Random Text”) and sets the value of hidden field called myField in the UiBinder template to this text.

MyPanel.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.jeffdouglas.client;
 
import com.google.gwt.core.client.GWT;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Hidden;
import com.google.gwt.user.client.ui.Widget;
 
public class MyPanel extends Composite {
 
	private static MyPanelUiBinder uiBinder = GWT.create(MyPanelUiBinder.class);
 
	interface MyPanelUiBinder extends UiBinder<Widget, MyPanel> {
	}
 
	@UiField(provided=true)
	Hidden myField;
 
	public MyPanel(String someText) {
		Hidden myField = new Hidden();
		myField.setValue(someText);
		initWidget(uiBinder.createAndBindUi(this));
	}
 
}

When the UiBinder template runs, its owner class loads the value (“Random Text”) into the Hidden field which then passes this same value into the SomeWidget widget.

MyPanel.ui.xml

1
2
3
4
5
6
7
8
9
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
	xmlns:g="urn:import:com.google.gwt.user.client.ui"
	xmlns:c="urn:import:com.jeffdouglas.client">
	<g:HTMLPanel>
	   <g:Hidden ui:field="myField"/>
	   <c:SomeWidget myField="{myField.getValue}"/>
	</g:HTMLPanel>
</ui:UiBinder>

The SomeWidget owner class is fairly straight forward. There is a setter method for MyField which runs after the constructor, receives the text (“Random Text”) and writes it to the displayText field in the UiBinder template.

SomeWidget.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.jeffdouglas.client;
 
import com.google.gwt.core.client.GWT;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Widget;
 
public class SomeWidget extends Composite {
 
	private static SomeWidgetUiBinder uiBinder = GWT
			.create(SomeWidgetUiBinder.class);
 
	interface SomeWidgetUiBinder extends UiBinder<Widget, SomeWidget> {
	}
 
	@UiField Label displayText;
 
	public void setMyField(String t) {
		displayText.setText(t);
	}
 
	public SomeWidget() {
		initWidget(uiBinder.createAndBindUi(this));
	}
 
}

The UiBinder template simply displays the text in an HTMLPanel.

SomeWidget.ui.xml

1
2
3
4
5
6
7
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
	xmlns:g="urn:import:com.google.gwt.user.client.ui">
	<g:HTMLPanel>
	   <g:Label ui:field="displayText"/>
	</g:HTMLPanel>
</ui:UiBinder>


  • Share/Bookmark

Categories: GWT

6 Comments

Force.com Advanced Developer Programming Assignment Complete!

February 3rd, 2010

I just finished the Programming Assignment and wanted to provide some input for anyone taking it in the future. Without giving away the assignment here are my thoughts:

Allow plenty of time – The instructions suggest you allocate 20+ hours to complete the assignment. I think this is somewhat low as it took me roughly 30 hours to develop, test and deploy. Start early and knock it out before the the exam deadline. I got wrapped up on a project and, of course, finished it in the night before. I tend to work better under pressure.

Read the requirements thoroughly and often – The requirements are narrative in form; something you would expect to receive from a customer. I went through the requirements and created my own design doc which outlined objects, sharing model, triggers, controllers, Visualforce pages, workflow, security, etc. This is the document that I worked from.

Build in Production and code in Sandbox – I did virtually everything in Production which limited the amount of time needed to deploy. I built everything that I could in Production and then once I felt the solution was solid, I refreshed a Sandbox and cranked out the Triggers, Controllers, utility classes and Visualforce pages. Essentially declarative work was done in Production while code development was, of course, done in the Sandbox.

Test, deploy and retest – The test cases took some time but I wanted really strong test coverage. I think I only had one class with less than 100% coverage. After deploying all of my code to Production, I loaded some test data and went through all of the use cases to make sure it functioned as expected.

The essay portion at the testing center was fairly straight forward and took about 30 minutes. It was 6 questions as to how, why and for what reasons did I design the application the way I did.

  • Share/Bookmark

Categories: Salesforce

6 Comments

Adobe Air Applications with Salesforce.com

February 1st, 2010

I finished up an offline Case management POC a couple of weeks ago using the new Adobe Flash Builder for Force.com and was really impressed with it features and functionality given that it is still pre-beta. I’ve built a number of Flex apps for Salesforce.com but the new Stratus framework makes it a breeze with the new Salesforce.com-aware components. You can drag and drop a new LabelAndField component onto your application, wire it to a particular sObject field and it generates the functionality specified by Salesforce.com. So if your field is a picklist it automatically displays a populated combobox and configured default value, a boolean displays a checkbox, a text field displays a text box and so on. The really slick feature is if your field is a lookup to another object, it allows you to either type a value or click the magnifying glass button to search for values from the related object. The framework syncs the values for the related object to the SQLite database for you automatically! No worries. Another huge productivity gain is field validation. The framework generates the same validation you would see in the native Salesforce.com UI. This is a huge time-saver as validation rules typically change often during the lifecycle of a project.

James Ward, the Technical Evangelist at Adobe that works with Salesorce.com, introduced me to Markus Spohn who is the product manager for the Stratus framework at Salesforce.com. I sent both of them my feedback on the product and they were very responsive and eager for input. I have to give Salesforce.com credit as they really listen to customers. My only real concern was related to Flash 4 directly. I am by no means a Flash developer but I can produce Flex application by sheer will of force and brute determination. There are A LOT of really great new components in Flash 4 that will require a substantial investment of time to become proficient. James replied that Adobe is aware of the learning curve for Flash 4 and is working on ways to make the transition as painless as possible.

Markus demoed my POC at the Silicon Valley Flex User Group last week to a couple hundred developers and he said it was well received. These guys are much smarter than I am and I’m sure it gave them a new perspective on Salesforce.com. There are a lot of great possibilities for cross platform applications that can be built when you have access to Salesforce.com, the local file system and an on-board database.


  • Share/Bookmark

Categories: Salesforce

3 Comments

Appirio Company Retreat 2010

January 28th, 2010

We had our first annual company retreat at the Boulder Resort in Phoenix AZ this week as a chance to recharge and focus on our goals for 2010. Since 75% of the company works virtually, it’s nice to get together with 150 of your closest friends. There as quite a bit of eating, drinking, laughing and even some friendly public embarrassment of senior management. There were life-sized posters created, very “revealing” post cards delivered to everyone’s room and even an old karaoke video of one of the founders (Narinder) that popped up. The meeting is still underway today with sales and senior management and they are working hard. On the way out I heard reference to a management dodgeball game scheduled to settle some internal disputes.

Update: The photographer just posted some photos on her blog that are really cool! I like her comment at the bottom of the page, so check that out as well.

Even though the event was about having fun and reinforcing our great team culture, we did manage to get some work done. We outlined our goals for 2010, set some new strategies in motion and even unveiled some incredibly awesome new “stuff” that we will be rolling out this year (the first rule of Fight Club is….?). One of our main topics was how do we keep our hardworking-funloving-cuttingedgetechnologydriven-smacktalking culture intact while essentially doubling the company each year. It’s nice to see that management

One thing that we are very proud of is that we rolled out our new community involvement program that encourages and rewards employees for becoming involved in their community. We even kicked off the program with a team building event with an entire afternoon spent at the McDowell Sonoran Conservancy (yes, the desert) where we raked dirt, planted hundreds of cactus, moved rocks from point A to point B and generally made the desert a more presentable place. I think we actually outworked their expectations as we ran out of stuff plant, move and cleanse. I’m not sure how that happened.

Appirio is a fantastic place to work and we are always hiring smart, hard-working people that love drinking the cloud computing koolaid. If you are interested, drop me a line or check out the link above.


  • Share/Bookmark

Categories: Appirio

No Comments

Feed

http://blog.jeffdouglas.com /