Want to display the text of the button clicked in adaptive cards - botframework

I have created a Teams bot and I have a few submit action buttons.
On clicking these buttons I would want to let the user know that the button has been clicked.
Im using adaptive cards and submit action.
card.Actions.Add(new AdaptiveSubmitAction()
{
Title = item.Key,
Data = item.Value,
DataJson = "{\"Type\": \"Sort\"}"
});
On clicking the "sort button I want the bot postback "sort".
This is how I see in teams
Thanks in advance

It's possible to do this using the special "msteams" payload, and specifying "messageBack", or "imBack" as the message type, similar to this:
{
"type": "Action.Submit",
"title": "Click me for messageBack",
"data": {
"msteams": {
"type": "messageBack",
"displayText": "I clicked this button",
"text": "text to bots",
"value": "{\"bfKey\": \"bfVal\", \"conflictKey\": \"from value\"}"
}
}
}
You can read more about it here.
Because you're using C#, you'd need an actual class to represent this, so you could do something like:
public class msteams
{
public string type {get; set;} = "messageBack";
public string displayText {get; set;}
public string text {get; set;}
public string value {get; set;}
}
Then you would set it like this:
card.Actions.Add(new AdaptiveSubmitAction()
{
Title = item.Key,
Data = new msTeams() { displayText = "...", ... }
});
Obviously you could use attributes to change the name of the class, property names, etc. For a more simple approach, you can just the "imBack" option, which I've wrapped below with attributes as well:
public class AdaptiveCardImBackButton
{
[JsonProperty("type")]
public string Type { get; set; } = "imBack";
[JsonProperty("value")]
public string Value { get; set; }
}
Then I wrap that again, to get it to serialize the outer "msteams" attribute, as follows:
public class AdaptiveCardImBackButtonContainer
{
[JsonProperty("msteams")]
public AdaptiveCardImBackButton AdaptiveCardImBackButton { get; private set; }
public AdaptiveCardImBackButtonContainer(string value)
{
AdaptiveCardImBackButton = new AdaptiveCardImBackButton() { Value = value };
}
}
The final usage in your code is really simple:
card.Actions.Add(new AdaptiveSubmitAction()
{
Title = "sort",
Data = new AdaptiveCardImBackButtonContainer("sort")
});

Related

Getting carousel data back into object

I am creating a dynamic carousel view in my Xamarin app, and so far it works just fine, but...
My carousel contains students and each carousel page is a link to a student info page. I want to be able to set an object with the current selected student for me to grab on all the students subpages (hope this makes sense :-/).
My script is as follow.
StudentModel:
public class StudentData
{
public string id { get; set; }
public string name { get; set; }
public string course { get; set; }
public string schoolclass { get; set; }
public string profileImage { get; set; }
}
CarouselPart:
ObservableCollection<StudentData> collection = new ObservableCollection<StudentData>();
collection.Add(new StudentData { name = "Soren Hanson", schoolclass = "4. grade", course = "Math" });
collection.Add(new StudentData { name = "Michael Trane", schoolclass = "7. grade", course = "English" });
collection.Add(new StudentData { name = "Tammy Jump", schoolclass = "1. grade", course = "English" });
DataTemplate template = new DataTemplate(() =>
{
var imageBtn = new Button();
imageBtn.Image = "Images/default.png";
imageBtn.Clicked += delegate {
// ADDING THE CURRENT STUDENT TO MY CURRSTUDENT OBJECT //
//App.currStudent = collection.......
var menteeOptions = new MenteeOptions();
imageBtn.Navigation.PushAsync(menteeOptions);
}
}
carousel.ItemTemplate = template;
carousel.PositionSelected += pageChanged;
carousel.ItemsSource = collection;
Hoping for help with this and thanks in advance :-)
Well you question was quite confusing at first but I will answer it from what I understood you want to keep the selected student object with you on your subpages:
So you can either use SQLite for it which can be found here
Or you can just maintain it as a static system object on your app.xaml.cs and use it
Something like this
App.Xaml.cs
public static object YourDataHolder {get; set;}
In your clicked event:
imageBtn.Clicked += delegate {
// ADDING THE CURRENT STUDENT TO MY CURRSTUDENT OBJECT //
App.YourDataHolder = _yourCollection;
var menteeOptions = new MenteeOptions();
imageBtn.Navigation.PushAsync(menteeOptions);
}
The use it something like this :
fooObject=App.YourDataHolder as FooType;

