This page looks best with JavaScript enabled

SlackのModal Viewの値をプログラムから操作する方法

 ·   2 min read

ほら、マルチセレクトメニューとかで「すべて選択」「すべて解除」とかやりたいじゃないですか。

残念ながら、SlackのAPIにはそのような機能がないのでviews.update を駆使して実現する。

まず views.open でModal Viewを表示する。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
view := slack.ModalViewRequest{
    Type:   slack.VTModal,
    Title:  slack.NewTextBlockObject(slack.PlainTextType, "サンプル", false, false),
    Submit: slack.NewTextBlockObject(slack.PlainTextType, "OK", true, false),
    Close:  slack.NewTextBlockObject(slack.PlainTextType, "キャンセル", true, false),
}
options := []*slack.OptionBlockObject{
    slack.NewOptionBlockObject("opt1", slack.NewTextOject(slack.PlainTextType, "1個", false, false), nil),
    slack.NewOptionBlockObject("opt2", slack.NewTextOject(slack.PlainTextType, "2個", false, false), nil),
    slack.NewOptionBlockObject("opt3", slack.NewTextOject(slack.PlainTextType, "3個", false, false), nil),
}
inputBlock = slack.NewInputBlock(
    "select-menu",
    slack.NewTextBlockObject(
        slack.PlainTextType, "マルチセレクト", false, false),
        slack.NewOptionsMultiSelectBlockElement(
            slack.MultiOptTypeStatic,
            slack.NewTextBlockObject(slack.PlainTextType, "複数選択", false, false),
            "" /* action_id */, options...))
view.Blocks.BlockSet = append(view.Blocks.BlockSet, inputBlock)
view.Blocks.BlockSet = append(view.Blocks.BlockSet,
    slack.NewActionBlock("select-menu-action",
        slack.NewButtonBlockElement("", "all", slack.NewTextBlockObject(slack.PlainTextType, "すべて選択", false, false)).WithStyle(slack.StylePrimary),
        slack.NewButtonBlockElement("", "none", slack.NewTextBlockObject(slack.PlainTextType, "すべて解除", false, false)).WithStyle(slack.StyleDanger)))
client.OpenView(triggerID, view)

後述するが MultiSelectBlockElementaction_id は意味を成さなくなるので空でよい。

このようなモーダルビューができる

このようなモーダルビューができる


そんで、select-menu-action ブロックの slack.InteractionTypeBlockActions(“block_action”) のハンドラで

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var ic slack.InteractionCallback
ic.UnmarshalJSON([]byte(jsonResponse))
view := ic.View
action := ic.ActionCallback.BlockActions[0]
if action.BlockID != "select-menu-action" {
    return
}
for _, b := range view.Blocks.BlockSet {
    if input, ok := b.(*slack.InputBlock); ok && input.BlockID == "select-menu" {
        element := input.Element.(*slack.MultiSelectBlockElement)
        element.ActionID = ""
        if action.Value == "all" {
            element.InitialOptions = element.Options
        } else {
            element.InitialOptions = []*slack.OptionBlockObject{}
        }
    }
}
mvr := slack.ModalViewRequest{
    Type:   view.Type,
    Title:  view.Title,
    Submit: view.Submit,
    Close:  view.Close,
    Blocks: view.Blocks,
}
client.UpdateView(mvr, view.ExternalID, view.Hash, view.ID)

という具合に、InitialOptions を差し替えて views.update を実行する。
ただし、ドキュメントにもあるように

Preserving input entry Data entered or selected in input blocks can be preserved while updating views. The new view object that you use with views.update should contain the same input blocks and elements with identical block_id and action_id values.
via views.update method | Slack

InputBlockblock_id と、そのブロックに属する BlockElementaction_idviews.open のときと同じだと入力状態が維持されるということになっているので、 action_id を空にして渡してやることで値の上書きを可能にする。
空で実行すると都度ランダムな文字列が割り当てられ、異なる action_id になるためである。

まったく小賢しいのだが、これで「すべて選択」「すべて解除」ボタンが実装できた。
くそめんどくさいのでどうにかしてほしい。

Share on

Avatar
WRITTEN BY
northeye
Takuo Kitame. A Software Engineer.