Is there a way to use ChoicePrompt without validating choices? - botframework

I would like to present the user with a series of choices, but also allow them to type in freeform text. The Choice prompt will automatically reprompt until a choice or synonym is chosen.
The RecognizerOptions NoValue and/or NoAction appear to be related to this, but I haven't been able to find good documentation on them. Setting them to true doesn't work.
AddDialog(new ChoicePrompt(promptForChoice) { RecognizerOptions = new FindChoicesOptions() { NoValue = true, NoAction = true } });
I've also tried creating an "anything" validator that always returns true.
AddDialog(new ChoicePrompt(promptForChoice, validator: AnythingValidator.AnythingValidatorAsync) { RecognizerOptions = new FindChoicesOptions() { NoValue = true, NoAction = true } });
public static Task<bool> AnythingValidatorAsync(PromptValidatorContext<FoundChoice> promptContext, CancellationToken cancellationToken)
{
return Task.FromResult(true);
}
This allows the prompt to exit, but the result is null. I can go dig out what the user entered from the Context.Activity.Text but that doesn't seem like a very robust solution.
There seems to be something obvious I'm missing with PromptChoice

Choices work by hardcoding in the options the user needs to choose from. We can't implement freeform text in choices. What you can do is, add another choice "others" in the choice list and implement a waterfall to get the user input. Also, you can't use the RecognizerOptions as they are related to synonyms.

Related

Cypress: Is there a way to write a command that can check a checkbox using UI