Elasticsearch NEST partial update of anonymous object with an empty nested object

Trying to "reset" a nested object in a document but it will not set it back to empty.
I have a POCO:
public class StreetAddress
{
public int HouseNumber { get; set; }
public string Street { get; set; }
}
public class FullAddress
{
public string City{ get; set; }
public string State { get; set; }
public StreetAddress StreetAddress { get; set; }
public int Zip { get; set; }
public List<string> Codes { get; set; }
}
So currently if I created this new document for FullAddress with the StreetAddress already set the document looks like this when queried:
"_source": {
"city": "Los Angeles",
"state": "California",
"zip": 90019,
"streetAddress": {
"houseNumber": 1,
"street": "Apple Street"
},
"codes" : [
{ "la-601" }
]
}
Now I want to call NEST client update to reset the StreetAddress nested object:
StreetAddress localStreetAddress = new StreetAddress();
var partialAddress = new
{
City = "NewCity",
Zip = 11111,
StreetAddress = localStreetAddress
};
this._client.Update<ElasticsearchProject, object>(update => update
.Id(1)
.Doc(partialAddress)
);
The final outcome I was hoping for when I query would be after the above update call:
"_source": {
"city": "NewCity",
"state": "California",
"zip": 11111,
"streetAddress": {}
}
However, the partial update does two things that is undesired:
It only updates the City and Zip fields and leaves the StreetAddress
as it was before and doesn't clear it out to empty or null.
It clears out the Codes list to empty since the partial update doesn't include the list.
I know I can set the StreetAddress to null and add the JSON property to include null like this:
[JsonProperty(NullValueHandling = NullValueHandling.Include)]
public StreetAddress StreetAddress { get; set; }
but all that would result is that the document update would set it to null and not empty and I am not sure if that is a desired result for the document:
"_source": {
"city": "NewCity",
"state": "California",
"zip": 11111,
"streetAddress": null
}
Not sure if there was a way to do a partial update without going down the script path to set the nested object back to empty.
Nested objects are actually mapped as separate hidden documents within Elasticsearch and as you have found, NEST by default is configured not to send through null values. the end result is that the nested type is not affected by your partial update to the top-level properties.
One way of handling this is to simply re-index the document again without the StreetAddress. An update is essentially a delete and insert behind the scenes anyway.
As an example
void Main()
{
var settings = new ConnectionSettings(new Uri("http://localhost:9200"), "address");
var client = new ElasticClient(settings);
// create the index
client.CreateIndex("address", c => c
.AddMapping<FullAddress>(m => m
.MapFromAttributes()
)
);
// index our document
client.Index(new FullAddress
{
City = "Los Angeles",
State = "California",
Zip = 90019,
Codes = new List<string>
{
"la-601"
},
StreetAddress = new StreetAddress
{
HouseNumber = 1,
Street = "Apple Street"
}
}, c => c.Id(1));
// Now at some later point, we need to update it,
// so get the current document
var response = client.Get<FullAddress>(1);
// make the changes to the current document
var address = response.Source;
address.StreetAddress = null;
// and re-index
client.Index<FullAddress>(address, c => c.Id(1));
}
public class StreetAddress
{
public int HouseNumber { get; set; }
public string Street { get; set; }
}
public class FullAddress
{
public string City { get; set; }
public string State { get; set; }
public StreetAddress StreetAddress { get; set; }
public int Zip { get; set; }
public List<string> Codes { get; set; }
}
The end result is as expected
{
"_index": "address",
"_type": "fulladdress",
"_id": "1",
"_version": 2,
"found": true,
"_source": {
"city": "Los Angeles",
"state": "California",
"zip": 90019,
"codes": [
"la-601"
]
}
}
You may also want to use optimistic concurrency control when doing this too, to handle any changes between the get and index.

Use knockout.js for 4 cascading dropdowns based on a hierarchy of objects

