-
Notifications
You must be signed in to change notification settings - Fork 5
Lab 2 ‐ Advanced Card View Functionality
- Go to your home site and create a new list named "Instruction List".
- Add a "Single line of text" column named "Description".

- Create a few items:
- Title = Step 1, Description = Use ACEs
- Title = Step 2, Description = ???
- Title = Step 3, Description = SPFx 🚀 🌝
- Get the list id


Save this list id for the next step.
Start with the HelloWorld ACE from Lab 1. Make the following updates in preparation for Step 2.
Navigate to
src/adaptiveCardExtensions/helloWorld/HelloWorldAdaptiveCardExtension.ts.
Update the
propertiesinterface.
export interface IHelloWorldAdaptiveCardExtensionProps {
title: string;
description: string;
iconProperty: string;
listId: string;
}Navigate to
src/adaptiveCardExtensions/helloWorld/HelloWorldAdaptiveCardExtension.manifest.json.
Initialize the ACE with the id of the List created in the previous step.
"preconfiguredEntries": [{
// ...
"properties": {
"title": "HelloWorld",
"description": "HelloWorld description",
"iconProperty": "", // Default to sharepointlogo
"listId": ""
}
}]Navigate to
src/adaptiveCardExtensions/helloWorld/HelloWorldPropertyPane.ts.
Update the Property Pane.
PropertyPaneTextField('listId', {
label: 'List ID'
})Navigate to
src/adaptiveCardExtensions/helloWorld/HelloWorldAdaptiveCardExtension.ts.
Add a new interface for the List data.
export interface IListItem {
title: string;
description: string;
}Update the
stateinterface.
export interface IHelloWorldAdaptiveCardExtensionState {
currentIndex: number;
items: IListItem[];
}Update the
stateinitialization.
public onInit(): Promise<void> {
this.state = {
currentIndex: 0,
items: []
};
// ...
}Temporarily remove where
stateis referenced in the AdaptiveCardExtension and Views.
// tslint:disable-next-line: no-any
protected onPropertyPaneFieldChanged(propertyPath: string, oldValue: any, newValue: any): void {
}Navigate to
src/adaptiveCardExtensions/helloWorld/quickView/QuickView.ts.
public get data(): IQuickViewData {
return {
subTitle: '',
title: strings.Title
};
}
public onAction(action: IActionArguments): void {
}Navigate to
package.json.
Add
@microsoft/sp-httpdependency.
"dependencies": {
// ...
"@microsoft/sp-http": "1.13.0-beta.N" // N = the current version of the beta packages installed
},npm installNavigate to
src/adaptiveCardExtensions/helloWorld/HelloWorldAdaptiveCardExtension.ts.
Fetch the list data using SPHttpClient.
import { SPHttpClient } from '@microsoft/sp-http'; private _fetchData(): Promise<void> {
if (this.properties.listId) {
return this.context.spHttpClient.get(
`${this.context.pageContext.web.absoluteUrl}` +
`/_api/web/lists/GetById(id='${this.properties.listId}')/items`,
SPHttpClient.configurations.v1
)
.then((response) => response.json())
.then((jsonResponse) => jsonResponse.value.map(
(item) => { return { title: item.Title, description: item.Description }; })
)
.then((items) => this.setState({ items }));
}
return Promise.resolve();
}Fetch the list data during initialization.
public onInit(): Promise<void> {
// ...
return this._fetchData();
}Fetch the list data when the Property Pane is updated.
Only update the ACE, when the List ID has changed.
protected onPropertyPaneFieldChanged(propertyPath: string, oldValue: any, newValue: any): void {
if (propertyPath === 'listId' && newValue !== oldValue) {
if (newValue) {
this._fetchData();
} else {
this.setState({ items: [] });
}
}
}Navigate to
src/adaptiveCardExtensions/helloWorld/cardView/CardView.ts.
Update the
datagetter to display data from the list.
public get data(): IPrimaryTextCardParameters {
const { title, description } = this.state.items[this.state.currentIndex];
return {
description,
primaryText: title
};
}Build and launch the ACE in the hosted Workbench.
gulp serve -l --nobrowserOnce the code is being served, navigate to the hosted Workbench.
https://{tenant}.sharepoint.com/_layouts/15/workbench.aspx
Open the Toolbox and select your ACE.

