13

Question

I have a Slack modal with a section block containing an input element. When a user submits my modal without entering a value in this input, how do I communicate that error to the user?

My attempt #1: respond with a validation error

Slack's documentation describes how to validate input blocks when I receive a view_submission event (https://api.slack.com/surfaces/modals/using#displaying_errors). But if I return an error for a section block containing a static_select, then Slack does not display an error message (nor does it close the modal).

My attempt #2: set the optional field to false

input blocks have an optional field that can be used for validation. But a static_select in a section block does not have an optional field: https://api.slack.com/reference/block-kit/block-elements#select

My attempt #3: use input blocks

I can not use input blocks because they do not trigger block_actions messages (documented at https://api.slack.com/surfaces/modals/using#interactions). I need to update the modal as the users answer questions.

My attempt #4: the desperation solution that works

I could reply to the view_submission event with a "response_action": "update" response. In that response include an error message like this above the input with the missing value:

{
  "type": "section",
  "text": {
    "type": "mrkdwn",
    "text": "*Please provide a start time:*"
  }
}

I do not like this solution because I doubt I can duplicate the nice error messaging UX that Slack provides for input block validation.

Details

Here is a simplified version of the view parameter that I am passing to the views.open call:

{
  "blocks": [
    {
      "block_id": "start_times",
      "type": "section",
      "text": {
        "type": "plain_text",
        "text": "Choose a start time"
      },
      "accessory": {
        "action_id": "start_times",
        "type": "static_select",
        "placeholder": {
          "type": "plain_text",
          "text": "Choose start"
        },
        "options": [
          {
            "text": {
              "type": "plain_text",
              "text": "10:27 pm"
            },
            "value":
              "{\"path\":\"bookings/new\",\"date\":\"2020-02-14 04:27:22 UTC\"}"
          },
          {
            "text": {
              "type": "plain_text",
              "text": "10:45 pm"
            },
            "value":
              "{\"path\":\"bookings/new\",\"date\":\"2020-02-14 04:45:00 UTC\"}"
          }
        ]
      }
    }
  ],
  "callback_id": "create booking",
  "private_metadata":
    "{\"channel_id\":\"C6M2A4690\",\"min_start_time\":\"2020-02-14 04:27:22 UTC\",\"path\":\"bookings/create\",\"room_id\":175128,\"selected_end_time\":null,\"selected_start_time\":null}",
  "type": "modal",
  "submit": {
    "type": "plain_text",
    "text": "Book"
  },
  "title": {
    "type": "plain_text",
    "text": "Booking room"
  }
}

If the user immediately clicks submit, this is my response to the view_submission event:

{
  "response_action": "errors",
  "errors": {
    "start_times": "Please complete this required field"
  }
}

Upon receipt of my response, Slack turns off the spinner, leaves the modal open, but does not display an error message. The modal looks exactly the same as when it was first opened via views.open.

ipd
  • 5,674
  • 3
  • 34
  • 49
Gabriel Deal
  • 935
  • 1
  • 8
  • 19
  • This looks like a bug. Did you report it to Slack? – Gajus Apr 08 '20 at 13:37
  • Sort of. I gave Slack a laundry list of things I thought need improvement in their modal API. This was one of them. – Gabriel Deal Apr 09 '20 at 16:39
  • @GabrielDeal Any update on this. I'm facing same issue. Pls help. – Gowri Jul 03 '20 at 04:44
  • I have no updates. I ended up using my "the desperation solution that works" solution that I described in my answer. I start the error message with the :warning: emoji, which helps draw attention to the error message. – Gabriel Deal Jul 10 '20 at 22:06

2 Answers2

7

Since you posted your question, the functionality of input blocks has changed. Earlier this month, the ability for input blocks to emit block_actions payloads was introduced.

In the Block Kit reference docs for the input block you can see a new dispatch_action parameter. This is a boolean which, when set to true, causes the interactive element inside the input block to emit a block_actions payload when changed.

Therefore, to answer your question more directly, your #3 solution should now be possible without the caveat that you included. Place the static_select inside of an input block, and set dispatch_action to true, like so:

{
        "type": "input",
        "dispatch_action": true,
        "element": {
            "type": "static_select",
            "placeholder": {
                "type": "plain_text",
                "text": "Select an item",
                "emoji": true
            },
            "options": [
                {
                    "text": {
                        "type": "plain_text",
                        "text": "*this is plain_text text*",
                        "emoji": true
                    },
                    "value": "value-0"
                },
                {
                    "text": {
                        "type": "plain_text",
                        "text": "*this is plain_text text*",
                        "emoji": true
                    },
                    "value": "value-1"
                },
                {
                    "text": {
                        "type": "plain_text",
                        "text": "*this is plain_text text*",
                        "emoji": true
                    },
                    "value": "value-2"
                }
            ],
            "action_id": "static_select-action"
        },
        "label": {
            "type": "plain_text",
            "text": "Label",
            "emoji": true
        }
    }

Upon receiving a view_submission payload, you'll now be able to respond with the correct validation errors and display them to a user. And you can still receive block_actions payloads as needed too.

Matthew Johnston
  • 4,409
  • 2
  • 20
  • 27
  • Also, **text** field must be changed to **label**, and **type** of **label** must be changed to **plain_text** (if text is being used). – Tariq Jun 24 '21 at 21:35
1

Set the response's "content_type" to "application/json" before returning response.

If you are using Django as backend then you can do this.

error_object = {
      "response_action": "errors",
      "errors": {
        "start_times": "Please complete this required field"
      }
    }

return HttpResponse(json.dumps(error_object), content_type="application/json")

   ===OR===

return JsonResponse(error_object)

don't forget to import JsonReponse if you are using it:

from django.http import JsonResponse
Irfan Alam
  • 476
  • 3
  • 9
  • 25
  • That is not my problem. I do set the content type correctly. The validation error response works fine if I use input blocks (a non-interactive input type). It just does not work if I use an interactive component like the one described in my question. – Gabriel Deal Jul 10 '20 at 21:56