41
RESCO JSBridge guide MobileCRM

RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

Embed Size (px)

Citation preview

Page 1: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

RESCO

JSBridge guide MobileCRM

Page 2: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

Contents Introduction ................................................................................................................................................... 2

Including the JSBridge.js file ...................................................................................................................... 2

Recommended META tags ........................................................................................................................ 2

Offline HTML ............................................................................................................................................. 3

URL parameters ......................................................................................................................................... 3

External web pages – limitation (Windows 8.1 app version – not supporting JSBridge). ......................... 4

Web Browser Component ......................................................................................................................... 4

External and 3rd party HTML / JavaScript libraries .................................................................................... 4

Woodford ...................................................................................................................................................... 6

Asynchronous approach ................................................................................................................................ 8

Common errors ......................................................................................................................................... 8

Debugging .................................................................................................................................................... 10

Windows .................................................................................................................................................. 10

Android .................................................................................................................................................... 13

iOS ........................................................................................................................................................... 14

Data access API ............................................................................................................................................ 16

Fetch ........................................................................................................................................................ 16

DynamicEntity ......................................................................................................................................... 20

LoadDocumentBody – load attachment and show it on the HTML page ........................................... 21

Controlling UI ............................................................................................................................................... 23

Entity Form .............................................................................................................................................. 23

OnChange ............................................................................................................................................ 24

OnSave ................................................................................................................................................. 25

Form Commands ..................................................................................................................................... 28

How to create a command in Woodford: ........................................................................................... 28

Creating command via JSBridge .......................................................................................................... 31

Form Manager ......................................................................................................................................... 34

Accessing configuration ............................................................................................................................... 36

Platform-specific Services ........................................................................................................................... 36

Page 3: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

Introduction Resco JavaScript Bridge (also known as JSBridge) is part of the Resco MobileCRM / Advantage application

(client application) which offers interaction of custom HTML pages with the client application. It provides

an API for accessing/modifying the data and controlling the UI components from within the JavaScript.

This document contains description of the main principles of the JavaScript Bridge, main features and API

areas, as well as a brief description of common errors and debugging guidelines. It covers the very basics

and getting started topics like including the JSBridge.js file or recommended HTML page structure, which

you can find in following chapters. It also contains many examples with their description, but for the full

list of all available methods and functions see the JavaScript Bridge Reference document -

http://www.resco.net/MobileCRM/support-jsbridge.aspx

The objective of this document is to make you familiar with the JSBridge concepts and showcase the

most common functionality on the examples. After reading this document you should be able to create

and debug your own custom pages and include them into the client application however knowledge of

HTML and JavaScript technology is recommended at least on a medium level.

Including the JSBridge.js file To start using the features described in this document it is necessary to include the JavaScript Bridge file into the custom page. This is done by simply including JSBridge.js file e.g. like this:

<script type="text/javascript" src="JSBridge.js"></script>

You can always download the latest version of this file at the following location (or go through Support

page on Resco web page) - http://www.resco.net/MobileCRM/downloads/JSBridge.js

While every client application version has a corresponding JSBridge version with specific features, it is

recommended to use the same JSBridge.js file as your client app. For that, we have made available an

archive with all the older versions at this URL -

http://www.resco.net/MobileCRM/downloads/JSBridge_Versions.zip

However it is not enough to include the above mentioned line into your page or script, it is necessary

that also the file itself is present at the specified location. If you are going to use your pages as Offline

HTML (see part XXX) you also need to upload the JSBridge.js file as the Offline HTML. Similarly, if you are

accessing these pages online, the JSBridge.js file should be present at the respective location.

Recommended META tags When constructing your HTML pages, there are a couple of META tags which are useful to know and use.

The first one is used on the platforms that utilize Internet Explorer (Windows 7, 8?) and makes sure that

Internet Explorer 9 Document Mode is being used. Without this line, you might get errors when trying to

use some of the methods from JavaScript Bridge on these platforms. (Typically XXX error.)

To enable IE9 Document Mode include following line into the page’s HEAD section:

<!-- Activate IE9 document mode, if available -->

Page 4: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

<meta http-equiv="X-UA-Compatible" content="IE=edge" />

The second useful tag is for iOS devices. Not using this one won’t result in errors (so it is optional if you

are going to use it or not), but it will allow the page to be pinch zoomed, which is undesired for the

custom UI components that are typically built on top of JSBridge. Also, the page and its UI might be

loaded smaller (to fit the screen) which is typically another undesired effect.

To disable pinch zooming and to load the page on iOS in its corresponding size, use following in the HEAD

section:

<!-- Defined iOS viewport -->

<meta name="viewport" content="initial-scale=1, minimum-scale=1, maximum-

scale=1, user-scalable=false">

Note that using any of those tags on platforms where not supported (e.g. Android) doesn’t cause any

issues, so it is a good practice to include these on all the pages.

Offline HTML Resco JavaScript Bridge comes really handy when you want to visualize mobile data in a very specific way

or if you need to capture more complex business logic in the application. But it wouldn’t be such a

powerful tool without the possibility to include the custom pages into the customization package, so

they don’t have to be available always online.

The option to include the pages (and generally any files) into the customization package is referred to as

Offline HTML capability. You can find this section in the mobile project in Woodford or Resco Advantage

management console where a simple file explorer window allows you to upload the local files and

include them into the package. Then, during synchronization these files are downloaded and available

also locally – or offline.

As has been mentioned in previous sections don’t forget to upload the JSBridge.js file your pages make

use of the Resco JavaScript Bridge. Besides that you can upload any file – an HTML page, JavaScript file,

image, etc. and then reference it on an IFrame in the project. To distinguish the local offline files start

the URL with “file://” and then continue with the path as in Offline HTML directory (e.g. if you have

