microsoft / AdaptiveCards

A new way for developers to exchange card content in a common and consistent way.
https://adaptivecards.io
MIT License
1.75k stars 550 forks source link

🤖 Templating for JavaScript and .NET now available! #2448

Closed matthidinger closed 4 years ago

matthidinger commented 5 years ago

We're excited to announce the v1.0 release of Adaptive Card Templating for JavaScript and .NET, which enables the separation of data from layout in your Adaptive Cards.

Breaking changes as of May 2020

  1. The binding syntax changed from {...} to ${...}.
    • For Example: "text": "Hello {name}" becomes "text": "Hello ${name}"
  2. The JavaScript API no longer contains an EvaluationContext object. Simply pass your data to the expand function. Please see the SDK page for full details.
  3. The .NET API was redesigned to more closely match the JavaScript API. Please see the SDK page for full details.

Get started today!

Templating is a standalone library, which means you can start using it today with any Adaptive Card host, like Teams, Outlook, or your own apps.

How can templating help you?

Templating enables the separation of data from the layout in an Adaptive Card.

It helps design a card once, and then populate it with real data

Today it's impossible to create a card using the Adaptive Card Designer and use that JSON to populate the payload with dynamic content. In order to achieve this you must write custom code to build a JSON string, or use the Object Model SDKs to build an OM representing your card and serialize it to JSON. In either case the Designer is a one-time one-way operation and doesn't make it easy to tweak the card design later once you've converted it to code.

It makes transmissions over the wire smaller

Imagine a world where a template and data can be combined directly on the client. This means if you use the same template multiple times, or want to update it with new data, you just need to send new data to the device and it can re-use the same template over and over.

It helps you create a great looking card from just the data you provide

We think Adaptive Cards are great, but what if you didn't have to write an Adaptive Card for everything you want to display to a user? With a template service (described below) we can create a world where everyone can contribute, discover, and share templates over any type of data.

Share within your own projects, your organization, or with the entire internet.

AI and other services could improve user experiences

By separating data from content it opens a door for AI and other services to "reason" over the data in the cards we see and enhance user productivity or help us find things.

What is Adaptive Cards Templating?

It's comprised of 3 major components:

  1. The Template Language is the syntax used for authoring a template. The Designer even lets you preview your templates at design time by including "sample data".
  2. The Templating SDK's will exist on all supported Adaptive Card platforms. These SDKs allow you to populate a template with real data, on the back-end or directly on the client.
  3. The Template Service is a proof-of-concept service that allows anyone to find, contribute to, and share a set of well-known templates.

Template Language

The template language is the syntax used to author an Adaptive Card template.

Follow along with the example below by opening up a new tab to

https://adaptivecards.io/designer

Click the Preview Mode button to toggle between design-mode and preview-mode.

Designer screenshot

The newly updated Designer adds support for authoring templates and providing Sample Data to preview the card at design-time.

Paste the example below into the Card Payload Editor pane:

EmployeeCardTemplate.json

{
    "type": "AdaptiveCard",
    "version": "1.0",
    "body": [
        {
            "type": "ColumnSet",
            "style": "accent",
            "bleed": true,
            "columns": [
                {
                    "type": "Column",
                    "width": "auto",
                    "items": [
                        {
                            "type": "Image",
                            "url": "${photo}",
                            "altText": "Profile picture",
                            "size": "Small",
                            "style": "Person"
                        }
                    ]
                },
                {
                    "type": "Column",
                    "width": "stretch",
                    "items": [
                        {
                            "type": "TextBlock",
                            "text": "Hi ${name}!",
                            "size": "Medium"
                        },
                        {
                            "type": "TextBlock",
                            "text": "Here's a bit about your org...",
                            "spacing": "None"
                        }
                    ]
                }
            ]
        },
        {
            "type": "TextBlock",
            "text": "Your manager is: **${manager.name}**"
        },
        {
            "type": "TextBlock",
            "text": "Your peers are:"
        },
        {
            "type": "FactSet",
            "facts": [
                {
                    "$data": "${peers}",
                    "title": "${name}",
                    "value": "${title}"
                }
            ]
        }
    ]
}

Then paste the JSON data below into the Sample Data Editor.

Sample Data helps you see exactly how your card will appear at runtime when passed actual data.

EmployeeData

{
    "name": "Matt",
    "photo": "https://pbs.twimg.com/profile_images/3647943215/d7f12830b3c17a5a9e4afcc370e3a37e_400x400.jpeg",
    "manager": {
        "name": "Thomas",
        "title": "PM Lead"
    },
    "peers": [
        {
            "name": "Lei",
            "title": "Sr Program Manager"
        },
        {
            "name": "Andrew",
            "title": "Program Manager II"
        },
        {
            "name": "Mary Anne",
            "title": "Program Manager"
        }
    ]
}

