Sparkle XRM provides a great way of creating grids and forms that look and work similar to Dynamics CRM but sometimes you need to create a responsive user interface that is a little different. Luckily there is a wealth of jQuery plugins out there that provide a great starting point. For this post I'm going to show you how to use FreeWall to create a dynamic grid layout of contact cards but the same approach would apply for any other plugin that isn't included in the core Sparkle XRM dependencies library. This sample creates an HTML web resource that lays out your contacts in a responsive grid that resizes depending on the size available.

Create the Script# Import
Whenever we need to use an external library from Sparkle XRM, the external API must be defined as imported classes. For FreeWall we must import the jQuery plugin and the configuration options class. Notice that the [ScriptName("Object")] is used on the configuration options so that although our Script# class uses a strongly typed class, the JavaScript compiled uses an anonymous type so as not to create unnecessary class prototypes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
|
[Imported] |
|
[IgnoreNamespace] |
|
[ScriptName("freewall")] |
|
public class FreeWallPlugin |
|
{ |
|
public jQueryObject container; |
|
public FreeWallPlugin(string selector) |
|
{ |
|
|
|
} |
|
public void reset(FreeWallOptions options) |
|
{ |
|
|
|
} |
|
public void fitWidth() |
|
{ |
|
|
|
} |
|
} |
|
[Imported] |
|
[IgnoreNamespace] |
|
[ScriptName("Object")] |
|
public class FreeWallOptions |
|
{ |
|
public string selector; |
|
public bool animate; |
|
public object cellW; |
|
public object cellH; |
|
public Action onResize; |
|
} |
Create the View Model
It is a good idea to start with the View Model since this defines all the functionality that the View must expose as well as communicating with the server. For this sample we have a simple view model that simply loads a list of contacts into a list so that FreeWall can be bound to it to display our contact cards. It also provides a function to get the Image Url of the contact and return a placeholder image if no image is defined. The contact image can be found by returning the attribute with logical name 'entityimage_url'
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
|
public class ContactCardViewModel : ViewModelBase |
|
{ |
|
public List<Entity> Contacts; |
|
public ContactCardViewModel() |
|
{ |
|
string fetchXml = @"<fetch version='1.0' output-format='xml-platform' |
|
mapping='logical' distinct='false'> |
|
<entity name='contact'> |
|
<attribute name='fullname' /> |
|
<attribute name='telephone1' /> |
|
<attribute name='emailaddress1' /> |
|
<attribute name='contactid' /> |
|
<attribute name='jobtitle' /> |
|
<attribute name='parentcustomerid' /> |
|
<attribute name='address1_city' /> |
|
<attribute name='entityimage_url' /> |
|
<order attribute='fullname' descending='false' /> |
|
</entity> |
|
</fetch>"; |
|
// Load Contacts |
|
EntityCollection contacts = OrganizationServiceProxy.RetrieveMultiple(fetchXml); |
|
Contacts = contacts.Entities.Items(); |
|
} |
|
|
|
public string getImageUrl(Contact contact) |
|
{ |
|
if (contact.EntityImage_Url != null) |
|
return Page.Context.GetClientUrl() + contact.EntityImage_Url; |
|
else |
|
return "../images/EmptyContactImage.png"; |
|
} |
|
} |
Include the library JS
Once you've selected the library you want to use, you'll need to include it in your Crm Solution project under the js folder, and give it a UniqueName and Display Name similar to dev1_/js/freewall.js

Create the HTML View
The HTML View should be added to the html folder and must contain the scaffolding to hook the jQuery library into and initialise the View code. The data binding is done using Knockout's built in templating engine using the 'template' binding.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
|
<div id="freewall" class="free-wall"> |
|
<div data-bind="template: { |
|
name: 'contact-template', foreach: contacts, as: 'contact', |
|
afterRender: Client.InlineSubGrids.Views.ContactCardView.onAfterRender |
|
}"></div> |
|
</div> |
|
<script type="text/html" id="contact-template"> |
|
<div class="brick"> |
|
<img data-bind="attr: { src: $root.getImageUrl(contact)}" width="32" /> |
|
<div class="info"> |
|
<p class='contact-name' data-bind="text: contact.fullname"></p> |
|
<p data-bind="text: contact.jobtitle"></p> |
|
<p data-bind="text: contact.parentcustomeridname"></p> |
|
<p data-bind="text: contact.emailaddress1"></p> |
|
<p data-bind="text: contact.telephone1"></p> |
|
<p data-bind="text: contact.address1_city"></p> |
|
</div> |
|
</div> |
|
</script> |
Create the View Class
The view class's job is to instantiate the View Model and initialise the binding.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
|
public class ContactCardView |
|
{ |
|
static FreeWallPlugin wall; |
|
public static void init() |
|
{ |
|
ContactCardViewModel vm = new ContactCardViewModel(); |
|
|
|
wall = new FreeWallPlugin("#freewall"); |
|
FreeWallOptions options = new FreeWallOptions(); |
|
options.selector = ".brick"; |
|
options.animate = true; |
|
options.cellW = 150; |
|
options.cellH = "auto"; |
|
options.onResize = delegate() |
|
{ |
|
wall.fitWidth(); |
|
}; |
|
wall.reset(options); |
|
|
|
// Data Bind the View Model |
|
ViewBase.RegisterViewModel(vm); |
|
|
|
} |
|
public static void OnAfterRender(Element[] rendered) |
|
{ |
|
// Layout grid everytime an image is loaded |
|
jQuery.FromElements(rendered).Find("img").Load(delegate(jQueryEvent e) |
|
{ |
|
wall.fitWidth(); |
|
}); |
|
} |
|
} |
Notice the OnAfterRender call back – this is called every time a new contact card is rendered because of the binding afterRender: Client.InlineSubGrids.Views.ContactCardView.onAfterRender in the HTML. If this is not done then the freewall grid will not layout until the window is resized.
The result is a nice and responsive layout that optimises the fill the available space and has variable height blocks.

@ScottDurow