
The SalesLogix Mobile client is more customizable than you might think. It’s easy to assume that the new architecture isn’t as customizable as you might be used to with SalesLogix Web or LAN clients. However, it does sometimes take some rethinking. In this article we’ll look at how to add a customization that pulls in data from the Twitter API and displays on the contact detail. We’ll delay the loading of the data so that the contact detail will display quickly and bring in the data from Twitter when it is received.
Background
For this customization what we’ll be doing is adding code to pull in a contact’s last tweet from the Twitter API and displaying it on the contact detail screen in SalesLogix Mobile. While there are a few different ways we could be doing this (for example, we could do this on the server using a custom code property on the entity), we’ll be letting the contact data load normally, and we’ll delay the loading of the data from Twitter until after the contact data has loaded. On the detail screen we’ll see the last tweet, however on the edit screen we’ll see a textbox allowing the user to add a contact’s twitter name. In my test system I have already created a contact property called TwitterName and have redeployed my SData portal so the TwitterName property is in the contact SData feeds.
What exactly do I mean by “delay loading”? What I mean is that instead of going out to Twitter and getting the last tweet while the contact detail is loading, we let the contact detail screen load normally, and then we asynchronously go get the last tweet and update the UI once it is received. Doing things this way will allow the contact screen to load quickly and not be slowed down by whatever slowness the Twitter API might be experiencing at the moment. Also, note that the point of this example isn’t to demonstrate how to grab data from Twitter, or the best way to do that, but to show how to go grab data from any other system using this technique. This could be used with getting something from an external support system, accounting system, or whatever.
Adding the Basic Customization
First, what we’ll need to do is add the basic customization. Meaning, we’ll add the TwitterName property to the query used for the detail and edit screens and also add the UI control elements. If you’ve been following the previous posts in this series, you should already know how to do this:
See Adding Custom Fields to SalesLogix Mobile
Here’s the code for adding the TwitterName to the queries for the edit and detail screens as well as the UI element for the detail and edit screens. Note, that for the UI element on the detail screen, we’re not actually binding the property to it. Instead we’re just adding a “loading” message and icon (we’ll have code that adds the result from the Twitter API to that element later):
// Add TwitterName to the query for the detail view dojo.extend(Mobile.SalesLogix.Views.Contact.Detail, { querySelect: Mobile.SalesLogix.Views.Contact.Detail.prototype.querySelect.concat([ 'TwitterName' ]) }); // Add TwitterName UI element (note, we're only adding the "loading" message here // The data will be loaded in later when recieved from the Twitter API this.registerCustomization('detail', 'contact_detail', { at: function (row) { return row.name == 'CuisinePreference'; }, type: 'insert', where: 'after', value: { name: 'TwitterName', label: 'twitter', property: 'TwitterName', cls: 'content-loading', // add "loading" icon value: 'loading...' // add "loading" message } }); // Add TwitterName to the query for the edit view dojo.extend(Mobile.SalesLogix.Views.Contact.Edit, { querySelect: Mobile.SalesLogix.Views.Contact.Edit.prototype.querySelect.concat([ 'TwitterName' ]) }); // Add TwitterName UI element to the edit view so the user can add a contact's // twitter name this.registerCustomization('edit', 'contact_edit', { at: function (row) { return row.name == 'CuisinePreference'; }, type: 'insert', where: 'after', value: { name: 'TwitterName', property: 'TwitterName', label: 'twitter', type: 'text' } }); |
For the UI element for the detail view, we’re not displaying the TwitterName property itself (yet). Instead we’re just displaying the “loading” message. We still need the TwitterName in the querySelect for the detail screen. Later, we’ll be adding code that gets this from the bound SData entity so we’ll need it to exist in the SData entity for the view. If we took at look at things right now, the screen would look like this on the detail screen:
When we’re done, this “loading” message will display until our request comes back from Twitter – at that point we’ll clear this loading message and replace it with the data returned. It would look like this on the edit screen:
What we need to do now is add some code that goes out to grab the last tweet from the Twitter API and load it into the UI element when it comes back.
Delay Loading the Last Tweet from Twitter
To be able to pull in the last tweet into the UI element we created, we’ll need to extend a method in the base code for the detail screen. To open and view the base code for the detail screen, go to the argos-sdksrc folder and open the Detail.js file. Scrolling through that code, we’ll find the following method:
/** * Saves the SData response to `this.entry` and invokes {@link #processLayout processLayout} by passing the customized * layout definition. If no entry is provided, empty the screen. * @param {Object} entry SData response */ processEntry: function(entry) { this.entry = entry; if (this.entry) { this.processLayout(this._createCustomizedLayout(this.createLayout()), this.entry); } else { this.set('detailContent', ''); } } |
From the comment, we can understand that this method receives the bound SData entity and then loads the data onto the screen. This will be perfect for us to use this and asynchronously load the last tweet from Twitter. We’re not going to edit this file directly. That wouldn’t be a good idea since this code is the base for all detail screens, not just the contact one. Instead, we’re going to extend this function in our ApplicationModule.
To extend this function our code will look similar to how we extend the querySelect on the base contact view. However, instead of extending the querySelect, we’ll be extending the processEntry function. The most important part of this is that we pass everything back to the base code so the view can be loaded normally as well. If we neglect this part our code will execute but the rest of the code in the base Detail.js won’t so none of the UI elements will be added to the view. The empty function would look like this when we add it to our ApplicationModule:
dojo.extend(Mobile.SalesLogix.Views.Contact.Detail, { processEntry: function(entry) { // you MUST pass everything back to base class so it can continue // loading the view Mobile.SalesLogix.Views.Contact.Detail.superclass.processEntry.apply(this, arguments); // WE WILL ADD THE REST OF OUR CODE HERE } }); |
Notice the line:
Mobile.SalesLogix.Views.Contact.Detail.superclass.processEntry.apply(this, arguments);
This line is what passes things back to the base processEntry code, applying the arguments (meaning the SData entity) to it and allowing it to proceed normally. This line is important.
Before we add the rest of the code, we’ll need to make some other parts of dojo available. Specifically, we’ll need to use “dojo/dom-class” and “dojo/query” available to us so we can find and manipulate the UI element to update the value received from Twitter. To do this, we just need to change the header or declaration of our ApplicationModule. Right now the first few lines of our Application Module looks like this (my product name is “Custom”):
define('Mobile/Custom/ApplicationModule', [ 'Sage/Platform/Mobile/ApplicationModule' ], function () { return dojo.declare('Mobile.Custom.ApplicationModule', Sage.Platform.Mobile.ApplicationModule, { loadCustomizations: function () { this.inherited(arguments); |
We’ll use dojo’s AMD loading to bring in these dependencies and change those lines to look like this:
define('Mobile/Custom/ApplicationModule', [ 'Sage/Platform/Mobile/ApplicationModule', 'dojo/dom-class', 'dojo/query' ], function ( ApplicationModule, domClass, query ) { return dojo.declare('Mobile.Custom.ApplicationModule', Sage.Platform.Mobile.ApplicationModule, { loadCustomizations: function () { this.inherited(arguments); |
I’ll cover more on dojo’s AMD loading and mixins in a future post, but for now you can read more on the dojotoolkit.org blog.
Now, let’s add the code that does all the work of going out to the Twitter API and getting the last tweet for this contact:
dojo.extend(Mobile.SalesLogix.Views.Contact.Detail, { processEntry: function(entry) { // you MUST pass everything back to base class so it can continue // loading the view Mobile.SalesLogix.Views.Contact.Detail.superclass.processEntry.apply(this, arguments); // get the TwitterName property from the bound SData contact entity var twitterName = entry['TwitterName']; // locate the TwitterName row var rowNode = query('[data-property="TwitterName"]', this.domNode)[0], contentNode = rowNode && query('span', rowNode)[0]; // remove the "loading" message if (rowNode) domClass.remove(rowNode, 'content-loading'); // exit if the contact does not have a twitter name if (twitterName == null) { contentNode.innerHTML = ''; return; } // now we'll go get the last tweet from twitter dojo.require("dojo.io.script"); dojo.ready(function() { dojo.io.script.get({ url: 'https://api.twitter.com/1/statuses/user_timeline/' + twitterName + '.json?count=1&include_rts=1', callbackParamName: "callback", load: function(data) { var twitterText = "<img src="" + data[0].user.profile_image_url + "" align="left" style="padding-right:8px;">" + data[0].text + "<br />" + "<a href="http://twitter.com/" + twitterName + "">@" + twitterName + "</a>"; // update the node to display the data if (contentNode) contentNode.innerHTML = twitterText; } }); }); } }); |
With this code in place we’ll see the following on the contact detail screen (last tweet from @RyanFarley, and what a “meaningful” tweet it is, lol)
Conclusion
As I mentioned earlier, this isn’t an exercise to see how to best grab a tweet from Twitter, but the process we’ve gone through is how we could go grab any data from any system in the mobile client. We could even go back to SalesLogix using SData to grab some related data of some kind and it would be delay loaded so things stay snappy.
Download the complete ApplicationModule.js file from this article
Ryan:
Thanks for this.
I am trying something similar and I am not sure if I am doing it the right way.
According to the dojo docs, if I am reading them correctly, dojo.io.script is deprecated in favor of dojo.request.script.
However, it seems the default build for SlxMobile 2.0 doesn’t include this library.
Also, according to the docs, dojo.io.script only supports GET, it doesn’t support any other verb. I need to do POSTs in my implementation, so I am using xhrPost.
How would I leverage this info with what you’ve posted here so that I can do POST operations?
Source: http://dojotoolkit.org/reference-guide/1.9/dojo/io/script.html