Click the Preview Mode button. You should see the card render according to the sample data provided above. Feel free to make tweaks to the sample data and watch the card update in realtime.

Congratulations, you just authored your first Adaptive Card Template! Next let's learn how to populate the template with real data.

Learn more about the template language

SDK support

The Templating SDKs make it possible to populate a template with real-data.

NOTE: At this time templating is targeted primarily for "backend" platforms (.NET and NodeJS). Over time we will release templating SDKs for all remaining Adaptive Cards platform, like iOS, Android, UWP, etc.

Platform Package Install Documentation
JavaScript npm install npm install adaptivecards-templating Documentation
.NET Nuget install dotnet add package AdaptiveCards.Templating Documentation

JavaScript Example

The JavaScript below shows the general pattern that will be used to populate a template with data.

var template = new ACData.Template({ 
    // EmployeeCardTemplate goes here
});

var card = template.expand({
    $root: {
        // Your data goes here
    }
});
// Now you have an AdaptiveCard ready to render!

Learn more about the templating SDKs

Template Service

The Adaptive Cards Template Service is a proof-of-concept service that allows anyone to find, contribute to, and share a set of well-known templates.

It's useful if you want to display some data but don't want to bother writing a custom Adaptive Card for it.

The API to get a template is straight-forward enough, but the service actually offers much more, including the ability to analyze your data and find a template that might work for you.

HTTP GET https://templates.adaptivecards.io/graph.microsoft.com/Profile.json

All templates are flat JSON files stored in a GitHub repo so anyone can contribute to them like any other open source code.

Learn more about the card template Service

What's next and sending feedback

Templating and the separation of presentation from data takes us a whole lot closer toward our mission: "an ecosystem standardized content exchange between apps and services". We've got plenty to deliver in this area, so stay tuned and let us know how it's working for you on GitHub!

v-kydela commented 5 years ago

@matthidinger - This is all very excellent! I'm on the Bot Framework support team and I use Adaptive Cards frequently and I'm always excited to hear new things about Adaptive Cards.

Since you asked, I do have some feedback. It seems to me like there are two ways to implement these features and I'm not sure what path you're taking.

  1. This could be a new version of the Adaptive Cards schema which would mean all this new syntax would be included in the payload being sent to whatever channel is meant to render the card.
  2. This could just be a new version of the Adaptive Cards packages, meaning a card would be converted from this new syntax into the old syntax before being sent to the channel.

Since this is being called Adaptive Cards "2.0" I'm a little confused. The name 2.0 implies that it's a new schema like Adaptive Cards 1.1. But it's in quotes, which might imply that you don't mean that.

It seems to me like this whole data binding feature could very easily be implemented using option 2, and my feedback is that it should be. That's not to say I don't think there should be any new versions of the schema. It's just that from my point of view, if you can implement a feature without requiring a new schema then it's very logical to do so.

Channels have been slow to implement support of Adaptive Cards 1.1, so we might expect the same regarding Adaptive Cards 2.0. If we can implement this new feature such that we're not counting on channels to implement support of a new Adaptive Cards schema, we'll get all the benefits of the new feature while still being able to reach as many channels as possible. The way to do this is of course to have cards containing the data binding syntax get converted into "classic" Adaptive Cards syntax on the bot side, before the card is sent to the channel. That way the channel wouldn't need to be responsible for interpreting the syntax.

andrewleader commented 5 years ago

@v-kydela that definitely could be possible! From the Bot Framework side, you could parse the new template/data card using the new Adaptive Card library, and then call a "populate" method to populate the data into the template, generating a fully self-contained Adaptive Card, which you then can send to all the channels.

Our current thoughts is that the templating/data binding will happen BEFORE rendering, so you can apply the templating ahead of time. Authors could even choose to apply the templating before they even send Bot Framework the card.

Good to know that you might use this feature in that way! Thanks!

AndreMantas commented 5 years ago

When will we be able to install a pre-release via nugget? We are waiting for this feature for so long :)

andrewleader commented 5 years ago

Hey @AndreMantas, you're looking for a .NET version? We have a JavaScript version on NPM: https://www.npmjs.com/package/adaptivecards-templating

Let us know if you want the .NET version on NuGet and we probably could get around to packaging up the current alpha! Also let us know what version of .NET you need it on (.NET standard 1.3, 2.0, or .NET PCL, etc)

AndreMantas commented 5 years ago

