Migrate Page.getCurrent().getJavaScript().addFunction to Vaadin Flow - javascript

I have the following Vaadin 8 code:
Page.getCurrent().getJavaScript().addFunction("getDocument", args -> {
...
//java rest call
...
});
getDocument is called in javascript code.
window.documentSign = function(signerUrl,procedure) {
....
getDocument(signerRequest.responseText,procedure,messageId);
....
}
How can I migrate this piece of code to Vaadin Flow 14?
Thank you in advance.

addFunction in Vaadin 8 provides a way for JavaScript to send data back to the server.
I guess this isn't a one-time case that can be handled by returning a value (or a Promise) through executeJs. I also guess this case isn't related to a component class where you can define a #ClientCallable method in Java and access that from JS through element.$server.
What remains is firing a custom DOM event on the <body> element and listening for that event on the UI element through Java.
Firing a event from JS:
document.body.dispatchEvent(new CustomEvent('event-name', { detail: 'String from JS' }));
Listening for the event in Java:
ui.getElement().addEventListener("event-name", event -> {
System.out.println(event.getEventData().getString("event.detail"));
}).addEventData("event.detail");

Simply use:
UI.getCurrent().getPage().executeJs("getDocument", <args as varargs>)

Related

JavaScript runtime error: Object doesn't support property or method 'get_items'

