Creating a Dashlet for Sugar 7 List Views

In our previous "Hello World" dashlet post, we established what a minimal dashlet entailed. In these next post, we'll be building on those skills to create a more useful dashlet that takes advantage of Sugar 7 List Views. We will be creating a dashlet for Cases that binds to the list's Collection and sums the number of Cases by their status. So if the Cases list contains 5 records, and 3 of those are in "New" state and 2 are in "Closed" state then we want our dashlet to display "New: 3" and "Closed: 2". To the code!

File Structure

Again, using what we learned in the previous post, we're going to create a folder in custom/clients/base/views/ called "case-count-by-status". Inside that folder you should create 3 files:

case-count-by-status.php

case-count-by-status.js

case-count-by-status.hbs

You should have something that looks like the following screenshot:

While technically optional, we will also utilize the Language extension in order to provide multilingual support for our example dashlet. This extension file will be located at custom/Extension/application/Ext/Language/en_us.case-count-by-status.php.

Dashlet Metadata (.php file)

<?php/** * Metadata for the Case Count by Status example dashlet view * * This dashlet is only allowed to appear on the Case module's list view * which is also known as the 'records' layout.*/$viewdefs['base']['view']['case-count-by-status'] =array('dashlets'=>array(array(//Display label for this dashlet'label'=>'LBL_CASE_COUNT_BY_STATUS',//Description label for this Dashlet'description'=>'LBL_CASE_COUNT_BY_STATUS_DESCRIPTION','config'=>array( ),'preview'=>array( ),//Filter array decides where this dashlet is allowed to appear'filter'=>array(//Modules where this dashlet can appear'module'=>array('Cases', ),//Views where this dashlet can appear'view'=>array('records', ) ) ), ),);

Dashlet Metadata Filter Options

Currently there are two main dashlet filter keys that you'll see in the codebase; "module" and "view". Across of these filter keys, the main thing to remember is that not specifying a filter at all means that your dashlet will be available in all views of all modules. You only need to add filters if you desire to restrict your dashlet to a specific module or view. Let's look at the filter keys in more detail.

Specifying a filter means your dashlet will be restricted to specified modules and views. Notspecifying a filter means your dashlet will be available in all modules and views.

"module"

The module filter lets you add an array of modules where your dashlet can appear. If you wanted your dashlet to appear in the list of available dashlets for only the Accounts, Cases, and Contacts modules then your module filter would look like the following.

'filter' => array(

'module' => array(

'Cases',

'Accounts',

'Contacts',

),

)

"view"

The view filter lets you add an array of views to limit on which views your dashlet can appear. If you wanted your dashlet to appear only on the Record view, your view filter would look like the following.

'filter' => array(

'view' => array(

'record',

),

)

Currently, there are two possible values for the view filter. The List View is indicated by using "records". The Record View is indicated by using "record".

Dashlet Controller (.js file)

Enough metadata nonsense, now for the fun stuff! Here is the JavaScript controller for a Case Count By Status dashlet.

//Listening to 'add' and 'remove' events on the collectioncollection.on('add remove', function(model, collection, options) {// The Backbone's options argument for 'add' and 'remove' events are different// if options.removed doesn't exist, then we will know this is a 'remove' eventif (_.isUndefined(options.remove)) {options.remove=true; }

// Backbone passes add/remove options as an event param, so we can tell// if this was the add or remove event and pass it to parseModelsthis._parseModels([model], options.remove); }, this);

if(collection.models&&_.isEmpty(this.modelsMap)) {// manually cause a parsing of the models// this covers the scenario when a user creates a new recordthis._parseModels(collection.models, true); } },

_.each(_.values(this.modelsMap), function(status) {this.totalCases++;// check to see if we've already set a value// for this statusif (this.values[status]) {// status is already set so just incrementthis.values[status].count++; } else {// add a new entry on the values object// with status as the key and an Object// with name and count for our templatethis.values[status] = { name: status, count:1 }; } }, this); },

/** * Takes an array of models and parses them into modelsMap then (re)counts the values in this map * * @param{Array}models The Array of models to parse * @param{Boolean}remove If the models passed in should be removed or not * @private*/_parseModels:function(models, remove) {var id, status;

if (remove &&this.modelsMap[id]) {// if the function was called // to remove models, delete the id/valuedeletethis.modelsMap[id]; } else {// otherwise, add the id and status// to modelsMapthis.modelsMap[id] = status; } }, this);

// now that we've updated the modelsMap,// recalculate the this.values object for renderingthis._recalcValues();

// double-check that the view has not been disposed// if not, then re-render the dashletif (!this.disposed) {this.render(); } }})

On a List View, this.context points to the BeanCollection. this.context.parent points to a parent model (when it exists, such as on a dashlet preview).

Dashlet Template (.hbs file)

Again, with a Dashlet you are free to format the display any way you like. However, we do recommend leveraging Sugar's Styleguide so that your dashlet appears like a seamless extension of the Sugar user interface. It is a great reference for you to leverage that allows your dashlets to appear as just another seamless part of the Sugar 7 application.

In this example, we leveraged some dashlet design patterns and CSS pulled directly from the Sugar 7 Styleguide.

The Sugar Styleguide describes how Sugar 7's CSS works and the design patterns used in common components such as Dashlets. The Styleguide is how you build seamless UI for Sugar 7.x.

As a Sugar Admin, navigate to Styleguide > Core Elements > Dashboards > Dashlets to view the Dashlets style guide. Specifically, we borrowed CSS from the "Summary" dashlet example listed in the Styleguide.

Here is our Handlebars template we put together leveraging this "Summary" pattern.

case-count-by-status.hbs

{{!Case Count by Status example dashlet Handlebars templateWe are reusing styling from the Sugar 7 Styleguide for our example dashlet.Here we are borrowing CSS used in our Forecast Details dashlet which is suitable for display any set of name value pairs.}}<divclass="forecast-details">{{#eachvalues}} <divclass="row-fluid"> <spanclass="span6">{{name}} </span> <spanclass="span6 tright">{{count}} </span> </div>{{/each}} <divclass="row-fluid"> <strongclass="span6">{{! 'str' is the Sugar 7 Handlebars helper that translates a label into a localized language string}}{{str"LBL_CASE_COUNT_BY_STATUS_TOTAL"}} </strong> <strongclass="span6 tright">{{totalCases}} </strong> </div></div>

Language Extension (.php file)

Finally, in the above Handlebars template and in the dashlet metadata file we have defined some display labels that need to be translated into human readable strings. To accomplish this, we have added a language extension for these new labels that we have introduced.

It is a development best practice to use labels for strings so that your user interface can be translated and supported in multiple languages

en_us.case-count-by-status.php

<?php// This file will provide English strings for our new labels.// Additional extensions could be created so that our dashlet supports other languages.$app_strings['LBL_CASE_COUNT_BY_STATUS'] ='Case Count By Status';$app_strings['LBL_CASE_COUNT_BY_STATUS_DESCRIPTION'] ='Shows the number of Cases on the Cases List view by status.';$app_strings['LBL_CASE_COUNT_BY_STATUS_TOTAL'] ='Total Cases:';

The "en_us" at the beginning of this filename is significant. It represents the user locale where these strings are used. If you wanted to created translations for other locales, then this value would be different. For example, French language extensions must start with "fr_FR".

Wrapping Up

So now we've got a working example dashlet that takes advantage of a module's List View. Remember to run a Quick Repair and Rebuild and then navigate to your Cases module and add the new dashlet. It should look something like what you see below.

I want to create a module that can be implemented as a "global" dashboard. By Global, I mean, it should be in the list of dashlets that are available if you hit "create dashboard" in the upper left hand corner of the home screen. I notice that the imported module does not appear here. It only shows in the list at the right hand side at the module level after clicking create dashboard and then "add dashlet".

Great post. One small thing I noticed though is that the Language Extension PHP file is showing up instead of the Dashlet Metadata in the Gist. Not a big deal since the links to the code are at the end of the post.

Great post, it help me a lot to understand how the Sugar Dashlets works. But what I can do to develop a Chart Dashlet similar to the Outcome by Month that exists in Sugar 6.5 but no longer in version 7.5. Can you help me with that???

Chart Dashlets will certainly be a topic for a future blog post. There is a Charts plug-in that can be used to build your own custom charts. For now, I'd recommend looking at existing charts in Sugar 7.5 such as opportunity-metrics dashlet.

There are a couple ways to do that Vinco. You could have it pulled in dynamically by adding a LINK tag when your Dashlet renders. Or you can bundle it with Sugar's CSS by installing your custom CSS into custom/themes/custom.less. After running Quick Repair and Rebuild then your custom CSS will be compiled in with Sugar's CSS as well.

You could still package the custom.less file as part of an installable package. You'd be able to install it at the necessary location and then after a Quick Repair and Rebuild the CSS that your dashlet needed would be available in the page.

You do run into problems when a custom.less already exists. So there's advantages to pulling CSS dynamically from dashlet.

Yes, it's possible. If you want to use metadata to define the buttons, you could add a buttons array to your Dashlet metadata file and then use the Field handlebars helper in your Dashlet HBS file to render the buttons.

For example,

{{#each meta.buttons}}

{{field ../this}}

{{/each}}

Otherwise, you can just define the button using HTML and attach a click event handler using jQuery.

Interestingly, in Sugar v6.5.x you probably wouldn't need to create a custom dashlet to achieve the result in this example, but instead just create a summation report and use the old dashlet that allowed you to display results from any report?