Hi. Yes I'm looking for a .NET Core version. Currently when creating new bots via the microsoft template I believe projects are set to .NET Core 2.1, but I usually switch to 2.2.

andrewleader commented 5 years ago

@AndreMantas we've got a PR in-progress (#3239) for a .NET Standard 2.0 library, you could compile from that source, or here's a pre-compiled NuGet from OneDrive that you could copy locally and consume until we get it merged and published to NuGet.org!

See the readme for a quick explanation on how to use the .NET library!

rvinothrajendran commented 5 years ago

@matthidinger,@andrewleader where I can download the new AdaptiveCard.FromJson API supporting package for C#

matthidinger commented 5 years ago

@paulcam206 is working on this as we speak, hopefully not much longer

matthidinger commented 5 years ago

@rvinothrajendran please see the package here: https://www.nuget.org/packages/AdaptiveCards.Templating

Note that the API has changed since this was posted.

var transformer = new AdaptiveTransformer();
var cardJson = transformer.Transform(templateJson, dataJson);

Please give it a shot and let us know how it works for you! Also, I will be updating the main issue, but official templating docs have been posted here: https://docs.microsoft.com/en-us/adaptive-cards/templating/

rvinothrajendran commented 5 years ago

@matthidinger transformer.Transform() API is working fine. Waiting for AdaptiveCards.FromJson API in C# :)

ThomasPe commented 5 years ago

seems to be working fine for me as well. the binding does not seem to work when the property name has a number in it, so Location1 & Location2 failed for me while LocationX & LocationY work fine. Not sure if that's a known issue.

matthidinger commented 5 years ago

Hi @ThomasPe, that's an interesting topic we're evaluating right now: type coercion. Hopefully we'll have that up for review soon, but if you were able to share more about your template payload, data, and your opinion on the most reasonable expected behavior it would be great information for us as we evaluate proposals.

zjrunner commented 4 years ago

@matthidinger , what is the relation to type coercion? I hit the same thing @ThomasPe saw. Adding some examples to make sure we're talking about the same thing.

This works fine:

var output = transformer.Transform("{\"hi\":\"{my.data}\"}", "{\"my\":{\"data\":\"foo\"}}");

This doesn't find the match because of the number, and leaves the {my.dat1a} in the transform result:

var output = transformer.Transform("{\"hi\":\"{my.dat1a}\"}", "{\"my\":{\"dat1a\":\"foo\"}}");

I was having trouble finding the C# code in GitHub when I saw the linked PR is still pending merge - and it is pretty lightweight and mostly runs the nodejs code which is in master. I'm rusty on JS but I think we're talking about the definition of identifier in the parse code which may be overly limiting not allowing digits:

{ tokenType: "identifier", regEx: /^[$a-z_]+/i },
v-kydela commented 4 years ago

Yeah that looks like bad RegEx to detect an identifier. But why does it even need RegEx to detect an identifier? Whatever is after the dot is automatically an identifier, right?

ghost commented 4 years ago

This issue has been automatically marked as stale because it has not had any activity for 5 days.

jbeek11 commented 4 years ago

LS, any update on when this might be moving from preview to general availability? Best regards, Joris.

matthidinger commented 4 years ago

Hi @jbeek11, we have no dates yet but we're making progress :) Is there anything preventing you from using the preview that we can help with?

jbeek11 commented 4 years ago

Hi Matt,

thank your for responding! Nothing preventing us from using the available preview.

But a few days ago I was 'overruled' by our front end programmers anyway. Current Adaptive Card setup doesn't allow enough control to get the full corporate layout and UX we're going for. So we are now looking at implementing our own custom attachment that does provide that, including the data binding that we still definitely need.

Thanks again and all the best!

Regards, Joris.

Op ma 23 dec. 2019 om 21:16 schreef Matt Hidinger <notifications@github.com

:

Hi @jbeek11 https://github.com/jbeek11, we have no dates yet but we're making progress :) Is there anything preventing you from using the preview that we can help with?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/microsoft/AdaptiveCards/issues/2448?email_source=notifications&email_token=ACUJUO73DU6TJAFNRVVDRLLQ2EMDPA5CNFSM4GZCZCCKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEHR3O6Y#issuecomment-568571771, or unsubscribe https://github.com/notifications/unsubscribe-auth/ACUJUO2X5IFO5NTZLV4UN4DQ2EMDPANCNFSM4GZCZCCA .

amitvig22 commented 4 years ago

@matthidinger transformer.Transform() API is working fine. Waiting for AdaptiveCards.FromJson API in C# :)

If there is an update on this please let us know :) we would like to move to an official nuget for the FromJson calls as well.

