Templates in the Microsoft Graph Toolkit
Most Microsoft Graph Toolkit components support the use of custom templates to modify the content of a component.
All web components support templates based on the <template>
element. For example, to override the template of a component, add a <template>
element inside a component.
<mgt-agenda>
<template data-type="event">
<div>{{event.subject}}</div>
<div data-for='attendee in event.attendees'>
<mgt-person person-query="{{attendee.emailAddress.name}}">
<template>
<div data-if="person.image">
<img src="{{person.image}}" />
</div>
<div data-else>
{{person.displayName}}
</div>
</template>
</mgt-person>
</div>
</template>
</mgt-agenda>
If you're using the Microsoft Graph Toolkit React components, you can use React for authoring templates. For details, see Use the toolkit with React.
Data-type
Each component can have multiple parts that can be templated. For example, in the mgt-agenda
component, you can template individual events, individual section headers, loading view, no data view, and more. To indicate the template, use the data-type
attribute on a template. For example, to template each event in the mgt-agenda
, use the event
data-type, as shown.
<mgt-agenda>
<template data-type="event"> </template>
</mgt-agenda>
If no data-type
is specified, the entire component will be replaced with the template. You can also use data-type="default"
for the same purpose.
Binding data
Many templates allow binding of data that is passed to the template as data context. For example, the event
template in the mgt-agenda
component passes an {event}
object that can be used directly in the template. To expand an expression, such as event.subject
, use the double curly brackets.
<template data-type="event">
<div>{{event.subject}}</div>
</template>
This format can also be used inside attributes:
<template data-type="event">
<a href="{{ event.onlineMeetingUrl }}" />
</template>
Note: You can also expand objects such as
{{event}}
or{{this}}
and they will render as JSON strings. This can be useful when you're developing the templates.
Change binding syntax
By default, to expand an expression, you use double curly brackets ( {{expression}}
). However, you can change this syntax for environments where the double curly bracket syntax is already used. For example, the following example uses double square brackets ( [[expression]]
).
import { TemplateHelper } from '@microsoft/mgt';
TemplateHelper.setBindingSyntax('[[', ']]');
Data context helper properties
The following properties can also be used with the data context object in your templates.
Property | Description |
---|---|
$index | Numerical index of item being rendered while being looped with data-for . |
$parent | If a template is rendered inside another template, this property allows you to access the parent data context. |
The following example shows how to use the $index
property in a data-for loop.
<mgt-person>
<mgt-person-card>
<template data-type="additional-details">
<span data-for="language in languages">
{{ language.displayName }}<span data-if="$index < languages.length - 1">, </span>
</span>
</template>
</mgt-person-card>
</mgt-person>
{{this}}
To help debug the data context, you can use this
in your binding expressions. The simplest form is to add {{this}}
anywhere in your template.
<template data-type="event">
<div>
{{this}}
</div>
</template>
Because you can use JavaScript in your binding expressions, you also have access to the console
object which allows you to use console.log(this)
(or any other console
API) in your templates.
<template data-type="event">
<div>
{{console.log(this)}}
</div>
</template>
Conditional rendering
You might only want to render elements when a condition is true or false based on the data context. The data-if
and data-else
attributes can evaluate an expression and render only if true or false.
<mgt-person person-query="john doe">
<template>
<div data-if="person.image">
<img src="{{ person.image }}" />
</div>
<div data-else>
{{ person.displayName }}
</div>
</template>
</mgt-person>
Looping
There will be cases where the data context object contains loop and you will need to loop over the data. For this scenario, use the data-for
attribute.
<template data-type="event">
<ul>
<li data-for='attendee in event.attendees'>
{{ attendee.displayName }}
</li>
</ul>
</template>
Template context
In scenarios where you need to convert data in your bindings, bind to events, or just use external data in your templates bindings, the templates support binding to external data context. You can add additional template context in two ways:
Directly on the component.
Each component defines the
templateContext
property, which you can use to pass additional data to any template in the component.document.querySelector('mgt-agenda').templateContext = { someObject: {}, formatDate: (date: Date) => { /* format date and return */ }, someEventHandler: (e) => { /* handleEvent */ } }
The properties in the
templateContext
object will now be available to be used in the binding expressions in the template.Globally for all components.
The
TemplateHelper
class exposes theglobalContext
object to add data or functions that should be globally available for all components.import { TemplateHelper } from '@microsoft/mgt'; TemplateHelper.globalContext.someObject = {}; TemplateHelper.globalContext.formatDate = (date: Date) => { /* format date and return */ }; TemplateHelper.globalContext.someEventHandler = (e) => { /* handleEvent */ }
Converters
In many cases, you might want to transform the data before presenting it in the template. For example, you might want to properly format a date before it is rendered. In these cases, you might want to use a template converter.
To use a template converter, you first need to define a function that will do the conversion. For example, you might define a function to convert an event object to a formatted time range.
document.querySelector('mgt-agenda').templateContext = {
getTimeRange: (event) => {
// TODO: format a string from the event object as you wish
// timeRange = ...
return timeRange;
}
}
To use the converter in your template, use it as if you would use a function in code behind.
<template data-type="event">
<div>{{ getTimeRange(event) }}</div>
</template>
Event or property binding
The data-props
attribute allows you to add an event listener or set a property value directly in your templates.
<template>
<button data-props="{{@click: myEvent, myProp: value}}"></button>
</template>
The data-props accepts a comma delimited string for each property or event handler you might want to set.
To add an event handler, prefix the name of the event with @
. The event handler will need to be available in the templateContext
of the element.
document.querySelector('mgt-agenda').templateContext = {
someEventHandler: (e, context, root) => { /* handleEvent */ }
}
<template>
<button data-props="{{@click: someEventHandler}}"></button>
</template>
The event args, data context, and the root element of the template are passed to the event handler as parameters.
Template rendered event
In certain cases, you might want to get a reference to the rendered element. This can be useful if you want to handle the rendering of the content yourself, or you want to modify the rendered element.
In this scenario, you can use the templateRendered
event, which fires after the template has been rendered.
let agenda = document.querySelector('mgt-agenda');
agenda.addEventListener('templateRendered', (e) => { });
The event details will contain a reference to the element that is being rendered, the data context object, and the type of the template.
agenda.addEventListener('templateRendered', (e) => {
let templateType = e.detail.templateType;
let dataContext = e.detail.context;
let element = e.detail.element;
if (templateType === 'event') {
element.querySelector('.some-button').addEventListener('click', () => {});
}
});
Styling
The templates can be styled normally via CSS as they are rendered outside of the shadow dom.