Scalajs-react: How to invoke a custom method on button click? - scala

I am trying to build a HTML page for my simple application using scalajs-react, and here is my effort:
<.div(
<.p("Welcome to foodland!"),
<.form(
<.label("Name of the recipe:",
<.input.text(^.name := "nameOfTheRecipe")),
<.br,
<.label("How to make it:",
<.textarea(^.name := "steps")),
<.br,
<.input.submit(^.value:="Submit")
)
)
I have put this in my Scala file, and it works fine.
Now I want to invoke a method when user clicks on 'Submit' button, wherein I would like to access the input fields in the form.
How do we do that? I tried with
<.input.submit(^.value:="Submit",onClick = handleClick)
..where handleClick is defined in the same Scala file as above.
But it doesn't work.

For attributes that are events, in scalajs-react you have to use Callbacks. Good entry points in the documentation are:
VDOM Event Handlers
Callback creation
In your case, it would look like the following:
def buildDiv = {
val div = <.div(
<.p("Welcome to foodland!"),
<.form(
<.label("Name of the recipe:",
<.input.text(^.name := "nameOfTheRecipe")),
<.br,
<.label("How to make it:",
<.textarea(^.name := "steps")),
<.br,
<.input.submit(^.value:="Submit", ^.onClick --> handleClick)
)
)
}
val handleClick = Callback {
println("the button was clicked")
}

Related

Scala swing wait for a button press after pressing a button