$(window).load(function () {
var combo = $find("<%=ComboBox.ClientID%>");
var items = combo.get_items();
}
As per the above code in JS, I receive the Error as JavaScript runtime error: Object doesn't support property or method 'get_items'
I am using Telerik RadcomboBox.
This issue exist only in window.load() Code Block.
During window.Load() I a getting var combo = $find("<%=ComboBox.ClientID%>"); as Null only in IE
Using native DOM events (like window.load or jQuery's $(document).ready may not give you the expected results. Such events are usualy prior to the Sys.Application.load event and cannot guarantee that the control instances are initialized.
The UI for ASP.NET AJAX controls are created by implementing the IScriptControl interface and have the lifecycle of MS AJAX-based controls. Thus, they are initialized during the Sys.Application.init event and the instance of the control can be accessed during the Sys.Application.load event at the earliest. You can see how in the Important MS AJAX Events section.
Check out this article for more info: https://docs.telerik.com/devtools/aspnet-ajax/general-information/get-client-side-reference

How to do client-side UI events in Blazor

I just started playing around with Blazor and I can already see the great potential of this new framework.
I'm wondering, though, how it will handle doing simple things like setting focus on an input control? For instance, after I handle a click event, I want to set the focus to a text input control. Do I have to use JQuery for something like that, or will Blazor have some built-in methods for that sort of thing?
Thanks
Update: I posted an answer below with an example of how to set the focus to a control by invoking a JavaScript function from the .Net code.
As of right now (Blazor 0.9.0) you create your JavaScript functions in the Index.html (or reference them from Index.html) and then in your Blazor page or component you call JsRuntime.InvokeAsync("functionName", parms);
https://learn.microsoft.com/en-us/aspnet/core/razor-components/javascript-interop
Blazor is just the replacement (to be more precise "value addition") to JavaScript. It is a client-side only solution (but it might add some easy binding to ASP.NET in the future).
Still, it's completely based on HTML and CSS. C# is replacing the JS part using web assembly. So nothing has changed on how you access / modify HTML controls.
As of now (version 0.1.0) you have to rely on HTML DOM focus() Method to do what you intend to do (yes you have to use JavaScript as of now :( ).
// Not tested code
// This is JavaScript.
// Put this inside the index.html. Just below <script type="blazor-boot"></script>
<script>
Blazor.registerFunction('Focus', (controlId) => {
return document.getElementById(controlId).focus();
});
</script>
//and then wrap it for calls from .NET:
// This is C#
public static object Focus(string controlId)
{
return RegisteredFunction.Invoke<object>("Focus", controlId);
//object type is used since Invoke does not have a overload for void methods. Don't know why.
//this will return undefined according to js specs
}
For more information, you can refer to below.
If you want to improve the packaging of JS neatly, you can do something like this:
https://stackoverflow.com/a/49521216/476609
public class BlazorExtensionScripts : Microsoft.AspNetCore.Blazor.Components.BlazorComponent
{
protected override void BuildRenderTree(Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeBuilder builder)
{
builder.OpenElement(0, "script");
builder.AddContent(1, "Blazor.registerFunction('Focus', (controlId) => { document.getElementById(controlId).focus(); });");
builder.CloseElement();
}
public static void Focus(string controlId)
{
RegisteredFunction.Invoke<object>("Focus", controlId);
}
}
then add this component to the root: (App.cshtml):
<BlazorExtensionScripts></BlazorExtensionScripts>
<Router AppAssembly=typeof(Program).Assembly />
I want to add a more up-to-date (as of 0.9.0) example of calling a JavaScript function to set the focus to another control after some event, like clicking on a button. This might be helpful for someone just starting out with Blazor (like me).
This example builds on the example code in the Blazor documentation "Build Your First Blazor Components App" at https://learn.microsoft.com/en-us/aspnet/core/tutorials/build-your-first-razor-components-app?view=aspnetcore-3.0
First, follow all the instructions in the documentation. When you have a working To-Do List page, then add the following:
At the bottom of Index.html, under wwwroot, and below the script tag that loads the webassembly.js, add the following script:
<script>
window.MySetFocus = (ctrl) => {
document.getElementById(ctrl).focus();
return true;
}
</script>
At the top of your todo.cshtml page, add the following using statement:
#inject IJSRuntime JsRuntime;
In the #functions section of your todo.cshtml page, add the following function:
async void Focus(string controlId)
{
var obj = JsRuntime.InvokeAsync<string>(
"MySetFocus", controlId);
}
In the AddToDo() function, just below the line where you set the "newToDo" variable to an empty string, add a call to the Focus function, passing in the string id of the input control. (The example in the docs does not assign an ID to the input control, so just add one yourself. I named mine "todoItem").
void AddTodo()
{
if (!string.IsNullOrWhiteSpace(newTodo))
{
todos.Add(new TodoItem { Title = newTodo });
newTodo = string.Empty;
Focus("todoItem"); // this is the new code
}
}
Build and run your app. When you click the add new item button, the new item should be added to the list, the input control blanked out, and the focus should be back in the input control, ready for another item to be added.
From .NET 5 Preview 8
Set UI focus in Blazor apps
Blazor now has a FocusAsync convenience method on ElementReference for setting the UI focus on that element.
<button #onclick="() => textInput.FocusAsync()">Set focus</button>
<input #ref="textInput"/>
You can't directly call JavaScript function. You are required to first register your functions like,
<script>
Blazor.registerFunction('ShowControl', (item) => {
var txtInput = document.getElementById("txtValue");
txtInput.style.display = "";
txtInput.value = item;
txtInput.focus();
});
return true;
</script>
Then you need to declare a method in C# which calls this JavaScript function. Like,
private void CallJavaScript()
{
RegisteredFunction.Invoke<bool>("ShowControl", itemName);
}
You can call this C# method on click of button. Like,
<button id="btnShow" class="btn btn-primary" #onclick(CallJavaScript)>Show</button>
This post Create a CRUD App using Blazor and ASP.NET Core
shows a working demo of calling JavaScript from Blazor.

Listening for event in Dart via JQuery "on"

I am using a Bootstrap Modal dialog in Dart via js interop. All works OK apart from listening for the custom events. I am trying to listen to the "shown" event using the following code:
js.scoped(() {
js.context.jQuery("#myModal").on("shown", new js.Callback.once(() {
print("Dialog Shown");
}));
});
However, I get the following Dart error when the event is fired:
Class '() => dynamic' has no instance method 'call'.\n\nNoSuchMethodError : method not found: 'call'\nReceiver: Closure: (dynamic) => dynamic\nArguments: [Instance of 'Proxy']
Any ideas what I'm doing wrong?
Thanks.
You get this error because the callback should have one parameter (handler parameter of on documentation take a eventObject parameter). So your code should be :
js.context.jQuery("#myModal").on("shown", new js.Callback.many((eventObject) {
print("Dialog Shown");
}));
Note also the use of js.Callback.many instead of js.Callback.once. The former allows the callback to be called several times.

Can you bind to jQuery events with plain JavaScript?

I'm working on a project where a number of different companies are working on the same site.
The main developer have set up an event - let's call it init - which indicates the page is ready for our code to execute.
They're basically calling it like this:
$(window).trigger('init');
For a number of reasons I won't go into here, we prefer to avoid using jQuery in our own code wherever possible. I tried to bind to it like this:
window.addEventListener('init', function (event) {
alert('hehehehe');
});
But that doesn't seem to work. This works perfectly, though:
$(window).bind('init', function (event) {
alert('hehehehe');
});
Does jQuery use special event objects by default that you can't bind to with plain JS? Am I just doing something stupid?
The docs for bind seem to contain the answer:
Any string is legal for eventType; if the string is not the name of a native DOM event, then the handler is bound to a custom event. These events are never called by the browser, but may be triggered manually from other JavaScript code using .trigger() or .triggerHandler().
There's no native DOM event called 'init':
http://en.wikipedia.org/wiki/DOM_events
Hence "These events are never called by the browser, but may be triggered manually from other JavaScript code using .trigger() or .triggerHandler()"

Registering a javascript method in GWT java code so that it can be invoked based on an event in GWT

I tried looking for a similar post but couldn't find any, hence posting this query for your help. Essentially, I have a custom UI created on the GWT side. Now, I want to send events occuring at the GWT side over to the javascript/jsp page. For this I was wondering if there's a way for the jsp/javascript to register a method in the GWT code, and whenever, any event happens at the GWT side, the GWT java code can simply invoke this javascript method (which is like a function pointer/object), and the information would be notified at the jsp page. Though I can directly call javascript methods from within the GWT code, however, that means that the GWT code also need to know the javascript method name, and this results in a tight coupling. Instead the javascript could simply pass in a handle to the function to the GWT code, which would simply invoke this handle to pass in necessary events on the jsp/javascript code. Any ideas would be very helpful.
You can use a Dictionary to pass the function name to your application.
In the JSP host page:
<script type="text/javascript">
function myFunction() {
// Do stuff...
}
var MyDictionary = {
myCallback = "myFunction"
};
</script>
And in your application:
public void onEvent(EventType event) {
Dictionary d = Dictionary.getDictionary("MyDictionary");
invokeNativeCallback(d.get("myCallback"));
}
private native void invokeNativeCallback(String callbackName) /*-{
if (typeof $wnd[callbackName] === "function") {
$wnd[callbackName]();
}
}-*/;

Categories