I am trying to get four cascading dropdowns using knockout.js:
Search Criteria
Sub Criteria
Value
State
I was able to get the first cascade going (but not the others due to databinding issues) by using code from the following link:
http://blogs.msdn.com/b/thebeebs/archive/2011/12/01/price-calculator.aspx
The data for these dropdowns is being returned to my razor viewpage as an IEnumrable of SearchCriterion from an MVC view using ViewBag.CriteriaData variable. The code for my classes is as follows:
public class SearchCriterion
{
public string Text { get; set; }
public string Value { get; set; }
public List<SubCriterion> SubCriteria { get; set; }
}
public class SubCriterion
{
public string SearchCriterionValue { get; set; }
public string Text { get; set; }
public string Value { get; set; }
public List<ColumnValue> ColumnValues { get; set; }
}
public class ColumnValue
{
public string SearchCriterionValue { get; set; }
public string SubCriterionValue { get; set; }
public string Text { get; set; }
public string Value { get; set; }
public IEnumerable<StateValue> StateValues { get; set; }
}
public class StateValue
{
public string SearchCriterionValue { get; set; }
public string SubCriterionValue { get; set; }
public string ColumnValue { get; set; }
public IEnumerable<int> InputStateIds { get; set; }
public IEnumerable<int> OutputStateIds { get; set; }
public int SelectedInputStateId { get; set; }
public int SelectedOutputStateId { get; set; }
public string Text { get; set; }
public string Value { get; set; }
}
The issues I am facing are in the following portions of the .cshtml code:
What do I specify in this template for the other two dropdowns. e.g. the third dropdown needs to be bound to ColumnValue.Value (ColumnValue is part of SubCriterion)
<script id='criteriaRowTemplate' type='text/html'>
<tr>
<td><select data-bind='options: criteriaData, optionsText: "Text", optionsCaption: "Search Criterion", value: SearchCriterion' /></td>
<td><select data-bind='visible: SearchCriterion, options: SearchCriterion() ? SearchCriterion().SubCriteria : null, optionsText: "Text", optionsCaption: "Sub Criterion", value: SubCriterion' /></td>
<td><select data-bind='visible: SubCriterion, options: SubCriterion() ? SubCriterion().ColumnValues : null, optionsText: "Text", optionsCaption: "Column Value", value: ColumnValue'/></td>
<td><select data-bind='visible: ColumnValue, options: ColumnValue() ? ColumnValue().StateValues : null, optionsText: "Text", optionsCaption: "State", value: StateValue'/></td>
<td><button data-bind='click: function() { viewModel.removeLine($data) }'>Remove</button></td>
</tr>
</script>
Is this correct?
var CriteriaLine = function() {
this.SearchCriterion = ko.observable();
this.SubCriterion = ko.observable();
this.ColumnValue = ko.observable();
this.StateValue = ko.observable();
// Whenever the Search Criteria changes, reset the Sub Criteria selection
this.SearchCriterion.subscribe(function() { this.SubCriterion(undefined); }.bind(this));
this.SubCriterion.subscribe(function() { this.ColumnValue(undefined); }.bind(this));
this.ColumnValue.subscribe(function() { this.StateValue(undefined); }.bind(this));
};
How do I map the complete C# object with the Javascript object? It works if we just have the first two dropdowns:
// Create a Javascript object object with the same property names as the C# object
var dataToSearch = $.map(this.lines(), function (line) { return line.StateValue() ? line.StateValue() : undefined; });
var SearchObject = new function () {
this.StateValues = dataToSearch;
};
// Convert the object to JSON
var searchCriteria = JSON.stringify(SearchObject);
Does anything need to change here for the binding?
// Apply the data from the server to the variable
var criteriaData = #Html.Raw(#Json.Encode(ViewBag.CriteriaData));
var viewModel = new Criteria();
ko.applyBindings(viewModel, document.getElementById("criteriaDiv"));
EDIT:
I am now able to populate the cascading dropdowns (updated code above). Now I have 4 columns, each column having one of the dropdowns. I also have 1...n number of rows being added dynamically by using Knockoutjs. So, the user can now select values from these dropdowns and add more rows of dropdowns if he wants. The only thing remaining is to return the values that the user selects for the dropdowns to the controller(point 3 above). I am not sure how I can do that. Any help would be appreciated.
EDIT 2:
Added working code for Item # 3 and modified the ColumnValue and StateValue classes.
I'm not sure I fully understand your question, but I'm going to take a whack at it anyway :). I think you're looking for a way to "validate" if it is in fact time to allow the next drop down to be active?
If so, you could approach it from a standpoint of Computed Observables. Basically, you would bind each of your dropdowns to a computed value which is derived from the previous dependencies.
Let me write fiddle and I'll show you :)
OK, give this a shot...sorry for the delay...http://jsfiddle.net/farina/ZNBcM/3/
I update the answer, Hope, it will help new Comers.
Methods for Binding Hierarchical Dropdowns using Knockout JS in MVC
Here you can find the good example .