uploaded the file “Test.html” into root folder of Offline HTML, the URL would look like this

“file://Test.html”). You can also use handy Browse option which allows you to browse files directly in the

Offline HTML folder.

URL parameters When constructing the Offline HTML URLs for IFrames on entity forms you can use following syntax to

dynamically create the URL from entity’s properties. When you use an entity field’s logical name (all

lowercase) in curly brackets – “{ … }”, this will be in run time replaced by the application with the content

of those fields.

Page 5: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

So, if you create a following URL – “file://mypage.html?{name}” and assuming that you have a field

“name” on the entity this will be during runtime replaced with e.g. “file://mypage.html?John”.

Due to the platform specifics it is recommended to get the full URL with all the parameters via

MobileCRM.Platform.getURL function. Some platforms will ignore the additional parameters so if you try

to access them via standard means e.g. document.URL you might get different results on different

platforms.

All this can be very useful for dynamic construction of the URLs and for passing parameters. However

there are some limitations.

Due to these limitations it might be a better idea to access entity properties via Entity Form object

instead of passing the parameters via URL. While the parameters you can pass are more or less limited to

the entity fields and the same fields can be accessed via the entity object on the Entity Form, this is the

recommended and ‘cleaner’ way to do it - see section XXXX for an example.

External web pages – limitation (Windows 8.1 app version – not supporting JSBridge).

Web Browser Component Displaying of the HTML pages within the client application is implemented using Web Browser

Component or its equivalent on the respective mobile platform. This means that a browser window is

displayed in the mobile app with your custom content.

The browser is in fact the standard browser installed on that particular device – on iOS it will be Safari,

on Android Chrome and on Windows devices Internet Explorer. So, when creating your custom pages

consider the capabilities of the corresponding mobile browser. Also bear in mind, that if you want your

pages to be displayed correctly on all the platforms make sure that you are using HTML and JavaScript

features supported by all the browsers and their versions on the targeted mobile devices.

Another important aspect to consider is that when a page is being displayed in Web Browser

Component, some of the features might not be enabled. A typical example is Window.Print() – even

though this function is supported by many mobile browsers, when the browsers are running in the Web

Browser Component (and so in Resco mobile application) this feature is disabled.

Unfortunately there is no list of all the disabled features, commands or tags – this differs from platform

to platform, browser version, OS version and so on. To be entirely sure, make sure you do test the

application and the custom content on all the actual devices.

External and 3rd party HTML / JavaScript libraries The abovementioned applies to 3rd party HTML / JavaScript libraries which you might want to use. Resco

mobile application does not limit the use of such libraries in any way, however make sure that it runs on

the targeted mobile browser (iOS - Safari, Android – Chrome, Windows - Internet Explorer). Also we

would recommend performing tests on the actual devices due to Web Browser Component context and

the limitations / disabled features and tags.

Page 6: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

Depending on the code of the external library it might or might not run. So far, we have successfully used

many 3rd party libraries and components including such popular frameworks like jQuery or D3 for data

visualization.

Page 7: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

Woodford

In this section, you can find how to use offlineHTML in woodford. How attach iframe to specific place in

application.

Add iFrame in woodford

Use iFrame as one of home items.

Page 8: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

In this application this iFrame will be visible in the home items of home screen.

Bind iFrame to edit form.

Navigate to specific entity, then select forms and choose between Edit / Detail form.

Click on iFrame button, to display properties window.

More information about Delay Load checkbox, please find section Controlling UI, onChange section.

Visible checkbox provide function hide / show iFrame in application tab bar.

Page 9: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

Asynchronous approach Most of the JSBridge methods are asynchronous. This approach comes from the platform limitations and

is required by the internal implementation.

The asynchronous approach practically means – very simply put - that when you call a method or a

function it won’t return the value or result immediately, but the code continues to run and only

afterwards, sometime in the future a callback method will be called containing the result.

Typically, when you write synchronous code you call a method and it returns the result ‘immediately’ –

your code waits until the result is returned. For example, if you have a method for returning an Account

– getAccount() – and you will call it in following synchronous way, you can use the returned Account

immediately.

var account = getAccount();

account.DoSomething();

However if getAccount would be an asynchronous method, the code would not wait for the result

(asynchronous methods typically don’t return the result) and therefore you couldn’t call DoSomething

directly afterwards, because account wouldn’t contain data yet. For that a callback method is usually

employed. Similar code, but in asynchronous fashion would look something like this.

getAccount(processAccount);

// 1 – a lot of code can be here

function processAccount(account) {

account.DoSomething();

}

ProcessAccount function is the callback which gets the result as a parameter, once the Account is

retrieved. Only in the callback method it is possible to process this parameter – if we would try to do it

