Bolt-js: Modals > app.client.views.update does not work

Created on 15 Oct 2019  ·  12Comments  ·  Source: slackapi/bolt-js

Description

I use Bolt and I'm attempting to use app.client.views.open and app.client.views.update and I'm running into an issue. I have no issues opening the modal, but when the user submits I want to call app.client.views.update and update the modal.

This is an issue because if I do not use ack(), I am getting an error on the successfully updated view “[ERROR] WebClient:0 Error: An incoming event was not acknowledged before the timeout. Ensure that the ack() argument is called in your listeners.”. But, if I do respond with ack(), before calling app.client.views.update then the modal closes and the update is never received?

The code examples you provided here https://slack.dev/bolt/concepts#updating-pushing-views do not work.

What type of issue is this? (place an x in one of the [ ])

  • [x] bug
  • [ ] enhancement (feature request)
  • [ ] question
  • [x] documentation related
  • [ ] testing related
  • [ ] discussion

Requirements (place an x in each of the [ ])

  • [x] I've read and understood the Contributing guidelines and have done my best effort to follow them.
  • [x] I've read and agree to the Code of Conduct.
  • [x] I've searched for any related issues and avoided creating a duplicate issue.

Bug Report

Reproducible in:

package version: 1.4

node version: v12.1.0

OS version(s): Ubuntu 16.04.6 LTS

Steps to reproduce:

  1. Here is my code https://gist.github.com/vidanov/1d4e7eff8c3e186f1f6ae187f4b30c0d
    The modal will be updated, but with an error.
  2. if you uncomment the line number 76 the modal will not be updated and disappear after the app.client.views.update call.

Expected result:

The modal should be updated and stay opened.

Actual result:

The modal disappeared after the app.client.views.update call.

Attachments:

1|test-node | [ERROR] WebClient:0 Error: An incoming event was not acknowledged before the timeout. Ensure that the ack() argument is called in your listeners.

Most helpful comment

@vidanov When you're acknowledging a view_submission event, you can include a response_action which allows you to update the view. This method doesn't use app.client.views.update, you'll just pass the JSON directly into the ack() function. When you pass nothing into ack, it defaults to closing the modal.

Response actions are detailed a bit in the documentation. A basic example with bolt is:

