Creating Dynamics Integrated Content
Content pages are created in Externable database and are cached when published to achive maximum performance. If you use static content without using Dynamics/Dataverse releated data you can expect very low latency as pages are rendered from server cache.
However, Externable core feature is its integration with Dynamics/Dataverse. Each content page in Externable can be integrated with Dynamics both to read data from or to update Dynamics data through forms. So, your core page content is loaded from server cache, but additional data that will be presented in the page which comes from Dynamics or Data Export Service database, will be loaded on top of it.
API or Data Export Service?
One of the core decisions you need to make for each content page is if you use direct Dynamics API calls or Data Export Service (DES). Using DES whereever possible is the most recommended scenario as it is not affecting your Dynamics/Dataverse API limits and provides better performance. That however comes with limitation - DES is effectively a copy of Dynamics data, so it refreshes with a delay. So if you expect to load lots of data from Dynamics on your website, which can accept a delay (like knowlegdebase content) you should probably stick with DES. If you want to work with transactional data like cases, where user can add new cases - using Dynamics direct API calls is most likely best bet as you may want user to see the records that he/she has just created immidiately. As a general rule we want to avoid retrieving large data sets with API calls and we want to avoid direct calls wherever possible.
Dynamics API calls limits may change over time. Externable relies on Application Users, so you have allocation of pooled API calls per day for all Application Users. More details at Microsoft site.
Data Query
If you don't create any queries in Content node (page), page will be loaded from Externable cache only. If you create at least one, your Dynamics API or DES database will be queried before the page is loaded.
To create new Query you need to open existing or create the Content Node you want to link to Dynamics and create new Dynamics Query. Let's call the Content Node 'Your Colleagues':
Name the query 'ContactsQuery'. You can as a starting point insert basic FetchXML for querying Accounts. Other way to do it is to export Dynamics FetchXML query with Advanced Find tool in Dynamics/Dataverse.
Let's enhence the FetchXML to query only Collegues, so Contacts who are attached to the same Account as Member who accesses the data. Create a query in Advanced Find:
Download the FetchXML, open it and paste the content of the file to query window. Change filter to narrow down the query by passing currently logged in Member linked Dynamics guid (see example below). Save and publish.
Note that we have changed the export query to include MEMBER_DYNAMICS_CONTACT_ID parameter instead of hardcoded guid. Those parameters can be used to inject currently logged in member details to the query. Don't use 'Equals current user' in FetchXML, because this refers to the Dynamics user who queries the data not the Externable logged in Member. Externable connects to your Dynamics with the same user every time it accesses your Dynamics, so this condition wouldn't work in this case.
You would follow same steps if you used Data Export Service with a difference that you would contruct SQL query not FetchXML query.
Data Presentation
Your Query is ready, Externable will query your linked Dynamics instance with your FetchXML or DES database with your SQL query every time someone opens this content page. However to present the data to end users you need to apply it to the content layout. To do so, add Content like you normally do.
Select Rich Text Editor:
You can see that in the rich text content editor you have 3 buttons, which work with data retrieved from Dynamics:
Let's create a table which will list all colleagues with their telephone numbers in the form of a table.
- Create a table (we only select two rows - first will be the header and second will be set as repeating row automatically repeated for each record found):
- Name headers, position cursor in first column second row to insert Full Name of a contact:
- Click Repeating Row and select query you created in the previous step:
- You will notice that this row is highlighted as repeating now:
- Click 'Insert Dynamics Data' button to insert variable for contact Full Name:
- As you see, parameter for dynamically retrieved contact Full Name has been injected:
Note that inserted parameter starts with 'Repeating.' prefix. That is because you inserted it to repeating table row. If you inserted it outside of repeating row, it would start with 'Dynamics.' prefix. Repeating row is used to list all records retrieved for the query.
- Insert telephone1 field the same way as fullname and click 'Save and publish':
Go to the frontend and you will see new section in your website called Your Colleagues listing all Contacts which are attached to the same Account that currently logged in member is:
It is worth setting content node as member only, because our query relies on current member guid, which will be null when member is not logged in.
JavaScript manipulations of Dynamics content
As you can see above, inserting Dynamics data to Content Node happens with dynamic parameter in curly brackets {{...}}
. All Dynamics data defined in the query is exposed in the form of VUE.JS object, which you can manipulate in several ways.
Inline logic
You can insert Dynamics field as a minimum by adding field reference only:
{{ Repeating.createdon }}
However, this will add raw data streight to the page. Instead you may want to manipulate the data so that it presents exactly what you want it to. When it comes to date/time fields, you may do the following to format the date and time in GB format:
{{new Date(Repeating.createdon).toLocaleDateString("en-GB") + " " + new Date(Repeating.createdon).toLocaleTimeString("en-GB")}}
So as you see, you can use standard JavaScript expressions in curly brackets to enrich the bare data with logic.
You could also use simple 'if' conditions to show different data depending on other boolean fields. In the example below the string visible on the page will depend on value of ispriceoverriden field in each row:
{{ Repeating.ispriceoverriden ? 'Custom Price' : 'Standard Price' }}
There are more expression examples available. See more details on VUE.JS syntax
Custom scripts
Inline expressions in moustaches are great for simplicity. However, sometimes you need more complex logic and you need the full power of JavaScript. That is possible by using custom scripts.
Each Content Node allows you to add custom JavaScript functions:
Your custom function may use Dynamics data fetched by Externable. All Dynamics data is available to you in the object named app
.
app.Dynamics.ContactsQuery
- holds the value of the first row retrieved (especially useful if your query is expected to give single row only).
app.DynamicsList.ContactsQuery
holds an array of all records retrieved.
app.DynamicsCurrentUser
holds email, full name and username of Externable member who is currently logged in (received from the server).
Don't use it to authorize access of the user as it is local variable which user may override. Always rely on server authentication.
app.DynamicsLoaded
- use it to check if Dynamics data loading has been completed.
Let's use the example from the begining of this article. You can create custom function which will have access to all contacts retrieved by the custom ContactsQuery. Our simplistic function will iterate though them all and popup and alert with Full Name for each record from Dynamics.
function checkIfLoaded() {
if(typeof app === 'undefined')
window.setTimeout(checkIfLoaded, 100);
else if(app.DynamicsLoaded === false) {
window.setTimeout(checkIfLoaded, 100); /* this checks if Dynamics data is loaded every 100 milliseconds*/
}
else {
app.DynamicsList.ContactsQuery.forEach(function(row)
{
alert(row.fullname);
})
}
}
checkIfLoaded();
If you had more Dynamics Queries in the same Content Node, you would have them available in the same app.Dynamics/app.DynamicsList object, just using different query name as property.