directly afterwards (on the place of comment // 1) this would result in an error. On the place of the

comment // 1 could be an arbitrary amount of code and the callback function can be called anytime in

between or afterwards.

See common errors below for a better overview on what misunderstanding of this concept can lead to.

Common errors The most common error is using the asynchronous result before the result callback is called. Following

snippet shows such a situation (see how this can be tricky to spot because of the in-line callback

function):

Wrong:

var entity = null;

MobileCRM.DynamicEntity.loadById(id, function (err) { if (err) alert(err);

else entity = this; });

alert(entity.primaryName); // “entity” is still “null” here

Page 10: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

Correct:

MobileCRM.DynamicEntity.loadById(id, function (err) { if (err) alert(err);

else gotEntity(this); });

// ... code ...

function gotEntity(entity) {

alert(entity.primaryName); // “entity” is already defined here

}

It is important that it has two parameters – ID of the entity to load and a callback function. In both

examples, this callback is provided inline – the function is defined directly in the method’s parameter

(loadById(id, function(err) { … }); ) and you can see that this can encourage using the result directly

afterwards the loadById call. But that would result in an error, because the callback might not yet be

called and the entity variable might not yet contain data. The callback can be called any time after the

loadById call so you can use the result only in the callback. Therefore your code has to be structure as in

the correct example above.

Another common error is calling the asynchronous method “too late”, e.g. from the form’s “onSave”

handler. If the save is successful, the form is closed immediately and the asynchronous operation is

aborted. The correct implementation should hold the save using SuspendSave and close the form from

JavaScript after all the job is done.

Wrong:

MobileCRM.UI.EntityForm.onSave(function(entityForm) {

var id = entityForm.entity.id;

var fetch = new MobileCRM.FetchXml.Fetch(…);

fetch.execute(…);

}, true);

Right :

(requires defining the custom command “custom_Save” in Woodford/Advantage Server):

MobileCRM.UI.EntityForm.onCommand(“custom_Save”, function(entityForm) {

var fetch = new MobileCRM.FetchXml.Fetch(…);

fetch.execute(…function(res){

MobileCRM.bridge.closeForm();

});

}, true);

Page 11: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

Debugging When creating and testing custom HTML pages and scripts it is many times necessary to debug the

JavaScript code to see what is actually happening. You can use alerts and logging as a substitute for this

option, but in general debugging gives you much quicker insight into the actual execution.

Depending on the platform you are working on, there are different ways how to enable debugging. Here

are described the approaches for Windows (7 and 8.1) and iOS. It is also described how to enable

debugging for Android, but you will need a device with Android OS at least 4.4.

Windows Visual Studio 2012 / 2013 supports the script debugging in external applications. It allows attaching to

the client application (the version for Window 7 and for Windows 8.1 Store) and debug your offline

HTML script code.

Before trying this, you will have to enable the script debugging in Internet Explorer. Open Internet

Options / Advanced and uncheck the box “Disable script debugging”.

After that it will be possible to attach the running process “MobileCRM.exe” to the script debugger in

Visual Studio. Uncheck all Code Types except the “Script”.

Page 12: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

Visual Studio provides the comfortable script debugging including the inline editing. To apply the

changes, you will need to close the form which contains the web browser. Easiest way how to do that is

opening the “Setup” or “Sync” dialog.

It is also possible to debug the Windows Store version of the client application. The only limitation here

is that the online editing is not allowed.

Page 13: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction
Page 14: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

Android Android in the present doesn’t support debugging the web pages opened in 3rd party applications. The

next version of client application will support the debugging on Android 4.4 (KitKat).

https://developers.google.com/chrome-developer-tools/docs/remote-debugging

Page 15: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

iOS iOS also supports debugging the web pages opened in 3rd party applications.

• Install the latest Xcode (6.3 or higher)

http://resco.net/mobilecrm/downloads/DebugBuilds.zip

• Start the iOS simulator

Xcode / Open Developer Tools / iOS Simulator

• Install the Mobile CRM app payload

xcrun simctl install booted DebugBuilds/iPhoneSimulator/MobileCrm.app

• Make the Develop menu visible in Safari

• Connect Web Inspector to the Mobile CRM iFrame

iOS Device :

• Prerequisites

• iOS Developer account

• Development wildcard (*) provisioning profile

• Signing identity (Keychain Access)

• Download the Mobile CRM debug builds

http://resco.net/mobilecrm/downloads/DebugBuilds.zip

• Resign MobileCrm.app with your signing identity

codesign -v -f -s “<signing_identity>“ DebugBuilds/iPhone/MobileCrm.app

• Install resigned app via Xcode

• Connect Safari Web Inspector to the Mobile CRM iFrame

To enable the remote debugging you will need to allow the WebInspector in Safari settings (option -

Advanced)

Page 16: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

After that you can use the Safari WebInspector on your Mac. But first you will have to enable the

“Develop” menu in Safari preferences (tab Advanced).

Now you can connect your iPad via USB, open the client application and navigate to the form showing

your offline HTML. After that you should observe the submenu “iPad” under Safari Develop menu

showing the client application and HTML file opened on its form.

Click on it to connect WebInspector.

Page 17: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

Data access API

One of the primary purposes of Resco JavaScript Bridge is exposing the data of the mobile application in

JavaScript – to allow accessing the records, entities and fields available in the offline storage or directly

on the CRM server. You can then use the obtained data and display them on custom HTML pages or UI

components and visualize them in the required way.

The main Data access API classes are MobileCRM.FetchXml for querying and fetching the records,

MobileCRM.DynamicEntity for loading, saving and accessing the entity instances and additionally

MobileCRM.Metadata for the data schema information.

This chapter explores these classes in more depth providing useful examples with explanation.

Fetch The most common way to get the data is to use FetchXML, the query language you might be familiar

with from Dynamics CRM. It allows creating queries to get the data, similar to SQL selects. For example

you can create a query to get all the Accounts and their associated Contacts, filtered and ordered by

some values. Using objects and methods in FetchXML namespace you can build such a query in the code,

but you can also directly execute a query stored as XML (using method executeFromXML), which might

come handy in some situations.

Following example shows how to create a Fetch expression in code. When executed, it will return all the

Accounts from Redmond, ordered by the ‘Name’ field. Building such a query usually begins with creating

a new instance of FetchXml.Entity and specifying which entity will be queried – in this case Accounts.

Then by adding attributes we specify that only Account’s Name and City will be returned (convenient

method for adding all attributes is also available). We add a link to associated Contacts (through Parent

Customer field) and specify that we need only Full Name and Id from this (contact) entity. To define the

condition a new FetchXml.Filter is created and a where condition on the City is specified. Similarly

ordering of the result is defined by a new instance of FetchXml.Order.

Page 18: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

Finally the Fetch query is executed and the results are processed in the success callback – one of the

elements on the HTML page is filled with HTML code and the application data. In case of an error a

message box with the error is displayed.

Code :

var entity = new MobileCRM.FetchXml.Entity("account");

entity.addAttribute("name");

entity.addAttribute("address1_city");

var linkEntity = entity.addLink("contact", "parentcustomerid",

"accountid", "outer");

linkEntity.addAttribute("fullname");

linkEntity.addAttribute("contactid");

entity.filter = new MobileCRM.FetchXml.Filter();

entity.filter.where("address1_city", "like", "Redmond");

var orderCity = new MobileCRM.FetchXml.Order("name");

var dataSpan = document.createElement("span");

var fetch = new MobileCRM.FetchXml.Fetch(entity);

fetch.execute("Array",

function (result) {

for (var i in result)

{

var contact = result[i]

data.innerHTML += "<p>Account Name : " +

contact[0] + "</p>" + "<p>City : " + contact[1] + "</p>"+"<p>Contact

Name : " + contact[2] + "</p>"

}

},

function (error) {

alert("Error has occurred " + err);

},

null);

Page 19: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

The JavaScript code creates a query, which corresponds to following expression in XML:

<fetch version="1.0" aggregate="false">

<entity name="account"><attribute name="name" />

<attribute name="address1_city" />

<filter>

<condition attribute="address1_city" operator="like"

value="Redmond" />

</filter>

<link-entity name="contact" from="parentcustomerid"

to="accountid" link-type="outer">

<attribute name="fullname" />

<attribute name="contactid" />

</link-entity>

</entity>

</fetch>

The same query can be executed directly as XML, using MobileCRM.FetchXML method as shows

following example.

var xmlData = '<fetch resultformat="Array"><entity

name="account"><attribute name="accountid"/><attribute

name="name"/></entity></fetch>';

MobileCRM.FetchXml.Fetch.executeFromXML(

xmlData,

function (result) {

for(var i in result){

var props = result[i];

processAccount(props[0], props[1]);

}

},

function (err) {

alert('Error fetching accounts: ' + err);

},

null

);

Page 20: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

Executed either way, the result of the query execution on demo data will look something like this (part of

the result):

Page 21: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

DynamicEntity Another way to work with the data is to use Dynamic Entity in JSBridge. This class gives access to one

entity record, allowing you to get the fields or properties, but also allows loading, saving and deleting an

instance. When you execute a FetchXML query, it the result can be an array of dynamic entity objects,

giving you more options for processing the results then plain array – just specify ‘DynamicEntities’ as the

first parameter in the FetchXML execute method.

On the following examples, we will illustrate how to work with this class. At first let see how to access an

existing entity.

For that we need to create a Dynamic Entity object, providing the ID of an existing record. In the

following example we are assuming that accountId variable holds this ID, but it is fairly simple to get this

ID using Fetch XML, see the code in previous chapter.

The first step is creating a Dynamic Entity object passing parameters to the constructor (if you want to

see all the arguments consult JSBridge reference).

var dynamicEntity = new MobileCRM.DynamicEntity("account", accountid);

If you want to access the properties or save them in a variable you can do it like this:

var props = dynamicEntity.properties;

If you want to access only some of the entity properties like ‘firstname’ (use logical name = all lowercase)

you can do it in following way:

var firstName = dynamicEntity.firstname;

Or:

var city = dynamicEntity.properties["address1_city"];

var city2 = dynamicEntity.properties.address2_city;

This way you can very easily access properties of an existing entity instance. If you perform any

modifications you might want to save the record using the save method from JSBrdige. In the next

example we will show how to use all above mentioned methods and properties, but also how to create a

reference (lookup) for the parent entity.

Code:

var newContactId = null; function DynamicEntityExample()

{

// Create reference for associated contact account

var refer = new MobileCRM.Reference("account", accountId); var account = new MobileCRM.DynamicEntity("account", accountId); var props = account.properties; var contact = new MobileCRM.DynamicEntity.createNew("contact"); var firstName = contact.properties["firstname"] = "#New Contact"; var lastName = contact.properties["lastname"] = "Example"; contact.properties["fullname"] = firstName + lastName; // one way to set the reference for account

Page 22: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

contact.properties["parentcustomerid"] = refer; // but also Dynamic entity contains reference, so can be used as lookup contact.properties["parentcustomerid"] = account; contact.save( function (err) { if (!err) {

// store the contact id for further use newContactId = this.id; alert("Associated Contact created"); } else alert(" An Error Has occurred \n" + err); }, null); }

In the code above we have created a contact and associated it to an account. You can see two ways how

to create a lookup and reference the contact. In the save method we stored the contact ID for future

use. Like deleting an entity instance which illustrates the example below and where we are using this

stored ID.

function DeleteRecord() { MobileCRM.DynamicEntity.deleteById("contact", newContactId, function (res) { alert("Record Was deleted"); }, function (err) { alert("An Error has occurred \n" + err); }, null); }

LoadDocumentBody – load attachment and show it on the HTML page A special addition to Dynamic Entity class methods is LoadDocumentBody. Because of different ways of

storing the attachments in the mobile application a dedicated method for loading the attachments from

local (and online) storage is provided. With this method you can load the body of a document –

attachment – and e.g. display in on an HTML page. See following example.

Code:

function LoadBody() { MobileCRM.DynamicEntity.loadDocumentBody("annotation", anntotationId, function (result) { // The result argument contains the string with base64-encoded data var imgElement = document.createElement("img"); imgElement.src = "data:image/png;base64," + result; attachment.appendChild(imgElement); }, function (err) { alert("An Error has occurred \n" + err); },null); }

Page 23: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

In the code above we have provided the method loadDocumentBody with Note’s logical name (=

annotation) and an existing annotation ID. The success callback will process the result so that it will find

the HTML div element with id ‘img’ and inserts an image element containing the loaded data. They are

stored as base64 string, therefore that’s specified first. Nothing else is in fact required – the image is

then displayed on the page!

Page 24: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

Controlling UI Another core aspect of JavaScript Bridge is the exposure of native application UI to the JavaScript. This

means that the Entity Form (in case of an IFrame placed on a form as a tab) is available in the JavaScript

to access, modify and even present change or save handlers.

Additionally commands on the form can be accessed and custom – more powerful handler can be

provided via JavaScript. Commands created directly from JavaScript are also available, however these are

only visible on the IFrame tab.

Finally Form Manger allows opening standard application forms (new or edit) for specified entities. This

ultimately interconnects the custom and native UI, so e.g. when a custom list in HTML is created, after

clicking on an element of this list a native form for the particular entity is displayed.

Entity Form Besides the data access Resco JavaScript Bridge allows also access to the standard UI elements of Resco

MobileCRM application like forms and lists. The main UI element is the entity form, represented by the

MobileCRM.UI.EntityForm class.

This class has many properties for different aspects of the form like entity, form, controllers etc. so to

see the full list click here:

http://www.resco.net/MobileCRM/support-jsbridge.aspx#MobileCRM.UI.EntityForm

With these methods and properties you can make many operations on the form. However you can’t

access the form directly - as usual with Resco JavaScript Bridge you must request the form object using a

callback function with entityForm object. Usually it will be MobileCRM.UI.EntityForm.requestObject.

In the first example we will show how to use the request object function and the usage of isDirty

property. This property comes handy in situations when you have modified or created data directly on

the offline html page and want to indicate that save is necessary (the form is ‘dirty’). If the property is set

to false the user is be able to close the form so the information entered on the HTML tab would be lost.

function SetIsDirty() { MobileCRM.UI.EntityForm.requestObject( function (entityForm) { entityForm.isDirty = true; }, function (err) { alert("An Error Has Occured " + err); }, null); }

As you can see, at first you have to request the form and only in the callback function you can access it.

By setting isDirty to true we have ensured the user will see the Save / Discard Changes / Cancel dialog.

Then we can do actual saving of the data in the onSave callback (see next chapter).

Page 25: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

OnChange In the second example we will show you how to effectively use onChange method and use FetchXML

with Dynamic Entities.

The onChange callback is used to track changes on the form and perform validations, computations … –

you name it. This is the JavaScript equivalent of the onChange rule in Woodford, but without the

limitations of Woodford rules ‘scripting language’. The onChange method is triggered when there is a

change on the form – when one of the fields has been modified (NOTE: it is NOT triggered by changes on

the other kinds of tabs – map, media, list…). It is called whenever a value in one of the fields on the form

changes, but in our case we only want to perform some code when the changed item is the field which

contains the name of a customer. It is very useful that the first argument of our success callback handler

is the Entity Form. This means that there is no need to call requestObject to get the form in this case – it

is automatically passed as parameter.

function OrderComplete() { MobileCRM.UI.EntityForm.onChange( function (entityForm) { var changedItem = entityForm.context.changedItem; // check if changed item is the customer field if (changedItem == "customerid") { var customerId = entityForm.entity.properties.customerid.id; FetchAsDynamicEntity(customerId); } }, true, null ); } function FetchAsDynamicEntity(customerId) {

var entity = new MobileCRM.FetchXml.Entity("account"); // entity.addAttributes(); entity.addAttribute("emailaddress1"); entity.filter = new MobileCRM.FetchXml.Filter(); entity.filter.where("accountid", "eq", customerId); var dynamicEntity = null; var fetch = new MobileCRM.FetchXml.Fetch(entity); fetch.execute( "DynamicEntities", function (result) { if (typeof (result) != "undefined") { for (var i in result) { dynamicEntity = result[0]; } MobileCRM.UI.EntityForm.requestObject( function (entityForm) { entityForm.entity.properties.new_customeremailaddress = dynamicEntity.properties.emailaddress1; }, function (err) { alert(err); },

null ); } }, function (error) { alert("An Error Has occurred " + error); }); }

Page 26: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

The first function registers our onChange handler and it is a good practice to call it immediately after the

HTML / JavaScript is loaded. One important note here – you probably want this code to take effect

immediately when the form is opened, so when adding this page on the form as a tab in Woodford,

make sure that Delay Load option is turned off. This option makes sure that the underlying HTML page is

loaded only when the user clicks on the tab and in this case it is almost the opposite of what we want, so

it is better to disable it and make sure the HTML and the JavaScript is loaded immediately with the form.

Once the onChange handler is successfully registered then it takes effect when a field is changed. The

first thing it checks is if the field which has been modified is the Parent Customer field. As always, we are

using logical names, all in lowercase.

var changedItem = entityForm.context.changedItem; // check if changed item is the customer field if (changedItem == "customerid")

Then we take the customer ID and pass it as a parameter to our function. You can notice two things here

– at first, the entityForm holds an instance of the entity, so we can directly access the data. Secondly, the

form entity is Dynamic Entity so we can access its properties like this.

var customerId = entityForm.entity.properties.customerid.id; var ob = FetchAsDynamicEntity(customerId);

Let’s inspect the fetch in the custom method FetchAsDynamicEntities. As the name suggests we will use

fetch, but the result will be an array of dynamic entities we can work with. As a matter of fact, it will be

only one dynamic entity, because we will filter the entities on the customerId specified as a parameter.

"DynamicEntities", function (result) { if (typeof (result) != "undefined") { for (var i in result) { dynamicEntity = result[0]; }

MobileCRM.UI.EntityForm.requestObject( function (entityForm) { entityForm.entity.properties.new_customeremailaddress = dynamicEntity.properties.emailaddress1; }, function (err) { alert(err); },

null); } }

If the Dynamic Entity was returned successfully specified properties, we will update the email of the

selected customer with content of the field “new_customeremailaddress”.

entityForm.entity.properties.new_customeremailaddress = dynamicEntites.properties.emailaddress1;

OnSave Another equivalent of a Woodford rule in JavaScript is the OnSave. Similarly to the Woodford’s OnSave

rule the JavaScript Bridge’s onSave callback is executed when the form is being saved. However due to

Page 27: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

the asynchronous nature of many calls you might want to call from the onSave handler (like Fetch for

getting some records for validation), we have introduced a special mechanism for the onSave callback

which allows control over the save validation execution.

In the example below we have created a validation function, which will check if the email matches the

regular expression during the Save. Again, please note that if you use event handler like OnSave or

OnChange it is desired to UNCHECK the delay load, so the handlers are registered immediately when the

form is opened and not only when the user switches the tab to the IFrame.

You also need to call your function while loading the offline HTML page so put window.onload =

name_of_function(); in your script.

Once you have the onSave handler in place, you need to understand the mechanism behind the save

validation execution. As soon as the onSave callback returns (true or false) the save execution continues

and the record is saved (or not). But imagine a scenario, where you need to perform a fetch to get some

data to perform the validation in onSave. This would include a call for FetchXml Execute and this function

requires another callback. But! The code in onSave will not wait until the callback is executed and the

data is available. It will inevitably run into the return and end the execution!

For this problem – when you want to call another asynchronous method in the onSave callback, we have

introduced following mechanism. You can call suspendSave before the async method of your choice to

stop the save execution. This will wait, until from any other callback or method you will call resumeSave.

The example below should put more light into this.

function SavingValidation() { MobileCRM.UI.EntityForm.onSave( function (entityForm) { ///<param name="entityForm" type="MobileCRM.UI.EntityForm"></param> var emailDetail = entityForm.getDetailView("General"); var emailItem = emailDetail.getItemByName("emailaddress1"); if (!emailItem.value || emailItem.value.length == 0) entityForm.cancelValidation("Email Is empty."); else if(validateEmail(emailItem.value) == null) entityForm.cancelValidation("Email is in incorrect format \n\n Correct format : e.g. [email protected]"); else {

// Return true to apply all changes return true; } // Return false to ignore all changes //return false },

Page 28: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

true, null); }

function validateEmail(email) { var mailformat = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/; return email.match(mailformat); }

In first step we check if field with email address contains any character and if exist. If not we use

property method cancel validation to stop onSave validation and display error message.

if (!emailItem.value || emailItem.value.length == 0) entityForm.cancelValidation("Email Is empty.");

We use this method also when our email doesn’t match to regular expression to display message with

correct email format. In the situation when email match to reg. expression we return true, to apply all

changes and close form.

Page 29: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

Form Commands So far we have examined how to access the form and react to the change on an item, but now let’s take

a look at another aspect of the form – the commands (actions). In this section we will show you how to

access a command from JavaScript, but also how to create a command in Woodford, while this is a

necessary part of the process. In the example in next section you will find a way how to create the

commands from JavaScript directly.

Commands can be very useful and can allow certain actions throughout the whole form, on all the detail

tabs. The main principle is to create a command in Woodford, but define and bind the logic in JavaScript.

How to create a command in Woodford: The first step of binding a command is creating the actual command using Woodford’s form designer.

Open the form where you want the command to be displayed and hit Edit Commands. There, select

‘New Command’ and enter the name and a label for your custom command.

Please note that a command defined like this will be visible on all the details tabs (tabs like ‘General’,

‘Address’, etc…), but won’t be available on associated tabs, IFrame tabs, Map tabs, etc…

Once your command is defined, just add the HTML / JavaScript file in the Offline HTML section in

Woodford and using Add IFrame and Browse add it onto the form.

Page 30: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

In our scenario we will create a command that will delete the associated parent customer for a contact

entity as well as the contact itself. So we have added a custom new command on the Contact form and

prepared following JavaScript code for handling this command.

As you can see in the code below we are defining a custom onCommand callback – this gets triggered

when a command is pressed – and with the first parameter which is the name of command, we specify

that we want this callback to be called only for our command with name ‘custom_Delete’. Second

parameter is of course the callback function with entity form as a parameter. Our command is created

on the Contact entity form so we can find parent customer’s ID for the associated record via entity

form’s property ‘entity’. In this situation our contact is associated to one parent only, but in the case

when there would be more records for one contact you will need to create a fetch with additional filter

and pass the results to “delete Item(entityName,id)”. When you have the entity name and correct ID,

you can call Dynamic Entity function deleteById to erase the Account record. In our example after all this

is done we call following to close the current form:

MobileCRM.bridge.closeForm();

Full code:

MobileCRM.UI.EntityForm.onCommand(

"custom_Delete", // Name of custom command created in woodford function (entityForm) {

deleteItem(entityForm.entity.properties.parentcustomerid.entityName,

entityForm.entity.properties.parentcustomerid.id);

Page 31: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

deleteItem(entityForm.entity.entityName, entityForm.entity.id)

close();

}, true, null

); // delete the record function deleteItem(entityName, id) {

var entity = new MobileCRM.DynamicEntity.deleteById(entityName, id, // Delete existing Dynamic entity by Exsting entity ID function (success) {

if (success != null) { alert("Update sucess error " + success);

} else {

alert("Item Was deleted"); }

}, function (err) {

if (err != null) { alert("Update failed error " + err);

} else {

alert("Item Was deleted"); }

}, null);

} function close() {

// Close Form containing the HTML MobileCRM.bridge.closeForm();

}

Page 32: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

Creating command via JSBridge We will continue exploring the commands on the forms and this time we will show you how to create a

command the on the IFrame tab. We will create three commands to showcase some of the features of

JavaScript Bridge – one to scan barcode, another to get the device info and third to update the location.

The big difference between previous approach and this one is that we are not binding the commands

created in Woodford with JavaScript methods, but we are creating new commands from the JavaScript

directly. These commands will be displayed ONLY on the IFrame tab!

As for the commands itself, they will show different special features of JavaScript Bridge – you will see

how you can display a scan barcode dialog and process the result, how to get the device information like

operating system or model of the device and also how to obtain location information (latitude and

longitude).

function CommandExample() {

MobileCRM.UI.ViewController.createCommand(true,

["Scan Barcode", "Get Device Info", "Update Location"],

function (command) {

// Check if command with label name "scan barcode" was clicked

if (command == "Scan Barcode") {

ScanBarcode(command);

}

else if (command == "Get Device Info") {

GetDeviceInfo(command);

}

else if (command == "Update Location")

UpdateLocation();

});

function CheckVersion(command) {

///<param name="command" type="MobileCRM.Configuration"></param>

if (command.applicationVersion < "6.0")

return false;

else

return true;

}

function ScanBarcode(command) {

data.style.display = "none";

if (CheckVersion(command) == true) {

MobileCRM.Platform.scanBarCode(

function (res) {

// Input parameter to function is "array" of data

if (!res || res.length <= 0)

MobileCRM.bridge.alert("result doesn’t

contain any data");

else {

// simply display barcode on Html Page

var code = document.createElement("span");

code.appendChild(document.createTextNode(res[0]));

data.appendChild(code);

data.style.display = "block";

}

Page 33: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

},

function (err) {

alert("Scan barcode failed due to \n " + err);

},

null);

}

}

function GetDeviceInfo(command) {

MobileCRM.Configuration.requestObject(

function (config) { // Input paramter is object , with

configuration properties

var setting = config.settings;

var span = document.createElement("span");

span.appendChild(document.createTextNode("Absoulte URL :

" + setting.absoluteUrl));

span.appendChild(document.createElement("br"));

span.appendChild(document.createTextNode("Device Info : "

+ setting.deviceInfo));

span.appendChild(document.createElement("br"));

span.appendChild(document.createTextNode("Application

Edition : " + config.applicationEdition));

span.appendChild(document.createElement("br"));

if (config.isOnline == true)

span.appendChild(document.createTextNode("Is

Online : True"));

else

span.appendChild(document.createTextNode("Is

Online : False"));

span.appendChild(document.createElement("br"));

if (config.isBackgroundSync == true)

span.appendChild(document.createTextNode("Is

BackGroundSync : True"));

else

span.appendChild(document.createTextNode("Is

BackGroundSync : False"));

span.appendChild(document.createElement("br"));

data.appendChild(span);

data.style.display = "block";

},

function (err) {

alert("An error has occured \n " + err);

},

null);

}

function UpdateLocation() {

MobileCRM.Platform.getLocation(

// If device supports it, create object with prop. lat and long.

function (res) {

// This result is object with properties latitude and

longitude

if (!res || res.length <= 0)

alert("No info about location");

else {

Page 34: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

var latLong = document.createElement("p");

latLong.appendChild(document.createTextNode("Latitude : " + res.latitude));

latLong.appendChild(document.createElement("br"));

latLong.appendChild(document.createTextNode("Longitude : " +

res.longitude));

data.appendChild(latLong);

data.style.display = "block";

}

},

function (err) {

alert("Get Location INFO failed due to \n" + err);

},

null);

}

}

Page 35: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

Form Manager Interesting feature of Resco JavaScript Bridge is the ability to show standard (native) UI forms from

JavaScript. This is especially useful if the whole UI isn’t created in HTML, but going back and forth from

the HTML to standard UI is necessary. There are three Form Manager functions available for this - one

for opening new entity dialog (edit form for a new entity), another for entity detail dialog (contact

information form) and third for entity edit dialog.

In the following example we will create a simple HTML page for a Contact associated to an Account. It

will display just the Contact name and after clicking on one of items (name), standard Contact edit dialog

will appear. There is one more option for create new associated contact by opening entity new dialog

with predefined Account id. In our solution we don’t use auto reload when you save new created

associated contact. We just create button with function to reload page. We also use some basics

Javascript and DOM html functions to print result in page.

function OpenForm() { data.innerHTML = ""; var entity = new MobileCRM.FetchXml.Entity("account"); //entity.addAttributes(); var linkEntity = entity.addLink("contact", "parentcustomerid", "accountid", "inner"); linkEntity.addAttribute("contactid"); linkEntity.addAttribute("fullname"); entity.filter = new MobileCRM.FetchXml.Filter(); entity.filter.where("accountid", "eq", accountId); var fetch = new MobileCRM.FetchXml.Fetch(entity); fetch.execute( "Array", function (res) { if (res && res.length > 0) { for (var i in res) { var contact = res[i]; try { var a = document.createElement("a"); a.href = "#"; a.id = contact[0]; a.addEventListener("click",

function (e){ MobileCRM.UI.FormManager.showEditDialog("contact", e.target.id); reloadData(); },

false);

a.appendChild(document.createTextNode(contact[1])); data.appendChild(a);

data.appendChild(document.createElement("br")); } catch (err){ alert("Exception Error : \n\n" + err); } }

Page 36: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

} },function (err) {alert(err); }); } function addAssociatedContact() { // Create reference for associated account try{ var target = new MobileCRM.Reference("account", accountId); var relationShip = new MobileCRM.Relationship("parentcustomerid", target, null, null); MobileCRM.UI.FormManager.showNewDialog("contact", relationShip); // Show all associated contact at the begining; reloadData(); } catch (err) { alert("Exception : " + err); } } function reloadPage() { document.location.reload(true); } function reloadData() { OpenForm(); }

At first we use FetchXML to get data and in loop of getting result data we create DOM element object of

“a” and create event handler “onclick” with function to Open Edit Form for specify Account. Function

“reloadData” is using to fetch and display data.

a.addEventListener("click",function (e) { MobileCRM.UI.FormManager.showEditDialog("contact", e.target.id);

reloadData(); }, false);

Create new associated contact we use Mehod for FormManager object , showNewDialog.

We need to create reference for Specify account and create relationship object, which contains our

reference as target of relationship.

function addAssociatedContact() { // Create reference for associated account try{ var target = new MobileCRM.Reference("account", accountId);

Page 37: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

var relationShip = new MobileCRM.Relationship("parentcustomerid", target, null, null); MobileCRM.UI.FormManager.showNewDialog("contact", relationShip); // Show all associated contact at the begining; reloadData(); } catch (err) { alert("Exception : " + err); } }

Our Html looks like this:

<h3>Account : </h3> Name <p id="accountName"></p> <button id="reload" onclick="reloadPage()">Reload page</button><br /><br /> <button id="reload" onclick="reloadData()">Reload Data</button><br /><br /> <button id="createNew" onclick="addAssociatedContact()">New Contact</button><br /><br /> <div id="data"> </div>

Accessing configuration …

Platform-specific Services Platform

Barcode,…

Page 38: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction
Page 39: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

Metadata:

Metadata is by definition ‘data about data’ and MobileCRM.Metadata class allows access to the

entity metadata information. There you can find information like entity permissions, types of

entity properties, precisions, formats and more. In some situations – like when you are displaying

the data on a custom HTML page and would like to check if the entered data complies with those

conditions - this might come really handy. In the following example we will show you how to get

field’s maximum and minimum value and also - for a lookup or parent customer field - to get the

lookup targets.

We are using Metadata’s function “requestObject” in the code, to get the managed metadata

object. The mechanism is similar to other requestObject calls in Resco JavaScript Bridge, but in

the metadata case the object containing the metadata is then (once the success callback is called)

accessible also directly via MobileCRM.Metadata functions.

To access entity’s metadata from the callback you can call getEntity method and pass entity’s

logical name (all lowercase) as a parameter.

var metaEntity = MobileCRM.Metadata.getEntity("account");

The following example showcases a typical use of Metadata class in a custom HTML / JS

scenario. Metadata for Order entity is requested and for a couple of fields – like discount

percentage, total amount, name and a custom email field – meta properties are accessed.

These metaproperties store information about maximal length (name, email) or minimal and

maximal value, which are checked when the corresponding input elements on the HTML page

have changed. If the input doesn’t correspond with the metadata, an error message is displayed.

Example :

Javascript :

var checkId; function MetaDataCheck(id) {

checkId = id; MobileCRM.Metadata.requestObject(onMetadataLoaded, function (err) { alert(err); },

null); } function onMetadataLoaded() { var metaEntity = new MobileCRM.Metadata.getEntity("salesorder"); var discountPercentageProperty = metaEntity.getProperty("discountpercentage"); var emailProperty = metaEntity.getProperty("new_customeremailaddress"); var nameProperty = metaEntity.getProperty("name"); var totalamountProperty = metaEntity.getProperty("totalamount");

Page 40: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

switch (checkId) { case "name1": if((nameProperty.maximum && nameProperty.maximum !== undefined))

{ if (name1.value.toString().length > nameProperty.maximum) { alert("name contains more character > " + nameProperty.maximum); name1.style.border = "thick solid #FF0000" } else name1.style.border = ""; } break; case "email": if ((emailProperty.maximum && emailProperty.maximum !== undefined))

{ if (email.value.length > emailProperty.maximum) { alert("Email contains more characters > " + emailProperty.maximum); email.style.border = "thick solid #FF0000"; }

else email.style.border = ""; } break;

case "percentage": if ((discountPercentageProperty.maximum &&

discountPercentageProperty.maximum !== undefined)) {

if (percentage.value > discountPercentageProperty.maximum) { alert("Percentage > " + discountPercentageProperty.maximum); percentage.style.border = "thick solid #FF0000"; } else percentage.style.border = ""; } break; case "totalamount": if((!totalamountProperty.maximum || totalamountProperty.maximum === undefined) || (!totalamountProperty.minimum || totalamountProperty.minimum === undefined) )

{ totalamountProperty.maximum = 300; // set some value if not defined or exist totalamountProperty.minimum = 0; } if (totalAmount.value > totalamountProperty.maximum || totalAmount.value < totalamountProperty.minimum) { alert("Input Total Amount between " + totalamountProperty.minimum + " AND "+ totalamountProperty.maximum ); totalAmount.style.border = "thick solid #FF0000"; }

Page 41: RESCO JSBridge guide · JSBridge guide MobileCRM . Contents Introduction

else totalAmount.style.border = ""; break; } }

HTML :

<h2>Order Detail fields</h2> <div id="data">

Name : <input id="name1" type="text" onchange="MetaDataCheck(this.id)" /> <br /> Email Account : <input id="email" type="email" onchange="MetaDataCheck(this.id)" /><br /> Discount Percentage : <input id="percentage" type="number" onchange="MetaDataCheck(this.id)" /><br /> Totalamount : <input id="totalAmount" type="number" onchange="MetaDataCheck(this.id)" /><br /> </div>