So What I want to do is to press a button and inside the ButtonClicked Event I want to wait completing the event, until I press a specific button/one of the specified buttons.
I also know that there are some similar questions to this topic but I wasnt able to find a fix out of the answers
So basically this:
reactions += {
case event.ButtonClicked(`rollDice`) =>
some code ...
wait for one of the specified buttons to be pressed and then continue
some code ...
Is there an easy way to solve this problem without the use of threads?
There are certainly some abstractions you could set up around the event layer, but you asked for "easy", so my suggestion is to set a flag/state when the dice are rolled, and then check for that flag in the other buttons' event handler.
private var postDiceRollState: Option[InfoRelatedToRollDice] = None
reactions += {
case event.ButtonClicked(`rollDice`) =>
// capture whatever info you need to pass along to the later button handler
val relevantInfo = /* some code... */
// store that info in the "state"
postDiceRollState = Some(relevantInfo)
case event.ButtonClicked(other) if isPostDiceRollButton(other) =>
// when the other button gets clicked, pull the relevant info from your "state"
postDiceRollState match {
case Some(relevantInfo) =>
postDiceRollState = None // clear state
doInterestingStuff(relevantInfo) // "some code..."
case None =>
// nothing happens if you didn't roll the dice first
}
}
Note: I represented the "flag" as an Option, under the assumption that you might have some information you want to capture about the rollDice event. If you don't actually have anything to put in there, you could represent your state as private var didRollDice: Boolean = false and set/clear would be setting it to true/false respectively.

How can I dynamically create HTML components in Marko?

I want to create new Marko components every time the user clicks a button — by calling something like the JavaScript DOM method document.createElement("tag"). How can I do this in Marko, not just with ordinary HTML tags, but with custom Marko tags?
What I tried: document.createElement("custom-marko-component")
Expected behavior: Marko engine compiles a new instance of the custom component.
Actual behavior: The browser makes a useless new <custom-marko-component></custom-marko-component>.
Use Marko's rendering functions (documentation: https://markojs.com/docs/rendering/):
Example:
// Create the custom component, like document.createElement() but asynchronous.
// Import `./custom-marko-component.marko`
var customComponent = require("./custom-marko-component");
var resultPromise = customComponent.render({});
// Insert the custom component into the webpage.
resultPromise.then(result => {
result.appendTo(document.body);
});

Check if the user has clicked inside a specific div in a component

I have got a global click event in my application.
host: {
'(document:click)': 'handleClick($event)',
},
Everytime the user clicks, my handleClick function will execute. I want to check if the user has clicked in a specific div. I've tried with the following:
handleClick(event){
console.log(event.target===document.getElementsByClassName("drop_down_wrapper")
}
But this does not work. I've also tried to get hold of a specific div by using ElementRef but I only managed to get hold of the native element of my div. Any suggestions?
First of all, getElementsByClassName returns a HtmlCollection, so you might want to use index to access individual items:
handleClick(event) {
console.log(event.target === document.getElementsByClassName("drop_down_wrapper")[0]
}
Or better use querySelector method:
handleClick(event) {
console.log(event.target === document.querySelector(".drop_down_wrapper")
}
However, if your div have inner HTML you would probably need to check if the clicked target is a child of the div or not:
var target = e.target;
var wrapper = document.querySelector(".drop_down_wrapper");
while (target != wrapper && target !== document) {
target = target.parentNode;
}
Add a click handler to the particular div you want to listen on, and add
event.stopPropogation();
This should prevent your document click handler from firing.

Validate only parts of a form

I have a rather large form, that I've broken up into individual sections with "Next" and "Back" buttons for the user to navigate each step.
Here is how I set it up to work for now:
Each section is within its own xp:panel
Each xp:panel is hidden with "display:none". The transition happens when the user clicks on "Next" or "Back" by using JQuery's fade animation
What I am trying to do is, if the user clicks on "Next" I would like to validate only the fields in the current, visible section. If the validation fails, don't transition to the next step. If the validation passes, transition to the next step.
Right now, when I click on the "Next" button, every field is being validated and the transition doesn't happen.
Is there a way, that I can only validate the fields in a certain section or would I have to use something like Don Mottolo's code snippet: http://openntf.org/XSnippets.nsf/snippet.xsp?id=ssjs-form-validation-that-triggers-errormessage-controls
Thank you for your help!
P.S.: I know I could use the CSJS portion of the onClick event of the button to run some validation, but I'd like to use the "Display Error" controls.
You could look at computing the required attribute of each control that is to be validated and have that check which button is submitting the form. Tommy Vaaland has a function that does this:
// Used to check which if a component triggered an update
function submittedBy( componentId ){
try {
var eventHandlerClientId = param.get( '$$xspsubmitid' );
var eventHandlerId = #RightBack( eventHandlerClientId, ':' );
var eventHandler = getComponent( eventHandlerId );
if( !eventHandler ){ return false; }
var parentComponent = eventHandler.getParent();
if( !parentComponent ){ return false; }
return ( parentComponent.getId() === componentId );
} catch( e ){ /*Debug.logException( e );*/ }
}
Link: http://dontpanic82.blogspot.co.uk/2010/03/xpages-making-validation-behave.html
You can look at using a client side form validation framework such as Parsley: http://parsleyjs.org
This can of course be combined with server side validation for at least the final submission.

Customize or change default message boxes issued by workflow dialogs on errors in Alfresco

Presently, a messagebox appears with the failing class name:
Is it possible to override the default behavior in Alfresco? Could we use forms service to present a different message ?
Additional to zladuric answer,
you can use failureCallback method to show message what you want.
But it is difficult to search failureCallback method of workflow forms for a new one because workflow forms such as "Start Workflow", "Task Edit", "Task Detail" are used form engine.
For example, in "Start Workflow" form, you can add our own successCallBack and failureCallBack by writing onBeforeFormRuntimeInit event handler in start-workflow.js like this.
onBeforeFormRuntimeInit: function StartWorkflow_onBeforeFormRuntimeInit(layer, args)
{
var startWorkflowForm = Dom.get(this.generateId + "-form");
Event.addListener(startWorkflowForm, "submit", this._submitInvoked, this);
args[1].runtime.setAJAXSubmit(true,
{
successCallback:
{
fn: this.onFormSubmitSuccess,
scope: this
},
failureCallback:
{
fn: this.onFormSubmitFailure,
scope: this
}
});
}
onFormSubmitSuccess: function StartWorkflow_onFormSubmitSuccess(response)
{
this.navigateForward(true);
// Show your success message or do something.
}
onFormSubmitFailure: function StartWorkflow_onFormSubmitFailure(response)
{
var msgTitle = this.msg(this.options.failureMessageKey);
var msgBody = this.msg(this.options.failureMessageKey);
// example of showing processing response message
// you can write your own logic
if (response.json && response.json.message)
{
if(response.json.message.indexOf("ConcurrencyFailureException") != -1)
{
msgTitle = this.msg("message.concurrencyFailure");
msgBody = this.msg("message.startedAgain");
}
else
msgBody = response.json.message;
}
Alfresco.util.PopupManager.displayPrompt(
{
title: msgTitle,
text: msgBody
});
}
Since Alfresco.component.StartWorkflow(in start-workflow.js) extends Alfresco.component.ShareFormManager(in alfresco.js). You can override onBeforeFormRuntimeInit event in start-workflow.js. I hope this your help you.
I'm not looking at the code right now, but this looks like a regular YUI dialog. So it's fired by YUI. So this YUI is client side, probably in My-tasks dashlet or my tasks page.
Furthermore, the error message looks like it is a status.message from the failed backend message/service.
You could probably locate that client-side javascript file, find the method that starts the task and see what its' failureCallback handler is. Then edit that failureCallback method and make it show something different then the response.status.message or whatever it is. Perhaps something like this.msg("message.my-custom-error-message"); which you then customize on your own.
Modifying YUI dialog scripts will might affect the other functionalities as well.
If we customize start-workflow. js, its only going to be achieved in start workflow form.
So as generic solution, below is the suggestion.
When alfresco is rendering the workflow form , it is rendering the transition button using the activiti-transition.js file.Basically this buttons are doing nothing more but submitting the workflow form.
So the best way would be , customizing this activiti-transition.ftl and activiti-transition.js file , to make an ajax call and handle the response as we want.
I just had a look on full flow of how this front end error is shown.
activiti-transition is submiting the workflow form.
Using a function named as submitForm which resides inside alfresco.js, it is invoking an submit event of form
Inside the forms-runtime.js file there is one function named as _submitInvoked(handles the submit event of form), which is responsible for making an ajax call and submitting the workflow form.If there is error while submitting , it will display the error which is from backend.