Currently, I am following cypress's example below and it works perfectly for the type commands. However, I have too many commands and I am trying to condense whichever ones I can. In this case, I only need to be able to check a box in certain tests but not sure how I would go about it. Any hints/ tips/ advice would be greatly appreciated. :)
Cypress.Commands.add('typeLogin', (user) => {
cy.get('input[name=email]').type(user.email)
cy.get('input[name=password]').type(user.password)
cy.get('input[name=checkbox]').check(user.checkbox)?
})
In the test:
const user = { email: 'fake#email.com', password: 'Secret1' }';
cy.typeLogin(user ) => {...
You can provide the checkbox to use (or whether to check/not check the checkbox) in your data model. From there, you have a few different options, depending on how your application is set up. If you only have one checkbox that should either be checked or not checked, you can use a boolean (useCheckbox below). If there are several values to choose from, you could pass in a string to use (this could be either a selector, if there are different selectors, or a value, if it is the same selector with different values).
const user = { email: 'foo#foo.com', password: 'foo', useCheckbox: true, checkbox: 'bar' }
...
Cypress.Commands.add('typeLogin', (user) => {
cy.get('input[name=email]').type(user.email)
cy.get('input[name=password]').type(user.password)
// we could use either `useCheckbox` to evaluate a boolean
if (user.useCheckbox) {
cy.get('input[name=checkbox]').check()
}
// or we could use the the `checkbox` field if we always to to check, we're just unsure of the value.
// in the first case, `user.checkbox` is the selector to use
cy.get(user.checkbox).check()
// in this second case, `user.checkbox` is the value to select
cy.get('input[name=checkbox]').check(user.checkbox)
})

How to remove tooManyAttempts message in Prompt.Choice? How to accept text in Prompt.Choice that is not in list of options? C#

I'm using bot-Framework SDK3 C#.
I want to allow user input anything which is not in "PromptDialog.Choice"'s options. Any better ways to recommend?
This is my code.
private async Task SelectCategory(IDialogContext context)
{
List<string> options = new List<string>();
options = category.Keys.ToList();
options.Add("Category1");
options.Add("Category2");
options.Add("Category3");
PromptOptions<string> promptOptions = new PromptOptions<string>(
prompt: "which one do you prefer?",
tooManyAttempts: "",
options: options,
attempts: 0);
PromptDialog.Choice(context: context, resume: ResumeAfterSelectCategory, promptOptions: promptOptions);
await Task.FromResult<object>(null);
}
private async Task ResumeAfterSelectCategory(IDialogContext context, IAwaitable<string> result)
{
try
{
selected = await result;
}
catch (Exception)
{
// if the user's input is not in the select options, it will come here
}
}
But the problem is it always send the message "tooManyAttempts". If I set it to empty, I will send me "0".
I suppose you are using NodeJS. You can use the simple builder.Prompts.choice with maxRetries set to 0. Here is a sample snippet. It asks user to choose some option from a list, or they can enter something which is not in the list.
If you are using C# SDK, you can find some similar option for the list.
bot.dialog("optionalList", [
function(session){
builder.Prompts.choice(
session,
"Click any button or type something",
["option1", "option2", "option3"],
{maxRetries: 0} // setting maxRetries to zero causes no implicit checking
)
},
function(session, result){
// something from the list has been clicked
if(result.response && result.response.entity){
console.log(result.response.entity); // use the clicked button
} else {
console.log(session.message.text) // if user entered something which is not in the list
}
}
]);
EDIT 1:
Hi, Saw that you are using C# SDK. I am not that proficient with that but I can give you some suggestion.
The list which you generate in the async task SelectCategory you can generate in some other place, which is also accessible to the second async task ResumeAfterSelectCategory, (like making it a class variable or getting from database).
Now that the list is accessible in the 2nd task, you can compare what user has typed against the list to determine if the message is from the list or not.
If message is something from the list, then take action accordingly, otherwise user has entered something which is not in the list, and then take action accordingly.
Your 2nd problem is
And if user typed, it will show a message "you tried to many times"
What is meant by that? Does bot sends "you tried to many times" to the bot visitor. In which case it could be the behavior of library. You will be able to control that only if library provides some option. Else I don't know. Hope, that helps
EDIT 2:
I came across this SO question Can I add custom logic to a Bot Framework PromptDialog for handling invalid answers?
You can use that questions answer. Basically extending PromptDialog.PromptChoice<T>.Here is an example.
Override TryParse method like this
protected override bool TryParse(IMessageActivity message, out T result)
{
bool fromList = base.TryParse(message, out result);
if (fromList)
{
return true;
} else {
// do something here
return true; // signal that parsing was correct
}
}
I used node.js and to get message which user entered. use this code snippet.
(session, args) => {
builder.Prompts.text(session, "Please Enter your name.");
},
(session, args) => {
session.dialogData.username = args.response;
session.send(`Your user name is `${session.dialogData.username}`);
}

Android Databinding TextEdit Validation of two or more fields enable button

I am working on my first Android application using MVVM and Databinding. Some areas I am grasping but this one I am struggling with. The scenario:
I have a Create Account wizard activity, which uses Android Navigation Architecture to page through several fragments asking for input from the user. The first fragment/step asks the user for the first and last name. I do not want the button to proceed to the next step to enable until something is entered in both fields. I have enabled buttons based on ONE fields validation before, but not two. I feel like I am missing something silly.
Here is the button I want to enable after both fields have data in them:
<Button
android:id="#+id/continueToSecondStepButton"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="32dp"
android:background="#drawable/button_transparent_background"
android:onClick="#{() -> viewModel.proceedToNextStep()}"
android:text="#string/step_proceed"
android:enabled="#{safeUnbox(viewModel.firstNamesValid) && safeUnbox(viewModel.lastNamesValid)}"
android:textAllCaps="false"
android:textColor="#{safeUnbox(viewModel.firstNamesValid) && safeUnbox(viewModel.lastNamesValid) ? #colorStateList/white : #colorStateList/transparent_white}"
android:textSize="18sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/subtext" />
Primarily here the focus is:
android:enabled="#{safeUnbox(viewModel.firstNamesValid) && safeUnbox(viewModel.lastNamesValid)}"
android:textColor="#{safeUnbox(viewModel.firstNamesValid) && safeUnbox(viewModel.lastNamesValid) ? #colorStateList/white : #colorStateList/transparent_white}"
I have two Transformations to listen to key changes on the first and last name fields and execute the validation method:
firstNamesValid = Transformations.switchMap(firstName) { firstName -> isNamesValid() }
lastNamesValid = Transformations.switchMap(lastName) { lastName -> isNamesValid() }
and for now, just a simple method to check if both fields have data in them:
private fun isNamesValid(): LiveData<Boolean> {
var namesValid = false
if (!firstName.value.isNullOrEmpty() && !lastName.value.isNullOrEmpty()) {
namesValid = true
}
val mediatorLiveData: MediatorLiveData<Boolean> = MediatorLiveData()
mediatorLiveData.value = namesValid
return mediatorLiveData
}
It "kind of" works but not well. Can you enable a button based on validating two fields with Databinding? I have a feeling there is an easier way to do this. What happens with this setup is that, you fill out first and last name, and nothing happens, but if you then navigate back to the first name and enter another character it works and enables the button. I assume this is due to some logic error in my code. Thanks for looking.
I ended up figuring it out right after. Typing it up helped me find the issue and I will share what I did for others. I had a logic error in my code referencing the isNamesValid function.
I made the following changes:
isNamesValid now takes a string parameter and checks the specific string rather then hardcoded checking the first and last name values
private fun isNameValid(name: String?): LiveData<Boolean> {
var namesValid = false
if (!name.isNullOrEmpty()) {
namesValid = true
}
val mediatorLiveData: MediatorLiveData<Boolean> = MediatorLiveData()
mediatorLiveData.value = namesValid
return mediatorLiveData
}
Updated Transformations to call that method passing in the name to be checked.
firstNamesValid = Transformations.switchMap(firstName) { firstName -> isNameValid(firstName) }
lastNamesValid = Transformations.switchMap(lastName) { lastName -> isNameValid(lastName) }

how to get access to current MessageActivity from PromptDialog choice in BotFramework

I'm presenting the user with 4 choices via the PromptDialog.choice method. In my Resume method, I want to forward the user to a dialog to handle their choices. I no longer have access to the current MessageActivity object
and wonder what my options are? I'd like to pass the original Message if at all possible. Passing an empty one seems like a hack. And the dialog PrintGraphicDialog will just display a graphic image and return back to the 4 choices. using Context.Call hits the PrintGraphicDialog's StartAsync method and has the context.wait() call which requires the user to type something. Then it prints the graphic. Not quite what is wanted either.
private void ShowOptions(IDialogContext context)
{
PromptDialog.Choice(context, this.OnOptionSelected, new List<string>() { OptionOne, OptionTwo, OptionThree, OptionFour }, "Please select from the following options:", "Not a valid option", 3);
}
private async Task OnOptionSelected(IDialogContext context, IAwaitable<string> result)
{
try
{
string optionSelected = await result;
switch (optionSelected)
{
case OptionOne:
await context.Forward(new PrintGraphicDialog(), this.ResumeAfterOptionDialog, context.MakeMessage(), CancellationToken.None);
break;
case OptionTwo:
break;
case OptionThree:
break;
case OptionFour:
break;
}
}
catch (TooManyAttemptsException ex)
{
}
}
You can store the message in a variable right before calling your PromptDialog.Choice and then use it in the Resume method.
Since the dialog I was forwarding control to really only needs a few properties from the original message, I just created a class, stored them in there and before forwarding, did context.MakeMessage() and populated that with the stored off properties. Seems like there should be a better way.

JavaFX: Prevent selection of a different tab if the data validation of the selected tab fails

I'm creating a CRUD application that store data in a local h2 DB. I'm pretty new to JavaFX. I've created a TabPane to with 3 Tab using an jfxml created with Scene Builder 2.0. Each Tab contains an AncorPane that wrap all the controls: Label, EditText, and more. Both the TabPane and the Tabs are managed using one controller. This function is used to create and to update the data. It's called from a grid that display all the data. A pretty basic CRUD app.
I'm stuck in the validation phase: when the user change the tab, by selecting another tab, it's called a validation method of the corresponding tab. If the validation of the Tab fails, I want that the selection remains on this tab.
To achieve this I've implemented the following ChangeListener on the SelectionModel of my TabPane:
boolean processingTabValidationOnChange = false;
tabPane.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> ov, Number t, Number t1) {
if (processingTabValidationOnChange == false) {
boolean success;
switch (t.intValue()) {
case 0: success = validationTab1Passed();
break;
case 1: success = validationTab2Passed();
break;
case 1: success = validationTab3Passed();
break;
default: success = false;
}
if (success == false) {
processingTabValidationOnChange = true;
// select the previous tab
tabPane.getSelectionModel().select(t.intValue());
processingTabValidationOnChange = false;
}
}
}
});
I'm not sure that this is the right approach because:
The event changed is fired two times, one for the user selection and one for the .select(t.intValue()). To avoid this I've used a global field boolean processingTabValidationOnChange... pretty dirty I know.
After the .select(t.intValue()) the TabPane displays the correctly Tab as selected but the content of the tab is empty as if the AnchorPane was hidden. I cannot select again the tab that contains the errors because it's already selected.
Any help would be appreciated.
Elvis
I would approach this very differently. Instead of waiting for the user to select a different tab, and reverting if the contents of the current tab are invalid, prevent the user from changing tabs in the first place.
The Tab class has a disableProperty. If it is set to true, the tab cannot be selected.
Define a BooleanProperty or BooleanBinding representing whether or not the data in the first tab is invalid. You can create such bindings based on the state of the controls in the tab. Then bind the second tab's disableProperty to it. That way the second tab automatically becomes disabled or enabled as the data in the first tab becomes valid or invalid.
You can extend this to as many tabs as you need, binding their properties as the logic dictates.
Here's a simple example.
Update: The example linked above is a bit less simple now. It will dynamically change the colors of the text fields depending on whether the field is valid or not, with validation rules defined by bindings in the controller. Additionally, there are titled panes at the top of each page, with a title showing the number of validation errors on the page, and a list of messages when the titled pane is expanded. All this is dynamically bound to the values in the controls, so it gives constant, clear, yet unobtrusive feedback to the user.
As I commented to the James's answer, I was looking for a clean solution to the approach that I've asked. In short, to prevent the user to change to a different tab when the validation of the current tab fails. I proposed a solution implementing the ChangeListener but, as I explained: it's not very "clean" and (small detail) it doesn't work!
Ok, the problem was that the code used to switch back the previous tab:
tabPane.getSelectionModel().select(t.intValue());
is called before the process of switching of the tab itself it's completed, so it ends up selected... but hidden.
To prevent this I've used Platform.runLater(). The code .select() is executed after the change of tab. The full code becomes:
//global field, to prevent validation on .select(t.intValue());
boolean skipValidationOnTabChange = false;
tabPane.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> ov, Number t, Number t1) {
if (skipValidationOnTabChange == false) {
boolean success;
switch (t.intValue()) {
case 0:
success = validationTab1Passed();
break;
case 1:
success = validationTab2Passed();
break;
case 1:
success = validationTab3Passed();
break;
default:
success = false;
}
if (success == false) {
Platform.runLater(new Runnable() {
#Override
public void run() {
skipValidationOnTabChange = true;
tabPane.getSelectionModel().select(t.intValue());
skipValidationOnTabChange = false;
}
});
}
}
}
});
Anyway, if anyone has a better solution to accomplish this, you're welcome. In example using a method like consume() to prevent the tab to be selected two times. This way I can eliminated the global field skipValidationOnTabChange.
Elvis
I needed to achieve the similar thing. I've done this by changing the com.sun.javafx.scene.control.behavior.TabPaneBehaviour class by overriding selectTab method:
class ValidatingTabPaneBehavior extends TabPaneBehavior {
//constructors etc...
#Override
public void selectTab(Tab tab) {
try {
Tab current = getControl().getSelectionModel().getSelectedItem();
if (current instanceof ValidatingTab) {
((ValidatingTab) current).validate();
}
//this is the method we want to prevent from running in case of error in validation
super.selectTab(tab);
}catch (ValidationException ex) {
//show alert or do nothing tab won't be changed
}
}
});
The ValidatingTab is my own extension to Tab:
public class ValidatingTab extends Tab {
public void validate() throws ValidationException {
//validation
}
}
This is the "clean part" of the trick. Now we need to place ValidatingTabPaneBehavior into TabPane.
First you need to copy (!) the whole com.sun.javafx.scene.control.skin.TabPaneSkin to the new class in order to change its constructor. It is quite long class, so here is only the part when I switch the Behavior class:
public class ValidationTabPaneSkin extends BehaviorSkinBase<TabPane, TabPaneBehavior> {
//copied private fields
public ValidationTabPaneSkin(TabPane tabPane) {
super(tabPane, new ValidationTabPaneBehavior(tabPane));
//the rest of the copied constructor
}
The last thing is to change the skin in your tabPane instance:
tabPane.setSkin(new ValidationTabPaneSkin(tabPane));

Resources