```
app.view('CALLBACK_ID', ({ ack, view } => {
ack({
"response_action": "update",
"view": {
"type": "modal",
"title": {
"type": "plain_text",
"text": "Updated view"
},
"blocks": [
{
"type": "section",
"text": {
"type": "plain_text",
"text": "I've changed and I'll never be the same. You must believe me."
}
}
]
});

console.log(view);
});

All 12 comments

I found a solution. In my case I tried to use app.view. It works with app.action.

@vidanov When you're acknowledging a view_submission event, you can include a response_action which allows you to update the view. This method doesn't use app.client.views.update, you'll just pass the JSON directly into the ack() function. When you pass nothing into ack, it defaults to closing the modal.

Response actions are detailed a bit in the documentation. A basic example with bolt is:

```
app.view('CALLBACK_ID', ({ ack, view } => {
ack({
"response_action": "update",
"view": {
"type": "modal",
"title": {
"type": "plain_text",
"text": "Updated view"
},
"blocks": [
{
"type": "section",
"text": {
"type": "plain_text",
"text": "I've changed and I'll never be the same. You must believe me."
}
}
]
});

console.log(view);
});

@shanedewael Thank you! I missed this example here:
https://slack.dev/bolt/concepts#creating-modals

Is there anything special about what cannot go into the blocks section? If I put any of the input elements, it wont work and gives an error.
For example:

       "blocks": [
         {
           "type": "section",
           "text": {
             "type": "plain_text",
             "text": "I've changed and I'll never be the same. You must believe me."
           }
         },
    {
        type: "input",
        element: {
            type: "radio_buttons",
            options: [
                {
                    text: {
                        type: "plain_text",
                        text: "*this is plain_text text*",
                        emoji: true
                    },
                    value: "value-0"
                },
            ]
        },
        label: {
            type: "plain_text",
            text: "Label",
            emoji: true
        }
    }           

       ]

@rnaimi No it isn't. Your blocks seem to be valid. Are you using views.update API method call in app.view listener as with the original question here? If so, tell the same thing using ack() method with valid response_action instead.

I'm responding back this in my call to ack() to a view_submission callback. When I do this, the original modal view that submitted the view_submission stays on, and after I call the ack, it displays "We had some trouble connecting. Try again?". If I take out the input element, it works fine and shows the section with static text.

    response_action: "update",
    view: {
        type: 'modal',
        callback_id: 'second_step_callback_id',
        title: {
          type: 'plain_text',
          text: 'Second step',
        },
        submit: {
            type: "plain_text",
            text: "Submit",
            emoji: true
        },
        close: {
            type: "plain_text",
            text: "Cancel",
            emoji: true
        },  
        blocks: [
            {
                type: "section",
                block_id: "section_890",
                text: {
                    type: "mrkdwn",
                    text: "*This is second step in a multi-step workflow.*"
                }
            },
            {
                type: "input",
                element: {
                    type: "radio_buttons",
                    options: [
                        {
                            text: {
                                type: "plain_text",
                                text: "*this is plain_text text*",
                                emoji: true
                            },
                            value: "value-0"
                        },
                    ]
                },
                label: {
                    type: "plain_text",
                    text: "Label",
                    emoji: true
                }
            }           
        ],
      }

If I take out the input element, it works fine and shows the section with static text.

hmm, I just tried both pattens out but they work for me. Probably, the error might be related to other reasons.

When you use the ack() method to send a view, there is no way to get the reason for its error. So, when you encounter something wrong with it, I recommend using Block Kit Builder to verify the blocks you compose (as I did in the previous comment).

Thanks a lot for your quick response. I got it partially working. It doesn't give me an error anymore, but it also doesn't behave as I expected. The reason why it was giving me an error was because I was mistakenly calling ack () without await... I fixed that by
await ack (response_content);

However, I am still having a problem with updating the view. My original view has a number of input fields (see the first section). When I send the update response through ack, I fill in the fields with initial_value attribute (see the second section). What I expected to see was the form with the values filled in. What I see is the form with empty fields.

initial form:

{
  view: {
    type: 'modal',
    title: {
      type: 'plain_text',
      text: 'Find Contact'
    },
    clear_on_close: true,
    notify_on_close: true,
    callback_id: 'do_find_contact',
    submit: {
      type: 'plain_text',
      text: 'Find'
    },
    close: {
      type: 'plain_text',
      text: 'Cancel'
    },
    blocks: [
      {
        label: {
          type: 'plain_text',
          text: 'Email'
        },
        type: 'input',
        block_id: 'email',
        element: {
          type: 'plain_text_input',
          action_id: 'email'
        },
        optional: true
      },
      {
        type: 'input',
        block_id: 'firstname',
        element: {
          type: 'plain_text_input',
          action_id: 'firstname'
        },
        label: {
          type: 'plain_text',
          text: 'First Name'
        },
        optional: true
      },
      {
        type: 'input',
        block_id: 'lastname',
        element: {
          type: 'plain_text_input',
          action_id: 'lastname'
        },
        label: {
          type: 'plain_text',
          text: 'Last Name'
        },
        optional: true
      },
      {
        type: 'input',
        block_id: 'phone',
        element: {
          type: 'plain_text_input',
          action_id: 'phone'
        },
        label: {
          type: 'plain_text',
          text: 'Phone Number'
        },
        optional: true
      }
    ]
  }
}

this is what I send back through ack:

{
  view: {
    type: 'modal',
    title: {
      type: 'plain_text',
      text: 'Contact Details'
    },
    clear_on_close: true,
    notify_on_close: true,
    callback_id: 'do_update_contact',
    submit: {
      type: 'plain_text',
      text: 'Update'
    },
    close: {
      type: 'plain_text',
      text: 'Cancel'
    },
    blocks: [
      {
        label: {
          type: 'plain_text',
          text: 'Email'
        },
        type: 'input',
        block_id: 'email',
        element: {
          type: 'plain_text_input',
          action_id: 'email',
          initial_value: '[email protected]'
        },
        optional: true
      },
      {
        type: 'input',
        block_id: 'firstname',
        element: {
          type: 'plain_text_input',
          action_id: 'firstname',
          initial_value: 'John'
        },
        label: {
          type: 'plain_text',
          text: 'First Name'
        },
        optional: true
      },
      {
        type: 'input',
        block_id: 'lastname',
        element: {
          type: 'plain_text_input',
          action_id: 'lastname',
          initial_value: 'Smith'
        },
        label: {
          type: 'plain_text',
          text: 'Last Name'
        },
        optional: true
      },
      {
        type: 'input',
        block_id: 'phone',
        element: {
          type: 'plain_text_input',
          action_id: 'phone',
          initial_value: '6501234567'
        },
        label: {
          type: 'plain_text',
          text: 'Phone Number'
        },
        optional: true
      }
    ]
  },
  response_action: 'update'
}

@rnaimi
If you use the same pair of block_id and action_id even after updating a modal, the updating operation keeps the previous state for those inputs. That's why those input values are not replaced with the initial_value. You can change either block_id or action_id for reflecting the initial_value anyways. As your second view has a different callback_id, I do understand the behavior may be a bit surprising but for now, going with different names for blocks would be the quickest way to realize the app you want to build.

thank you!!!! I spent 2 days trying to figure out what was wrong... That worked.
One thing that still puzzles me is that when I update the view with the initial_value for the fields, if I cancel out of the modal, it asks "Are you sure you want to leave?" as if there is a dirty flag that was set even if the values are unchanged. Is there a parameter or API call that I can make to clear the dirty flag?

I think I figured it out... Apparently if I change block_id, then the modal can be dismissed without prompt. If I only change the action_id, then modal thinks there were fields that were modified so it prompts.
Thanks again for your help.

I tried first sending a message along with ack({update view}) that we are validating the data then I posted the updated view using views.update. Then it worked for me. Cheers!👍

Was this page helpful?
0 / 5 - 0 ratings

Related issues

i-break-codes picture i-break-codes  ·  4Comments

st3fan picture st3fan  ·  3Comments

kmartin-215 picture kmartin-215  ·  5Comments

malonehedges picture malonehedges  ·  5Comments

TK95 picture TK95  ·  3Comments