grahamehorner commented 4 years ago

@jbeek11 can you share information with regards your issue around the layering that you UX/frontend developer has/had; as I'd like to avoid any possible issue with our UX/frontend developers prior to going to far into adaptive card service developments. thanks in advance of any information you provide

jbeek11 commented 4 years ago

@jbeek11 can you share information with regards your issue around the layering that you UX/frontend developer has

I've not mentioned an issue with 'layering', not sure what you mean by that. Our problem is that, when using Adaptive Cards in its current setup there is not full control over layout and UX in the frontend. E.g. you are limited to certain letter sizes, which you have to 'translate' into what you would like in the frontend. This gives only very limited flexibility as far as layout and UX is concerned. For us this was not the way to go, especially since we are only beginning and still have the option to go a different, more suitable route.

v-kydela commented 4 years ago

@jbeek11 - The Adaptive Card schema defines 5 different font sizes which each Adaptive Card renderer is able to define actual sizes for. Are you saying you need your cards to have more than 5 different font sizes?

rbanate commented 4 years ago

@matthidinger - I noticed that data is not being rendered on azure healthbot web chat too though I can see it working properly via the designer https://adaptivecards.io/designer/

NxP4Code commented 4 years ago

It is possible to have dynamic number of rows? e.g. in the Expense report template is fixed for 3 rows, is it possible to have one template row and expand it based on input array of rows?

matthidinger commented 4 years ago

Hi @nxp4code, does this answer your question? https://docs.microsoft.com/en-us/adaptive-cards/templating/language#repeating-items-in-an-array

NxP4Code commented 4 years ago

@matthidinger , It kind of helps only when you know exact rows before building template. E.g. https://adaptivecards.io/samples/ExpenseReport.html , in here you need to know before creating template there are 3 rows in dataset, If I am planning to store static template file and than generate complete json based on incoming data.

In real-life its difficult to fix number of rows in template, lets say I am sending expense report card in my chat bot, all expense report will have different number of rows. So fixing 3 rows in static templating file and using data array of 3 rows is not going to be helpful. I am trying to find a way to have template to define row 1 row (1 json block in template )and expand that 1 row based on data array size. If I get 5 rows I will see template repeated 5 times.

Am I missing something big here??

matthidinger commented 4 years ago

@NxP4Code that should work no problem, just set the "$data" property to an array and that adaptive card element will be repeated for each object in the array. If you can send your example card and data format with private information removed I can try to help more specifically

NxP4Code commented 4 years ago

@matthidinger , Thanks for your offer and support. I will try to play with it, I got the core data ready working on creating template. If I have any challenge I will post it here.

@NxP4Code that should work no problem, just set the "$data" property to an array and that adaptive card element will be repeated for each object in the array. If you can send your example card and data format with private information removed I can try to help more specifically

NxP4Code commented 4 years ago

@matthidinger

Is it possible to update the expense report template to use templating and data binding at appropriate places in a designer? It seems the Data context is not set and editing sample data doesn't change anything on the card. It seems the card is pretty much static.

image

dclaux commented 4 years ago

@NxP4Code an update to the designer is coming - no ETA, but we're getting there. You can see a preview of it at https://vnext.adaptivecards.io/designer

NxP4Code commented 4 years ago

I got this working in the current version. what is are the enhancements in the new version?

@NxP4Code an update to the designer is coming - no ETA, but we're getting there. You can see a preview of it at https://vnext.adaptivecards.io/designer

The issue was the data array needs to be explicitly called with root context. e.g. {$root.arrayofObjectorArray} adaptivecard

Also, I noticed that the data structure panel was not getting updated with sample data. But that is not a major issue.

dclaux commented 4 years ago

The new designer has an updated sample picker and enhanced data binding support. The Data Structure pane is also gone.

If you're successful with the current designer no need to look into the new one for now.

shalinijoshi19 commented 4 years ago

@matthidinger reminder to update + close this issue out post RTM push today!

ghost commented 4 years ago

:tada:AdaptiveCards@2020.06 has been released which fixes this issue.:tada:

Handy links:

mohitborse commented 2 years ago

Hi Team,

I'm trying to integrate the Adaptive card designer with the Angular application. I make a one-live solution for it but in that, I'm facing one problem I'm not able to load monaco editor in that.

So anyone can provide a real way to integrate the Adaptive cards with the Angular App? https://stackblitz.com/edit/angular-ivy-mfde8r?file=src/app/app.component.ts

harikaphanikodali commented 11 months ago

Hi Team,

Does adaptive cards support Javascript string methods? eg: "url": "${member.photoUrl.replace('.blob.', '.z20.web.')}",