WebGrid grid.Columns Format Error

#{
var grid = new WebGrid(Model.Auctions, rowsPerPage: Model.PagingInfo.ItemsPerPage, defaultSort: "AddedDate");
}
#grid.GetHtml(
columns: grid.Columns(
**grid.Column(columnName: "", header: "Type", format: (auction) => AuctionListViewModel.GetAuctionType(auction)),**
grid.Column(columnName: "OwnerReference", header: "Owner reference")
)
);
public class AuctionListViewModel
{
public IEnumerable<Auction> Auctions { get; set; }
public IEnumerable<Item> Items { get; set; }
public PagingInfo PagingInfo { get; set; }
public string Title { get; set; }
public string Action { get; set; }
public static string GetAuctionType(Auction auction)
{
var type = string.Empty;
if (auction is LubAuction)
{
type = "Lowest unique wins";
}
else if (auction is EsfAuction)
{
type = "Highest wins";
}
return type;
}
}
With the above view code and model, get the following error on the line marked in bold, why is this?
The best overloaded method match for 'UI.Models.AuctionListViewModel.GetAuctionType(UI.AuctionService.Auction)' has some invalid arguments
In the grid.Column method's format parameter's parameter (in your case auction) you get the actual item (an Auction) but it's wrapped into a dynamic wrapper called WebGridRow.
You can use your properties on this wrapper and it delegates to the actual item e.g: auction.Title will work, but if you want to get the whole item (the Auction) you need to use the Value property of the WebGridRow.
format: auction =>
uctionListViewModel.GetAuctionType(((WebGridRow)auction).Value)
Due to the dynamic (weak) typing of the WebGrid helper you need a cast:
grid.Column(
columnName: "",
header: "Type",
format: (auction) => AuctionListViewModel.GetAuctionType((Auction)auction.Value)
)
I would recommend you using better grid helpers such as MvcContrib Grid and Telerik Grid which will give you strong typing and compile time safety.

Render Partial View from within a System.Web.Helpers.WebGrid

I am trying to render a Partial from within a System.Web.Helpers.WebGrid
my model class looks like this:
class GameInfo
{
public List<AppUser> Team1 { get; set; }
public List<AppUser> Team2 { get; set; }
// and more properties
}
class AppUser
{
public string PictureUrl { get; set; }
public string ProfileUrl { get; set; }
public long GamesWon { get; set; }
public long GamesLost { get; set; }
public int Points { get; set; }
// and more properties
}
I want my GridView to show a list of GameInfo's in my grid view.
What is turning out be to be tougher than expected is rendering the Teams (List).
To stay DRY I created a partial view to render a Team (_Team.cstml).
This is my razor code:
#if (Model != null)
{
var webgrid = new WebGrid(source: Model.Games,
rowsPerPage: 10);
<div id="grid">
#webgrid.GetHtml(
columns: webgrid.Columns(
webgrid.Column(header: "Score", format: #<text>#item.Score1/#item.Score1</text>),
webgrid.Column(header: "Team 1", format: (item) =>
{
return "hello sb"; // this line works!
//return Html.Partial("_Team", item.Team1); // this gives an error
})
)
)
</div>
}
Any idea how I can get this to work?
Thank you!
In case someone else runs into this, I managed to solve it this morning.
This works:
webgrid.Column(header: "Team 1", format: (item) =>
{
List<Cuarenta.Web.Models.AppUser> team = ((Cards.Cloud.WebRole.Admin.GameInfo)item.Value).Team1;
return Html.Partial("_Team", team);
})

Resources