I am creating an slack app which posts polls and quizzes to channels and specific users. I'm using chat.postMessage() for that. I have used conversations_select type input field in modal to select a channel/user. When I select a channel from that list and its value is passed to chat.postMessage() it's successfully posting the polls/quizzes. But when I select user from the list and pass the userID to chat.postMessage() method as a channel field it doesn't work.
How should I go about it.
//Quiz Main screen UI
app.action("quizstart", async ({ ack, body, context, client }) => {
// Acknowledge the button request
ack();
try {
const result = await app.client.views.update({
token: context.botToken,
// Pass the view_id
view_id: body.view.id,
// View payload with updated blocks
view: {
type: "modal",
// View identifier
callback_id: "quiz_view",
title: {
type: "plain_text",
text: "Quiz"
},
close: {
type: "plain_text",
text: "Cancel",
emoji: true
},
blocks: [
{
dispatch_action: true,
type: "input",
element: {
type: "plain_text_input",
dispatch_action_config: {
trigger_actions_on: ["on_character_entered"]
},
action_id: "question_input_field" //ooy
},
label: {
type: "plain_text",
text: "Question",
emoji: true
}
},
{
dispatch_action: true,
type: "input",
element: {
type: "plain_text_input",
dispatch_action_config: {
trigger_actions_on: ["on_character_entered"]
},
action_id: "plain_text_input_action"
},
label: {
type: "plain_text",
text: "Choice A",
emoji: true
},
optional: true
},
{
dispatch_action: true,
type: "input",
element: {
type: "plain_text_input",
dispatch_action_config: {
trigger_actions_on: ["on_character_entered"]
},
action_id: "plain_text_input_action"
},
label: {
type: "plain_text",
text: "Choice B",
emoji: true
},
optional: true
},
{
dispatch_action: true,
type: "input",
element: {
type: "plain_text_input",
dispatch_action_config: {
trigger_actions_on: ["on_character_entered"]
},
action_id: "plain_text_input_action"
},
label: {
type: "plain_text",
text: "Choice C",
emoji: true
},
optional: true
},
{
dispatch_action: true,
type: "input",
element: {
type: "plain_text_input",
dispatch_action_config: {
trigger_actions_on: ["on_character_entered"]
},
action_id: "plain_text_input_action"
},
label: {
type: "plain_text",
text: "Choice D",
emoji: true
},
optional: true
},
{
dispatch_action: true,
type: "input",
element: {
type: "plain_text_input",
dispatch_action_config: {
trigger_actions_on: ["on_character_entered"]
},
action_id: "plain_text_input_action"
},
label: {
type: "plain_text",
text: "Choice E",
emoji: true
},
optional: true
},
{
dispatch_action: true,
type: "input",
element: {
type: "plain_text_input",
dispatch_action_config: {
trigger_actions_on: ["on_character_entered"]
},
action_id: "correct_answer_action"
},
label: {
type: "plain_text",
text: "Correct Answer",
emoji: true
},
optional: false
},
{
dispatch_action: true,
type: "input",
element: {
type: "plain_text_input",
dispatch_action_config: {
trigger_actions_on: ["on_character_entered"]
},
action_id: "quizname_action"
},
label: {
type: "plain_text",
text: "Quiz name",
emoji: true
},
optional: false
},
{
type: "section",
text: {
type: "mrkdwn",
text:
"Here you can configure your *WebMobi Poll*. (<https://google.com|learn more>)"
}
},
{
type: "divider"
},
{
type: "section",
text: {
type: "mrkdwn",
text: "*Audience*"
}
},
{
type: "actions",
elements: [
{
type: "conversations_select",
placeholder: {
type: "plain_text",
text: "conversations", //730
emoji: true
},
action_id: "select_channel_menu"
}
]
},
{
type: "section",
text: {
type: "mrkdwn",
text: "*Advanced Settings*"
}
},
{
type: "section",
text: {
type: "mrkdwn",
text: ":clipboard: *Anonymus*\nChoose to send poll as anonymous"
},
accessory: {
type: "static_select",
placeholder: {
type: "plain_text",
text: "Select",
emoji: true
},
options: [
{
text: {
type: "plain_text",
text: "True",
emoji: true
},
value: "value-anonymus1"
},
{
text: {
type: "plain_text",
text: "False",
emoji: true
},
value: "value-anonymus2"
}
]
}
},
],
submit: {
type: "plain_text",
text: "Submit"
}
}
});
console.log(result);
} catch (error) {
console.error(error);
}
});
//app.view for view submission
// main question UI
app.view("quiz_view", async ({ ack, body, view, context }) => {
await ack();
console.log("inside quiz view app.view " + view.id);
const val1 = view["state"]["values"];
const user1 = body["user"]["id"];
const user2 = body["user"]["name"];
var quiz_id = await makeQuizPostRequest(val1, user1, user2);
publishMessageQuiz(val1, user1, user2, quiz_id,body);
console.log(quiz_id);
});
async function makeQuizPostRequest(val1, user1, user2) {
const x = val1;
console.log(x);
const ques = Object.values(x)[0].question_input_field.value;
console.log("before ques console");
console.log("ques " + ques);
const opt1 = Object.values(x)[1].plain_text_input_action.value;
console.log("opt1 " +opt1);
const opt2 = Object.values(x)[2].plain_text_input_action.value;
console.log("opt2 " +opt2);
const opt3 = Object.values(x)[3].plain_text_input_action.value;
console.log("opt3 " +opt3);
const opt4 = Object.values(x)[4].plain_text_input_action.value;
console.log("opt4 " +opt4);
const opt5 = Object.values(x)[5].plain_text_input_action.value;
console.log("opt5 " +opt5);
const correctAns= Object.values(x)[6].correct_answer_action.value;
console.log("correctAns " + correctAns);
const quizname = Object.values(x)[7].quizname_action.value;
console.log(" quizname " + quizname);
const channel = Object.values(x)[8].select_channel_menu.selected_conversation;
var options = new Array(7);
if (opt1 != null) {
console.log(opt1);
options.push(opt1);
}
if (opt2 != null) {
console.log(opt2);
options.push(opt2);
}
if (opt3 != null) {
console.log(opt3);
options.push(opt3);
}
if (opt4 != null) {
console.log(opt4);
options.push(opt4);
}
if (opt5 != null) {
console.log(opt5);
options.push(opt5);
}
options.shift();
options.shift();
options.shift();
options.shift();
options.shift();
var options1 = options.toString();
options1 = "[" + options1 + "]";
let payload =
{
"app_id": "XXXXXXXXXXXXXXXXXXXXXXXX",
"created_by": user2,
"customtheme": false,
"quizarray": [
{
"question": ques,
"options": options1,
"correctanswer": correctAns,
"isActive": true
}
],
"quiz_name": quizname
}
let res = await axios.post(
"https://api_to_create_quiz",
payload
);
let data = res.data;
console.log(data.responseString);
var quiz_id = res.data.data.quiz_id;
console.log(quiz_id);
return quiz_id;
}
//publishMessage for quiz
async function publishMessageQuiz(val1, user1, user2, quiz_id, body) {
console.log("publishmessage_entry Quiz");
console.log("quiz_id", quiz_id);
let x = val1;
// console.log(x);
let voteList = [];
let ques = Object.values(x)[0].question_input_field.value;
let opt1 = Object.values(x)[1].plain_text_input_action.value;
let opt2 = Object.values(x)[2].plain_text_input_action.value;
let opt3 = Object.values(x)[3].plain_text_input_action.value;
let opt4 = Object.values(x)[4].plain_text_input_action.value;
let opt5 = Object.values(x)[5].plain_text_input_action.value;
let correctAns=Object.values(x)[6].correct_answer_action.value;
let quizname=Object.values(x)[7].quizname_action.value;
const channel = Object.values(x)[8].select_channel_menu.selected_conversation;
let options = new Array(5);
if (opt1 != null) {
options.push(opt1);
}
if (opt2 != null) {
options.push(opt2);
}
if (opt3 != null) {
options.push(opt3);
}
if (opt4 != null) {
options.push(opt4);
}
if (opt5 != null) {
options.push(opt5);
}
options.shift();
options.shift();
options.shift();
options.shift();
options.shift();
let options1 = options.toString();
let optionList = [];
let optionButtons = [];
let optionGraph = [];
let arr_options = options1.split(",");
arr_options.forEach(function(main, index) {
// console.log(main);
let val_id = "value" + index;
// console.log(val_id);
let act_id = "option" + index + "_" + quiz_id;
console.log(act_id);
optionButtons.push(act_id);
console.log(optionButtons);
let voteObj = {
type: "button",
text: {
type: "plain_text",
emoji: true,
text: main
},
value: val_id,
action_id: act_id
};
optionList.push(voteObj);
});
try {
// Call the chat.postMessage method using the built-in WebClient
let result = await app.client.chat.postMessage({
// The token you used to initialize your app
token: process.env.SLACK_BOT_TOKEN,
signingSecret: process.env.SLACK_SIGNING_SECRET,
channel:channel,
text: "WebMobi Quiz",
blocks: [
{
type: "section",
text: {
type: "mrkdwn",
text: "Quiz question from WebMobi poll"
}
},
{
type: "section",
text: {
type: "mrkdwn",
text: "*" + ques + "*"
}
},
{
type: "actions",
elements: [
...optionList
]
},
{
type: "divider"
},
{
type: "context",
elements: [
{
type: "mrkdwn",
text:
"hello (Sender: " +
user2 +
" | :unlock: *Responses*: Non-Anonymous/Anonymous | :clock930: *Status*: "
}
]
},
{
type: "context",
elements: [
{
type: "mrkdwn",
text:
":smile: Get insight of your polls from <https://google.com|WebMobi dashboard>"
}
]
}
]
});
optionButtons.forEach(el => {
app.action(el, async ({ ack, body, client }) => {
// Acknowledge the button request
await ack();
// console.log(el);
console.log("bodymessage", body);
// console.log("bodymessage", client);
// console.log("my user id", body.user.id);
let person = body.user.name;
let time = body.message.ts;
console.log("message Body", body);
// console.log("This is my Email Id",emailId);
// let result = await postPollApi(poll_id);
let action = body.actions[0].action_id;
let optionValue = "";
let optionResults = [];
let optionGraphResults = [];
let pollAnswer = "";
optionList.forEach(els => {
let opitonObj = {};
let optionGraphResultsObj = {};
let ok = els.text.text;
if (els.action_id == action) {
optionValue = els.value;
opitonObj = {
type: "button",
text: {
type: "plain_text",
emoji: true,
text: els.text.text
},
value: action,
style: "primary"
};
optionResults.push(opitonObj);
const red = "█████████████████████████";
optionGraphResultsObj = {
type: "section",
text: {
type: "mrkdwn",
text: els.text.text + ": `" + red + "` | 100% (1)"
} //option1 + ": `" + graph + "` " + percent + "% (" + noofvotes + ")"
};
pollAnswer = els.text.text;
} else {
opitonObj = {
type: "button",
text: {
type: "plain_text",
emoji: true,
text: els.text.text
},
value: action
};
optionResults.push(opitonObj);
const white = "█ ";
optionGraphResultsObj = {
type: "section",
text: {
type: "mrkdwn",
text: els.text.text + ": `" + white + "` | 0% (0)"
//els.text.text+"`█ ` | 0% (0)"
}
};
}
optionGraphResults.push(optionGraphResultsObj);
});
console.log("action id", action);
let email = await getUserEmail(client, body.user.id);
console.log("webmobi email id", email);
let user_webmobi_id = await getuserId(email);
console.log("webmobi user id", user_webmobi_id);
// let poll_result = await submitPoll(pollAnswer, poll_id,user_webmobi_id);
// console.log("submited poll",poll_result);
try {
console.log("364 quiz");
// Call views.update with the built-in client
const result = await client.chat.update({
token: process.env.SLACK_BOT_TOKEN,
ts: time,
response_url: body.response_url,
response_type: "ephemeral",
channel: channel,
text: "Updated quiz results",
//as_user:true,
blocks: [
{
type: "section",
text: {
type: "mrkdwn",
text: `Quiz question from WebMobi poll`
}
},
{
type: "section",
text: {
type: "mrkdwn",
text: ques
}
},
{
type: "actions",
elements: [...optionResults]
},
// ...optionGraph
...optionGraphResults,
{
type: "divider"
},
{
type: "context",
elements: [
{
type: "mrkdwn",
text:
"hello (Sender: user | :unlock: *Responses*: Non-Anonymous/Anonymous | :clock930: *Status*: "
}
]
},
{
type: "context",
elements: [
{
type: "mrkdwn",
text:
":smile: Get insight of your polls from <https://google.com|WebMobi dashboard>"
}
]
}
]
});
console.log(result);
console.log("chatpostmessage");
} catch (error) {
console.error(error);
}
});
});
// Print result, which includes information about the message (like TS)
console.log(result);
} catch (error) {
console.error(error);
}
}
chat.postMessage is a bit tricky in that way.
https://api.slack.com/methods/chat.postMessage#channels__post-to-a-direct-message-channel
You need to pass the dmId instead of UserId in chat.postMessage()
To get the dmId,you will need conversations.open method.
https://api.slack.com/methods/conversations.open
Sample Code in JS
slackClient.conversations.open({
users: userId
})
.then(async (res) => {
await slackClient.chat.postMessage({
channel: res.channel.id,
text: text,
blocks: blocks
})
}
I have grid with drag&drop functionality:
var data = [
{ id: 1, text: "text 1", position: 0 },
{ id: 2, text: "text 2", position: 1 },
{ id: 3, text: "text 3", position: 2 }
]
var dataSource = new kendo.data.DataSource({
data: data,
schema: {
model: {
id: "id",
fields: {
id: { type: "number" },
text: { type: "string" },
position: { type: "number" }
}
}
}
});
var grid = $("#grid").kendoGrid({
dataSource: dataSource,
scrollable: false,
columns: ["id", "text", "position"]
}).data("kendoGrid");
grid.table.kendoDraggable({
filter: "tbody > tr",
group: "gridGroup",
threshold: 100,
hint: function(e) {
return $('<div class="k-grid k-widget"><table><tbody><tr>' + e.html() + '</tr></tbody></table></div>');
}
});
grid.table.kendoDropTarget({
group: "gridGroup",
drop: function(e) {
e.draggable.hint.hide();
var target = dataSource.getByUid($(e.draggable.currentTarget).data("uid")),
dest = $(document.elementFromPoint(e.clientX, e.clientY));
if (dest.is("th")) {
return;
}
dest = dataSource.getByUid(dest.parent().data("uid"));
//not on same item
if (target.get("id") !== dest.get("id")) {
//reorder the items
var tmp = target.get("position");
target.set("position", dest.get("position"));
dest.set("position", tmp);
dataSource.sort({ field: "position", dir: "asc" });
}
}
});
(working example: http://jsfiddle.net/JBeQn/ )
Is it possible to show the placeholder where the row will be dropped? From current implementation it's not obvious. So I would like to make something like this:
http://demos.telerik.com/kendo-ui/sortable/events
or this:
http://jsfiddle.net/bgrins/tzYbU/
In the demo above placeholder is showed and it's quite easy to understand where the row will be dropped.
So any ideas?
I have two TreeViews, one has a list of countries, and the other is empty, now I want drag and drop selected countries into the second tree-view. I don't know how to send data to the controller from the TreeView and there is also some text field on the page in a form. So, how can I send both the form data and the TreeView's data to the controller.
Here is the code for the second tree-view which is empty and I want to add the selected nodes to:
#(Html.Kendo().TreeView()
.Name("treeview-right")
.DragAndDrop(true)
.Events(events => events
.Drag("onDrag")
.Drop("onDrop")
)
)
Please try with the below code snippet.
HTML/VIEW
<div style="border: 1px solid green;">
<div id="treeview-left"></div>
</div>
<div style="border: 1px solid red;">
<div id="treeview-right"></div>
</div>
<div id="mydiv" onclick="SaveData()">Click me to save data</div>
<script>
$("#treeview-left").kendoTreeView({
dragAndDrop: true,
dataSource: [
{
id: 11, text: "Furniture", expanded: true, items: [
{ id: 12, text: "Tables & Chairs" },
{ id: 13, text: "Sofas" },
{ id: 14, text: "Occasional Furniture" }
]
},
{
id: 15, text: "Decor", items: [
{ id: 16, text: "Bed Linen" },
{ id: 17, text: "Curtains & Blinds" },
{ id: 18, text: "Carpets" }
]
}
]
});
$("#treeview-right").kendoTreeView({
dragAndDrop: true,
dataSource: [
{
id: 1, text: "Storage", expanded: true, items: [
{ id: 2, text: "Wall Shelving" },
{ id: 3, text: "Floor Shelving" },
{ id: 4, text: "Kids Storage" }
]
},
{
id: 5, text: "Lights", items: [
{ id: 6, text: "Ceiling" },
{ id: 7, text: "Table" },
{ id: 8, text: "Floor" }
]
}
]
});
var selectedID;
function SaveData() {
selectedID = '';
var tv = $("#treeview-right").data("kendoTreeView");
selectedID = getRecursiveNodeText(tv.dataSource.view());
alert(selectedID);
var data = {};
data.str = selectedID;
$.ajax({
url: 'Home/SaveData',
type: 'POST',
data: data,
dataType: 'json',
success: function (result) {
alert("Success");
},
error: function (result) {
alert("Error");
},
});
}
function getRecursiveNodeText(nodeview) {
for (var i = 0; i < nodeview.length; i++) {
selectedID += nodeview[i].id + ",";
//nodeview[i].text; You can also access text here
if (nodeview[i].hasChildren) {
getRecursiveNodeText(nodeview[i].children.view());
}
}
return selectedID;
}
</script>
CONTROLLER
namespace MvcApplication2.Controllers
{
public class HomeController : Controller
{
[HttpPost]
public JsonResult SaveData(string str)
{
foreach (string s in str.Split(','))
{
if (!string.IsNullOrEmpty(s))
{
//Perform your opeartion here
}
}
return Json("");
}
}
}
jsfiddle Demo
I'm using Durandal and Kendo UI. My current problem is the edit popup event on my grid. I cannot seem to set the selected value on my dropdown.
I can debug and inspect, and I indeed do see the correct value of e.model.InstrumentName nicely populated.
How can I set the value/text of those dropdowns in edit mode ?
Here's my grid init:
positGrid = $("#positGrid").kendoGrid({
dataSource: datasource,
columnMenu: false,
{
field: "portfolioName", title: "Portfolio Name",
editor: portfolioDropDownEditor, template: "#=portfolioName#"
},
{
field: "InstrumentName",
width: "220px",
editor: instrumentsDropDownEditor, template: "#=InstrumentName#",
},
edit: function (e) {
var instrDropDown = $('#InstrumentName').data("kendoDropDownList");
var portfDropDown = $('#portfolioName').data("kendoDropDownList");
instrDropDown.list.width(350); // let's widen the INSTRUMENT dropdown list
if (!e.model.isNew()) { // set to current valuet
//instrDropDown.text(e.model.InstrumentName); // not working...
instrDropDown.select(1);
//portfDropDown.text();
}
},
filterable: true,
sortable: true,
pageable: true,
editable: "popup",
});
Here's my Editor Template for the dropdown:
function instrumentsDropDownEditor(container, options) {
// INIT INSTRUMENT DROPDOWN !
var dropDown = $('<input id="InstrumentName" name="InstrumentName">');
dropDown.appendTo(container);
dropDown.kendoDropDownList({
dataTextField: "name",
dataValueField: "id",
dataSource: {
type: "json",
transport: {
read: "/api/breeze/GetInstruments"
},
},
pageSize: 6,
select: onSelect,
change: function () { },
optionLabel: "Choose an instrument"
}).appendTo(container);
}
thanks a lot
Bob
Your editor configuration is bit unlucky for grid, anyway i have updated my ans on provided code avoiding manual selections:
Assumptions: Instrument dropdown editor only (leaving other fields as strings), Dummy data for grid
<div id="positGrid"></div>
<script>
$(document).ready(function () {
$("#positGrid").kendoGrid({
dataSource: {
data: [
{ PositionId: 1, Portfolio: "Jane Doe", Instrument: { IID: 3, IName: "Auth2" }, NumOfContracts: 30, BuySell: "sfsf" },
{ PositionId: 2, Portfolio: "John Doe", Instrument: { IID: 2, IName: "Auth1" }, NumOfContracts: 33, BuySell: "sfsf" }
],
schema: {
model: {
id: "PositionId",
fields: {
"PositionId": { type: "number" },
Portfolio: { validation: { required: true } },
Instrument: { validation: { required: true } },
NumOfContracts: { type: "number", validation: { required: true, min: 1 } },
BuySell: { validation: { required: true } }
}
}
}
},
toolbar: [
{ name: "create", text: "Add Position" }
],
columns: [
{ field: "PositionId" },
{ field: "Portfolio" },
{ field: "Instrument", width: "220px",
editor: instrumentsDropDownEditor, template: "#=Instrument.IName#" },
{ field: "NumOfContracts" },
{ field: "BuySell" },
{ command: [ "edit", "destroy" ]
},
],
edit: function (e) {
var instrDropDown = $('#InstrumentName').data("kendoDropDownList");
instrDropDown.list.width(400); // let's widen the INSTRUMENT dropdown list
},
//sortable: true,
editable: "popup",
});
});
function instrumentsDropDownEditor(container, options) {
$('<input id="InstrumentName" required data-text-field="IName" data-value-field="IID" data-bind="value:' + options.field + '"/>')
.appendTo(container)
.kendoDropDownList({
dataSource: {
type: "json",
transport: {
read: "../Home/GetMl"
}
},
optionLabel:"Choose an instrument"
});
}
</script>
Action fetching json for dropddown in Home controller:
public JsonResult GetMl()
{
return Json(new[] { new { IName = "Auth", IID = 1 }, new { IName = "Auth1", IID = 2 }, new { IName = "Auth2", IID = 3 } },
JsonRequestBehavior.AllowGet);
}
I am new on kendo UI framework. I am struggling with observable datasource with kendoGrid.
The problem is the table gets created but with empty data.
Here is the link http://jsfiddle.net/praveeny1986/Pf3TQ/5/
And the code :
var gridDataModel = kendo.data.Model.define({
fields: {
"Product": {
type: "string"
},
"Domain": {
type: "string"
},
"PercentPlan": {
type: "string"
},
"CWV": {
type: "string"
},
"Target": {
type: "string"
},
"Accuracy": {
type: "string"
}
}
});
var dataSource = new kendo.data.DataSource({data: tabledata1});
var gridModel = kendo.observable({
gridData: dataSource
});
kendo.bind($("#chart"),gridModel);
$("#chart").kendoGrid({
scrollable:false,
dataSource:gridModel.get('gridData'),
height:600,
autoBind:true,
columns:[
{
field: "Product",
title: "Product"
},
{
field: "Domain",
title: "Sales Domain"
},
{
field: "PercentPlan",
title: "% to Plan"
},
{
field: "CWV",
title: "CWV"
},
{
field: "Target",
title: "Target"
},
{
field: "Accuracy",
title: "Accuracy"
}]
});
var tabledata1 = [
{
Product:"mobile",
Domain:"SMARTPHONES-EAST",
PercentPlan:"95",
CWV:"160",
Target:"200",
Accuracy:"9"
},
{
Product:"mobile",
Domain:2,
PercentPlan:"80",
CWV:"160",
Target:"200",
Accuracy:"8.5"
},
{
Product:"mobile",
Domain:3,
PercentPlan:"75",
CWV:"150",
Target:"200",
Accuracy:"8"
},
{
Product:"mobile",
Domain:4,
PercentPlan:"60",
CWV:"120",
Target:"200",
Accuracy:"6"
},
{
Product:"mobile",
Domain:5,
PercentPlan:"50",
CWV:"150",
Target:"300",
Accuracy:"5"
}
];
Please suggest what i am doing wrong ?
Thanks in advance
Your table data is undefined at the time that you create and bind the datasource.
var dataSource = new kendo.data.DataSource({data: tabledata1});
var tabledata1 = [ ... ];
Move the declaration of tabledata1 to before creating the datasource.
See this updated fiddle.
http://jsfiddle.net/nukefusion/Pf3TQ/7/