Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.5k views
in Technique[技术] by (71.8m points)

azure - Can we add text field dynamically

I've created an adaptive card(using json) in my chatbot that takes input from users. I want to add a button that enables the user to add a new text field every time the user clicks on the insert field. (i.e., the user can click on insert button to enter details of education (school, college etc.))

Can this be achieved in adaptive cards?

I also wanted to know, can adaptive cards be designed in any other language (excluding json)

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The easiest way to do this is with Action.ShowCard:

{
  "type": "AdaptiveCard",
  "body": [
    {
      "type": "Input.Text",
      "placeholder": "Placeholder 1",
      "id": "text1"
    }
  ],
  "actions": [
    {
      "type": "Action.ShowCard",
      "title": "Add field",
      "card": {
        "type": "AdaptiveCard",
        "body": [
          {
            "type": "Input.Text",
            "placeholder": "Placeholder 2",
            "id": "text2"
          }
        ],
        "actions": [
          {
            "type": "Action.ShowCard",
            "title": "Add field",
            "card": {
              "type": "AdaptiveCard",
              "body": [
                {
                  "type": "Input.Text",
                  "placeholder": "Placeholder 3",
                  "id": "text3"
                }
              ],
              "actions": [
                {
                  "type": "Action.ShowCard",
                  "title": "Add field",
                  "card": {
                    "type": "AdaptiveCard",
                    "body": [
                      {
                        "type": "Input.Text",
                        "placeholder": "Placeholder 4",
                        "id": "text4"
                      }
                    ],
                    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json"
                  }
                }
              ],
              "$schema": "http://adaptivecards.io/schemas/adaptive-card.json"
            }
          }
        ],
        "$schema": "http://adaptivecards.io/schemas/adaptive-card.json"
      }
    }
  ],
  "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
  "version": "1.0"
}

Nested cards

You may not like the way that looks, but there is an alternative. Microsoft Teams allows you to update messages, so you can update the card with more input fields in response to a submit action. First, you'll need a way of saving state for your card so you can update the card's activity. In C# you can declare a state property accessor like this:

public IStatePropertyAccessor<Dictionary<string, (string ActivityId, int InputCount)>> InputCardStateAccessor { get; internal set; }

Then you can instantiate it like this:

InputCardStateAccessor = _conversationState.CreateProperty<Dictionary<string, (string, int)>>("cardState");

In Node.js you won't need to declare anything but you can instantiate it like this:

this.inputCardState = this.conversationState.createProperty('cardState');

You'll want a consistent way to generate your card that you can use when you send the card initially and when you update the card. I'm using the AdaptiveCards NuGet package in C#:

public static IActivity GenerateAdaptiveCardActivityWithInputs(int inputCount, object valueObject)
{
    var cardData = JObject.FromObject(valueObject);
    var cardId = Convert.ToString(cardData[KEYCARDID]);

    var card = new AdaptiveCard(new AdaptiveSchemaVersion(1, 0))
    {
        Body = Enumerable.Range(0, inputCount).Select(i =>
        {
            var inputId = $"text{i}";

            return new AdaptiveTextInput
            {
                Id = inputId,
                Value = Convert.ToString(cardData[inputId]),
            };
        }).ToList<AdaptiveElement>(),
        Actions = new List<AdaptiveAction>
        {
            new AdaptiveSubmitAction
            {
                Title = "Add field",
                Data = new Dictionary<string, string>
                {
                    { KEYCARDID, cardId },
                    { KEYSUBMITACTIONID, ACTIONSUBMITADDFIELD },
                },
            },
            new AdaptiveSubmitAction
            {
                Title = "Submit",
            },
        },
    };

    return MessageFactory.Attachment(new Attachment(AdaptiveCard.ContentType, content: JObject.FromObject(card)));
}

Node.js:

generateAdaptiveCardActivityWithInputs(inputCount, cardData) {
    var cardId = cardData[KEYCARDID];
    var body = [];

    for (let i = 0; i < inputCount; i++) {
        var inputId = `text${i}`;
        body.push({
            type: "Input.Text",
            id: inputId,
            value: cardData[inputId]
        });
    }

    var card = {
        type: "AdaptiveCard",
        $schema: "http://adaptivecards.io/schemas/adaptive-card.json",
        version: "1.0",
        body,
        actions: [
            {
                type: "Action.Submit",
                title: "Add field",
                data: {
                    [KEYCARDID]: cardId,
                    [KEYSUBMITACTIONID]: ACTIONSUBMITADDFIELD
                },
            },
            {
                type: "Action.Submit",
                title: "Submit",
            }
        ]
    };

    return MessageFactory.attachment(CardFactory.adaptiveCard(card));
}

Using this function, you can send the card initially like this in C#:

var inputCount = 1;
var cardId = Guid.NewGuid().ToString();
var reply = GenerateAdaptiveCardActivityWithInputs(inputCount, new Dictionary<string, string> { { KEYCARDID, cardId } });
var response = await turnContext.SendActivityAsync(reply, cancellationToken);
var dict = await InputCardStateAccessor.GetAsync(turnContext, () => new Dictionary<string, (string, int)>(), cancellationToken);

dict[cardId] = (response.Id, inputCount);

Node.js:

var inputCount = 1;
var cardId = Date.now().toString();
var reply = this.generateAdaptiveCardActivityWithInputs(inputCount, { [KEYCARDID]: cardId });
var response = await turnContext.sendActivity(reply);
var dict = await this.inputCardState.get(turnContext, {});
dict[cardId] = {
    activityId: response.id,
    inputCount: inputCount
};
await this.inputCardState.set(turnContext, dict);

And you can update the card in response to the card's "add field" submit action like this in C#:

private async Task AddFieldAsync(ITurnContext turnContext, CancellationToken cancellationToken)
{
    var activity = turnContext.Activity;

    if (activity.ChannelId == Channels.Msteams)
    {
        var value = JObject.FromObject(activity.Value);
        var cardId = Convert.ToString(value[KEYCARDID]);
        var dict = await InputCardStateAccessor.GetAsync(turnContext, () => new Dictionary<string, (string, int)>(), cancellationToken);

        if (dict.TryGetValue(cardId, out var cardInfo))
        {
            var update = GenerateAdaptiveCardActivityWithInputs(++cardInfo.InputCount, value);

            update.Id = cardInfo.ActivityId;
            update.Conversation = activity.Conversation;

            await turnContext.UpdateActivityAsync(update, cancellationToken);

            dict[cardId] = cardInfo;
        }
    }
}

Node.js:

async addField(turnContext) {
    var activity = turnContext.activity;

    if (activity.channelId == 'msteams') {
        var value = activity.value;
        var cardId = value[KEYCARDID];
        var dict = await this.inputCardState.get(turnContext, {});
        var cardInfo = dict[cardId];

        if (cardInfo) {
            var update = this.generateAdaptiveCardActivityWithInputs(++cardInfo.inputCount, value);

            update.id = cardInfo.activityId;
            update.conversation = activity.conversation;
            update.serviceUrl = activity.serviceUrl;

            dict[cardId] = cardInfo;

            await this.inputCardState.set(turnContext, dict);
            await turnContext.updateActivity(update);
        }
    }
}

Updated card


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...