The scenario is I have a list of items in HTML; when I click on an item I use JS to dynamically create the HTML to load a silverlight app passing in the specific item # (using initParams); and my silverlight app visualizes this in a nice way. I do this on the same page rather than loading a new webpage, and the transition is smooth.
I know it is possible to have silverlight call a JS function on my page (opposite to what I need). I'm thinking it is also possible for my JS function to raise an event/call a method in silverlight, but not exactly sure how - has anyone tried this? While a workaround would be to recreate the silverlight app each time, just raising an event in existing, loaded SL app would would be the perfect solution to my problem.
regards
ewart.
You can call a method in your Silverlight application from JavaScript.
See this blog post
You just need to create a class in your silverlight app that registers itself as callable from JS:
[ScriptableType]
public partial class SomeClass
{
private bool mouseHeldDown = false;
private Point moveMeOffset = new Point();
public SomeClass()
{
HtmlPage.RegisterScriptableObject("SilverlightObject", this);
}
[ScriptableMember]
public void DoThing(int x)
{
//do some stuff
}
}
Then you can call this from JS
document.getElementById("mySilverlightControl").content.SilverlightObject.DoThing(5);
Related
I implemented Shepherd in my Vaadin Project, so i can guide users in tours through my web application.
But, i need to get access from the javascript on the Accordion Components in Vaadin, to open or close specific tabs. For this, i need to have access on the open() and close() method for the Accordion Components. So how can i access them through Javascript?
Already seen the Tutorial on the Website of them:
Vaadin calling java from javascript,
but sadly nothing over there, what could help me.
I already tried to use something like this:
UI.getCurrent().getPage().executeJs("window.startTour($0, $1)", this, Accordion1.getElement());
But when i try to bind it in javascript through:
window.startTour = (element, accordion) => { ... }
and in this window:
beforeShowPromise: function () {
return new Promise(function(resolve) {
element.$server.openAccordion(accordion.$server, 1);
resolve();
});
},
with the following method in java:
#SuppressWarnings("unused")
#ClientCallable
public void openAccordion(Object object, int index) {
Accordion accordion = (Accordion) object.get(this);
accordion.open(index);
}
i only get the following error message:
Class '...' has the method 'openAccordion' whose parameter 0 refers to unsupported type 'java.lang.Object'
No matter what i use as first parameter, everythin that extends Object doesnt work and i dont know why.
I found a recent post with the same question, but it was not helpful for me:
Unable to send a new bean instance to the server
Im using Intellij and in my Project: Java, Spring, Vaadin and Shepherd
Already tried to use different parameters, but only the int parameter is working, Object doesnt work.
The Problem is, i cant change the opened Tab of the Accordion from the Javascript over the Java, because of this error, so i have to implement for each Accordion 2 methods to open and close it.
Maybe somebody can help me with it or knows some tricks to master this.
Thanks
When using #ClientCallable you can pass only json or primitive types from JavaScript call to server. There is a real systems boundary here. Object is not supported and furthermore, you can cast that parameter to Java object.
I'm experimenting with Blazor in Visual Studio, specifically with calling Blazor code from JavaScript. I'm reasonably confident that I've got all the right libraries in place. However, when I attempt to call my Blazor method with invokeMethodAsync, I get the message "no .net call dispatcher has been set". In my Index.Html file, I have this:
<script>
DotNet.invokeMethodAsync("BlazorFour.App", "HelloYou").then(data => alert(data), reason => alert(reason));
</script>
(It's the alert(reason) that generates the error message)
I've added a class file to my Blazor project and it contains this:
using Microsoft.JSInterop;
using System.Threading.Tasks;
public class HelloWorld
{
[JSInvokable]
public static Task<string> HelloYou()
{
return Task.FromResult("Hello, ");
}
}
I've used all the templates in Visual Studio and the dotnet -new blazor commandline utility to create my start points but get the same message in all of the projects. It's seems likely to me that I'm missing something fundamental here.
Peter Vogel:
It would be great if you could call Blazor C# methods from your JavaScript code. Right now, that's not quite possible: Simply calling Blazor C# from JavaScript won't work (you'll get a message about a call dispatcher not being available).
What you can do, however, is once your Blazor code is running, call JavaScript code from your C# code. Once you've done that, your JavaScript code can, in turn, call Blazor code.
Hope this solves the problem...
I'm using server-side-blazor
I was getting js errors
No .NET call dispatcher has been set
So yep .. looks like Blazor wasn't initialized..
Not keen on a timeout
.. seems hacky and gives a laggy UI
Not keen on 'call JavaScript code from your C# code. Once you've done that, your JavaScript code can, in turn, call Blazor code.'
.. again seems hacky
This works for me ..
async function myBlazoryFunctionThing() {
// see https://sourcegraph.com/github.com/aspnet/AspNetCore#bd65275148abc9b07a3b59797a88d485341152bf/-/blob/src/Components/Web.JS/src/Boot.Server.ts#L41:9
await window.Blazor.reconnect();
..now it's safe to do my stuff
}
it 'seems' that Blazor.reconnect() will re-use the existing connection (if there is one)
...so it's not actually 're-connecting' (so 'seems' not much overhead ;-))
I tried a setTimeout (5000 ms delay) and it worked for me.(of course it is not needed to use the setTimeout every time, just the first time).
e.g. setTimeout( DotNet.invokeMethod(...), 5000);
I guess this is a timing issue, not everything is loaded/initialized.
Have you tried putting the js code in a button onclick event?
using Microsoft.JSInterop;
[Inject] protected IJSRuntime JSRuntime { get; set; }
From your blazor function call this function from this method
await JSRuntime.InvokeAsync<object>("fnToJavascriptCall", DotNetObjectReference.Create(this));
Create this function in blazor file
[JSInvokable]
public async Task fnToCallFromJavascript(int param1, int param2)
{
//Do some stuff
StateHasChanged();
}
In your javascript file
var fnToJavascriptCall = function (param1,currentInstance)
{
//Do your stuff
currentInstance.invokeMethodAsync("fnToCallFromJavascript", param1, param2);
}
The No .NET call dispatcher has been set error can be received when the JavaScript interop is used at the wrong time. Have a look at this StackOverflow question.
I'm building an ebook reader, so I have a webview that is loading a page stored locally on the device which it retrieved from the ebook itself. On this page, it has a javascript function controls.nextPage() that loads and runs just fine; it's used to not actually navigate to new web pages, but instead redraw virtual pages using javascript. I have this function bound to a button on the web page itself so that I can manually click it to test, again, works just fine when I touch the button on my webview.
Now I am trying to trigger this exact function from within my app. Ideally, I want to do this from a gesture swipe but that is too complicated for this specific question, as I have other issues with the gestures I need to solve first. For now, I've set up a button in my navigation drawer to trigger it and test it:
NavigationView navigationViewRight = (NavigationView) findViewById(R.id.nav_view_webview_right);
navigationViewRight.setNavigationItemSelectedListener(
new NavigationView.OnNavigationItemSelectedListener() {
#SuppressWarnings("StatementWithEmptyBody")
#Override
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
int id = item.getItemId();
if (id == R.id.nav_camera) {
// *** - Focus here - *** //
runOnUiThread(new Runnable() {
#Override
public void run() {
mWebview.evaluateJavascript("controls.pageNext()", null);
}
});
} else if (id == R.id.nav_share) {
}
drawer.closeDrawer(GravityCompat.END);
return true;
}
}
);
Note, I've also tried calling window.controls.pageNext() to no avail.
So my page is loaded, and I've hit my in-page button to test the function; works. I go to hit my navigation drawer button in the app? Error (when using window.controls.pageNext():
[INFO:CONSOLE(1)] "Uncaught TypeError: Cannot read property 'pageNext' of undefined", source: (1)
So it seems to be that evaluateJavascript() is being run in a fresh environment/thread. How can I tell it not to?
To get around this,I've tried to create an empty javascript interface in the hopes that I could simply initialize my page javascript into it and thus be able to call it from Android.
mWebview.addJavascriptInterface(new TestInterface(), "TestInterface");
public class TestInterface {
#JavascriptInterface
public void test() {
runOnUiThread(new Runnable() {
#Override
public void run() {
mWebview.evaluateJavascript("console.log('test')", null);
}
});
}
}
From my webapp, the javascript can call the interface just fine. calling TestInterface.test(); yields:
[INFO:CONSOLE(1)] "test", source: (1)
But when I tried to assign a new function to that interface from my webapp:
TestInterface.testTwo = function() {
console.log('testTwo');
};
TestInterface.testTwo();
Android wouldn't have it:
[INFO:CONSOLE(674)] "testTwo", source: http://127.0.0.1:8080/js/main.js (674)
What's weird is that it's not really giving me much info to go on. I do know that the rest of my page has issues loading after the testTwo() attempt that test() did not, so I'm assuming failure to load the rest of the script.
Lastly, out of curiousity, I changed my navigation drawer button to try and run the new function like this:
mWebview.evaluateJavascript("TestInterface.testTwo()", null);
Log:
[INFO:CONSOLE(1)] "Uncaught TypeError: TestInterface.testTwo is not a function", source: (1)
Yes but is it something else? I dunno. Thoughts? Thank you.
So I figured out in the end what my issue was. By running mWebview.evaluateJavascript("window.location", null); I realized that I in fact was not actually on the page I thought I was. My ebook page was being loaded into an iframe or some other type of skeleton/wrapper in such a way that the webapp functions were not in fact available when running evaluateJavascript().
So once figured that out, I can confirm some things that I originally questioned above:
So it seems to be that evaluateJavascript() is being run in a fresh
environment/thread. How can I tell it not to?
It does not. Just make sure you know what page is actually loaded.
To get around this,I've tried to create an empty javascript interface in the hopes that I could simply initialize my page javascript into it and thus be able to call it from Android.
This does in fact work. I'm not sure what my mistake was before, but if I create a javascript interface in Android, it's functions are available to the webapp AND I can in fact write new objects to the interface object from within the webapp. So, initializing new objects into TestInterface from within the webapp can be run within the Android app via:
mWebview.evaluateJavascript("TestInterface.someNewFunctionFromWebapp()", null);
I can NOT however overwrite any existing objects/properties of that javascript interface object. so TestInterface.test() is immutable.
I am creating an ActiveX control for a hardware (card swiper) device. I have understanding of ActiveX development and working, but I am little stuck with my specific scenario. Device SKD (dll files) have method to activate swiper/chip reader in order to be in listening state. Assume the following:
SDK sdk = new SDK();
sdk.RegisterCallback(GlobalCallback);
sdk.ActivateSwiper();
public void GlobalCallback(op, .., ..)
{
switch(op) {
case Swiped:
readCardData();
break;
case TransactionData:
readData();
break;
//. . . .
}
Above code is just for example. SDK has a global callback method for any event triggered through device (eg, card swiped, ICC seated, etc). If the SDK is being used in WPF/WinForm app, it is super easy get it working. But in my case, I must need to handle the raised event in javascript on my web page.
So, I have exposed a method in ActiveX to turn on the device, which works perfect:
[Guid("4794D615-BE51-4a1e-B1BA-453F6E9337C4")]
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(IComEvents))]
public class MyComObject : IComObject
{
[ComVisible(true)]
public string ActivateDevice()
{
SDK sdk = new SDK();
sdk.RegisterCallback(GlobalCallback);
string resultCode = sdk.ActivateSwiper();
return resultCode;
}
//. . <other code> . . .
}
and use it in webpage as:
<object id="myComComponent" name="myComComponent" classid="clsid:4794D615-BE51-4a1e-B1BA-453F6E9337C4"></object>
<script language="javascript" type="text/javascript">
var returnCode = myComComponent.ActivateDevice(); //or by creating ActiveXObject
</script
I have also got the idea about how to handle events using ActiveX on webpage. I learnt it from HERE. But the event is handled by calling the exposed method to raise event. And in my case, when card is swiped, the call will go to GlobalCallback() method.
Questioins:
Is there any workaround, I can implement handler in my scenario, to make it usable on javascript?
I am thinking of something like PropertyChanged event, bound to a string property, which holds the derived card data. and the handlers returns this property value. Is there any workaround possible like this? I need little help with this.
I am thinking this as:
public static string CARD_DATA;
public void GlobalCallback(op, .., ..)
{
switch(op) {
case Swiped:
CARD_DATA = readCardData();
//and CARD_DATA is bound to property-changed event, and its handler returns its value.
//other code
Is this even possible? If so, What to be exposed? and How to use it? as this property will be changed internally, when card is swiped, and case Swiped: is executed. Is there any workaround?
Other Info:
Web App is MVC 4 famework based
Device is The Augusta (IDTech). (they don't have actieX/plugin for web)
I'm writing a JavaFX App, which Interacts with the JavaScript, Using WebView and WebEngine (.executeScript() Method).
Here, I have this part of code from Medow.java, which loads map.html(Contains JavaScript Code), And This Code Works Pretty well:
add_button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent ea5) {
// webEngine.executeScript("document.fun();"); // For Drawing Shapes
if (add == false) {
webEngine.executeScript("document.fun();"); // For Drawing Shapes
add = true;
}
// }
else {
webEngine.executeScript("document.reSet();"); // To remove Drawing Shapes
add = false;
}
}
});
In Here
webEngine.executeScript();
Is Invoking Appropriate JavaScript function's
But Now, I want my Java Code to Invoke Some JS function, when the Program Starts, So I'm directly writing :
webEngine.executeScript("document.draw();");
right Under/after the code, which loads the map.html file.
So, now as Both of the
webEngine.execute("document.fun();"); and webEngine.executeScript("document.draw();"); are nearly similar, I cannot Understand what Difference does, it makes to be inside the <button>.setOnAction block and to be outside it, Because both WebEngine and webView are declared as Global Variables.
cannot invoke document.draw() function using HTML's onLoad options, because i need to pass some Values To function draw from java.
The Exception Generated is :
netscape.javascript.JSException: TypeError: undefined is not a function (evaluating 'document.draw()')
how can i make this work? thank you
While Continuously trying to figure out whats the cause, I Discovered That the HTMLDocument Object, created using webEngine.load(), is for some reason visible only inside the handle method, And nowhere else, even though its been defined outside.
What happens here is that you want to call a JavaScript function before the content is loaded completely. Therefore the function draw is not defined. So the only way is to wait until the page is loaded. There are two ways that might prove useful:
Add a changelistener on the state and execute the JavaScript once the loading has succeeded:
String htmlURL = ...
webView.getEngine().load(htmlURL);
webView.getEngine().stateProperty().addListener(new ChangeListener<Worker.State>() {
#Override
public void changed(ObservableValue<? extends Worker.State> ov, Worker.State t, Worker.State t1) {
if (t1 == Worker.State.SUCCEEDED) {
// this will be run as soon as WebView is initialized.
webView.getEngine().executeScript("document.draw()");
}
}
});
The other way is more of a solution within JavaScript. You first have to register a bridge between Java and your html page (has to be done in the SUCCEEDED state change as well, see WebView callback from Javascript):
JSObject window = (JSObject) webEngine.executeScript("window");
window.setMember("app", this);
Now this JavaObject is referenced in your JavaScript. Let's say that you have a method on the class that is of the above type of this:
public void executeOnPageLoaded() {
...
}
Then you can call this method from Javascript. If you are using jQuery it could look like this:
$( document ).ready(function() {
console.log( "ready!" );
app.executeOnPageLoaded();
});
This second approach is more complex but in the long run may give you more flexibility.
When you start working with JavaScript in the WebView is is a good idea to have Firebug lite in there as well, so investigate what is happening but mainly to have a means to seed the console output of JavaScript. See Java FX application onload event