-
Notifications
You must be signed in to change notification settings - Fork 1
Axios Template Reference
Using this solution is straightforward: simply include the index.html web resource on your entity form as you would a standard CRM HTML web resource. Once the web resource loads, we can proceed with manipulating the entity form, making WebAPI calls, etc.
Setting up your various templates involves a few steps. This section will provide explanations and samples for each of the components.
Samples of each are included in the Samples folder.
NOTE: Please let us know if you have any issues with the information and examples provided. As we refine the script, receive feedback (bugs!), and work on additional script models, we will be updating these and other pages. All feedback is welcome and extremely helpful!
The index.html web resource can be very simple. Here is an example of an entire index.html file:
<html>
<head>
<meta charset="utf-8" />
<script src="../../ClientGlobalContext.js.aspx" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.16.2/axios.min.js"></script>
<script src="Xrm.js"></script>
<script src="app.js"></script>
</head>
<body>
</body>
</html>
This minimal bit of HTML is all we need to load the Axios library, our generated Xrm.js, and our app.js. In our situation, we are using relative "virtual" folder paths in our web resource naming. For example, "new_/entity/Xrm.js". This means that our reference to Xrm.js is in the same "folder" as index.html but we need to provide relative paths for the Client Context script that provides the Xrm.Page context.
Once complete, simply add the index.html web resource to the correct CRM form. If this solution does not provide a user interface, simply set Visible on the web resource false.
You can download a copy of the index.html sample here: index.html
Your Xrm.ts will generate an Xrm.js via the TypeScript compiler, but we will discuss the TypeScript Xrm.ts. This file is a bit more complex. The Xrm.ts provides base TypeScript interfaces, classes, and methods that primarily focus on WebAPI calls. All interfaces, classes, etc are contained within a common TypeScript module definition. For example, using the Account system entity, our module would be named MCS.Account.
The interfaces outline the expected classes and their functional definition in TypeScript. Our initial template includes the following predefined interfaces:
Outlines methods for entity objects passed to the WebAPI calls. Each entity referenced in your script will implement this interface.
export interface IRetrieveMultipleData<T> {
'@odata.context': string,
value: T[]
}
Outlines methods for the standard WebAPI methods
export interface IWebApi {
retrieve<T>(e: Entity, params?: IParams, formattedValues?: boolean): Axios.IPromise<T>;
retrieveNext<T>(e: Entity, nextLinkUrl: string, formattedValues?: boolean): Axios.IPromise<T>;
create<T>(e: Entity, formattedValues?: boolean, returnRecord?: boolean): Axios.IPromise<T>;
retrieveMultiple<T>(e: Entity, params?: IParams, formattedValues?: boolean, returnRecord?: boolean): Axios.IPromise<T>;
update<T>(e: Entity, route: string, id: string): Axios.IPromise<T>;
remove<T>(e: Entity): Axios.IPromise<T>;
fetch<T>(e: Entity, fetchXml: string, formattedValues?: boolean): Axios.IPromise<T>;
getConfig(formattedValues?: boolean, returnRecord?: boolean): any;
}
Helper interface for passing parameters to the WebAPI calls.
export interface IParams {
$select?: string;
$filter?: string;
$orderby?: string;
$top?: string;
$expand?: string;
[key: string]: string;
}
Various helper methods used within the app.ts
export interface IUtils {
formatDate(dateVal: string): string;
getFormattedValue(entity:any, attribute:string): string;
isNullUndefinedEmpty(value: any): boolean;
padLeadingZeros(num: number, precision: number): string;
cleanGuid(guid: string, removeDashes?: boolean): string;
reopenForm(entityName: string, entityId: string): void;
MarkAllFieldReadOnly(): void;
}
Helper method for the attribute name vs escaped name required by the WebAPI call
export interface IAttribName {
name: string,
api_name:string
}
Each of these interfaces are implemented in the same Xrm.ts and are available to your app.ts.
An additional class abstract class Entity is included that each generated CRM entity will extend.
export abstract class Entity {
constructor(public route: string, public id?: string) { }
}
Each entity selected for generation will include specific components. Using Account as an example:
This interface ensures that we have the proper format for the WebAPI retrieve calls
export interface IAccounts extends IRetrieveMultipleData<IAccount> {}
This interface defines what will be passed to the WebAPI service calls and what will be returned as the result.
export interface IAccount {
[key: string]: string | number
preferredcontactmethodcodename?: number
emailaddress3?: string
...
The reason for two values is that the WebAPI calls expect Lookups to be formatted differently. For example, below you see slaid and _slaid_value. This class can be used for intellisense within your script, such as with an Xrm.Page.getAttrite() call.
export class AccountAttributes {
slaid: IAttribName = { name:"slaid", api_name:"_slaid_value" }
emailaddress3: IAttribName = { name:"emailaddress3", api_name:"emailaddress3" }
...
This class will contain the data that is passed to the WebAPI methods for create, updated, etc. It contains a constructor that initializes all relevant values.
export class Account extends Entity {
public address1_upszone:string;
public address2_addressid:string;
...
constructor(initData?: IAccount) {
super("accounts");
if (initData == undefined) { return; }
this.id = initData.accountid;
...
You can download the Account example here: Xrm.ts Note that the sample includes definitions for the Task entity as well. We will use them later!
The app.ts will compile to app.js, just as the Xrm.ts/Xrm.js. This contains the business logic that you would like to apply on the entity form. Continuing with the Account example, we might want to perform some simple operations on the CRM form like pre populating an attribute value or provide a complex user interface, such as aggregating data from related records.
The app.ts first requires setting up the containing module. To keep things simple, our module declaration will match that of the Xrm.ts. Continuing the Account example, we have:
module MCS.Account
We next need to create a class that will wrap the work on the Account entity that will be instantiated by the Angular engine. As a really simple example, we can create a class named account_entity. This class will provide a constructor that Angular will invoke that provides objects we will need to do our work. We can also set up some public fields on this class to make our work easier. And in the constructor, we can perform some simple form manipulation. With our Account, we have
export class account_entity
{
public entityName: string; // grab the entity name from Xrm.Page
public entityId: string; // grab the entity Id from Xrm.Page
public crmUrl: string; // grab Xrm.Page.context.getClientUrl()
public $webapi: IWebApi; // A generated service for doing webapi calls against CRM
public utils: Utils; // set up reusable utils object
constructor(public $scope?: any, public $http?: ng.IHttpService, public $interval?: ng.IIntervalService) {
// disable the Name field
var attribs: MCS.Account.AccountAttributes = new MCS.Account.AccountAttributes();
parent.Xrm.Page.getControl(attribs.name.name).setDisabled(false);
}
...
}
Note that because this is within the context of index.html, an inline web resource, we need to call parent.Xrm.Page in order to gain access to the form scripting API.
The constructor arguments will depend on what you require from Angular. For example, we include public $interval?: ng.IIntervalService that allows us to perform timed work using the built in Angular tools like $interval().
The next step requires initialization of the angular app and controller, as with standard Angular scripts. Now that we have the constructor on our account_entity object, we know how to request initialization from Angular. Our setup of the Account example looks like: var app = angular.module('app', []); app.controller('mainCtrl', ['$scope', '$http', '$interval', account_entity]);
When our index.html loads, our app.js will include the above lines which will kick off creation of an instance of our account_entity object. Within our constructor, we set up some variables and perform any other initialization, such as adding event handlers, setting attribute values, toggling control visibility, etc.
If you need to perform additional work, such as calling WebAPI methods, you can make those calls from within the constructor in response to an event. For example, if you wanted to retrieve a list of Tasks related to the Account, you could make a WebAPI call on load using the existing scripts. Our generated Xrm.ts includes entities classes and interfaces for Task, so we can make the call for Tasks related to the Account using the following.
var acct: account_entity = this.getAccount();
var t: TaskAttributes = new TaskAttributes();
var filterString = t.regardingobjectid.api_name + " eq " + this.utils.cleanGuid(acct.entityId) + " and statecode eq 1";
var params: IParams = {
$select: t.subject.api_name,
$filter: filterString
};
acct.$webapi.retrieveMultiple<ITasks>(new Task(), params)
.then((respItems) => {
var vals: any = respItems.data.value;
for (let val of vals) {
var task: ITask = val;
// do your work here!
parent.Xrm.Utility.alertDialog(task.subject, null);
}
});
This might be kind of an annoying example with all of the alerts, but it should demonstrate how to complete a simple WebAPI retrieve using the scripts provided in the template.
You can download a copy of the app.js sample here: app.js