By default, Views are automatically responsive to the Card size. However, ACEs can optionally provide different Views for any given Card size.
Change the HelloWorld ACE to display the total count of List items in the Medium Card size,
and display the List items in the Large Card size to maximize the use of available space.
Create a new file under the
src/adaptiveCardExtensions/helloWorld/cardViewfolder namedMediumCardView.ts.
import {
BaseBasicCardView,
IActionArguments,
IBasicCardParameters,
ICardButton
} from '@microsoft/sp-adaptive-card-extension-base';
import {
IListItem, QUICK_VIEW_REGISTRY_ID,
IHelloWorldAdaptiveCardExtensionProps,
IHelloWorldAdaptiveCardExtensionState
} from '../HelloWorldAdaptiveCardExtension';
// Extend from BaseBasicCardView
export class MediumCardView extends BaseBasicCardView<IHelloWorldAdaptiveCardExtensionProps, IHelloWorldAdaptiveCardExtensionState> {
// Use the Card button to open the Quick View
public get cardButtons(): [ICardButton] {
return [
{
title: 'View All',
action: {
type: 'QuickView',
parameters: {
view: QUICK_VIEW_REGISTRY_ID
}
}
}
];
}
// Display the total number of steps
public get data(): IBasicCardParameters {
return {
primaryText: `${this.state.items.length} Steps`
};
}
}Navigate to
src/adaptiveCardExtensions/helloWorld/HelloWorldAdaptiveCardExtension.ts.
Register the new View.
import { MediumCardView } from './cardView/MediumCardView';const MEDIUM_VIEW_REGISTRY_ID: string = 'HelloWorld_MEDIUM_VIEW';public onInit(): Promise<void> {
// ...
this.cardNavigator.register(CARD_VIEW_REGISTRY_ID, () => new CardView());
this.cardNavigator.register(MEDIUM_VIEW_REGISTRY_ID, () => new MediumCardView());
// ...
}Return either the Medium Card View or the Large Card View based on the Card size.
protected renderCard(): string | undefined {
return this.cardSize === 'Medium' ? MEDIUM_VIEW_REGISTRY_ID : CARD_VIEW_REGISTRY_ID;
}Reload the Workbench.

IMPORTANT NOTE: There is a known bug where the
renderCardmethod is not being called again when the Card size is changed. We will fix this. The workaround is to change the Card size and refresh.
Change the Card size to Large. Refresh.

ACE Card views can be interacted with. The buttons could invoke REST APIs or be used to interact with the Card. Change the Large Card view to iterate through the List items.
Navigate to
src/adaptiveCardExtensions/helloWorld/cardView/CardView.ts.
The buttons on the Card view can be dynamic based on the current state of the ACE.
public get cardButtons(): [ICardButton] | [ICardButton, ICardButton] {
const buttons: ICardButton[] = [];
// Hide the Previous button if at Step 1
if (this.state.currentIndex > 0) {
buttons.push({
title: 'Previous',
action: {
type: 'Submit',
parameters: {
id: 'previous',
op: -1 // Decrement the index
}
}
});
}
// Hide the Next button if at the end
if (this.state.currentIndex < this.state.items.length - 1) {
buttons.push({
title: 'Next',
action: {
type: 'Submit',
parameters: {
id: 'next',
op: 1 // Increment the index
}
}
});
}
return buttons as [ICardButton] | [ICardButton, ICardButton];
}Update the state when a button is clicked.
public onAction(action: IActionArguments): void {
if (action.type === 'Submit') {
const { id, op } = action.data;
switch (id) {
case 'previous':
case 'next':
this.setState({ currentIndex: this.state.currentIndex + op });
break;
}
}
}Reload the Workbench.



After this lab you should be familiar with:
- Changing the default
propertiesof an ACE - Changing the ACE
properties/stateinterfaces - Creating and registering Card views
- Conditionally rendering Card view elements
- Advanced Card view manipulation