Problem with JavaScript files in Blazor component - javascript

I use JavaScript file in Blazor component (Blazor server and .net 6).
as below:
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await jsRuntime.InvokeAsync<IJSObjectReference>("import", "/css/template/vendor/libs/bs-stepper/bs-stepper.js");
}
}
When for the first time loading page everything is OK , but when I change page with nav and enter to another page (component) and after that again return to same as previous page , not working JavaScript codes.
What is the reason for this problem?

Here is how you can use javascript libraries in blazor apps.
Add the script of the library at the end of body element in your index.html (or _Layout.cshtml)
<body>
...
<script src="css/template/vendor/libs/bs-stepper/bs-stepper.js"></script>
...
</body>
and the css at the end of head element
<head>
...
<link rel="stylesheet" href="css/template/vendor/libs/bs-stepper/bs-stepper.min.css">
...
</head>
Create a js file named e.g stepperInteropHelper.js in path wwwroot/js and inside add this function:
export function initStepper() {
new Stepper(document.querySelector('.bs-stepper'));
}
Finally in your components that use a stepper:
#inject IJSRuntime jsRuntime
<div class="bs-stepper">
...
</div>
#code {
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
// import stepperInteropHelper.js module
IJSObjectReference module = await jsRuntime.InvokeAsync<IJSObjectReference>(
"import", "./js/stepperInteropHelper.js");
// call initStepper method from stepperInteropHelper.js
await module.InvokeVoidAsync("initStepper");
}
}
}

Related

How to invoke tauri api in Blazor wasm?

I am using TAURI + Blazor web assembly to make a desktop app. Now I need to add some native window features to my app, like maximize the window, etc. However, when I invoke a JavaScript function in the index.razor file, something cannot work right. There's some errors, but I don't not where they are.
My code in js/scripts.js is
import { appWindow } from "#tauri-apps/api/window";
export async function ToggleWindowMax() {
await appWindow.toggleMaximize();
}
And my code in Pages/index.razor is
#page "/"
#inject IJSRuntime JS
<PageTitle>Index</PageTitle>
<MudStack>
<MudButton Class="pa-3 border-solid" Variant="Variant.Outlined" DisableRipple="true" OnClick="ToggleMax"
Style="#($"color: {Colors.Purple.Accent3}; border-color:{Colors.Purple.Accent2}; border-width: 2px; border-radius: 9999px;")">
#result
</MudButton>
</MudStack>
#code {
private IJSObjectReference? module;
private string result = "Toggle Maximize";
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSObjectReference>("import", "./js/scripts.js");
}
}
private async Task ToggleMax()
{
await module.InvokeVoidAsync("ToggleWindowMax");
}
}
I have added the needed yarn package with yarn add #tauri-apps/api. I need your help.
I tried to find the right ways to invoke some JavaScript functions, such as prompt or alert. However, I cannot invoke the Tauri api correctly.

Blazor include javascript library in specific component

My Blazor server side app has several js / css libraries included in the _Layout.cshtml file. However, in one of my components I want to leverage an additional set of js / css libraries to add functionality for that specific component-- I don't want these libraries to get loaded globally.
Is there a way to do this that is native to Blazor? I've seen many posts that use a third-party library or some sort of hack to accomplish this (for example https://github.com/mishelshaji/DynamicJavaScriptInBlazor), but it seems like something that ought to be supported natively.
Using the lazy loading feature of IJSRuntime you can dynamically load JavaScript with your component.
In this component I lazy load a script file within a Razor Class Library.
Relevant sections from a code behind approach component:
public partial class Dialog : ComponentBase, IAsyncDisposable
{
private readonly Lazy<Task<IJSObjectReference>> moduleTask;
private DotNetObjectReference<Dialog> dotNetObjectReference;
...
public Dialog()
{
moduleTask = new(() => jsRuntime!.InvokeAsync<IJSObjectReference>(
identifier: "import",
args: "./_content/BlazorDialogs/dialogJsInterop.js")
.AsTask());
dotNetObjectReference = DotNetObjectReference.Create(this);
}
[Inject]
private IJSRuntime jsRuntime { get; set; }
...
public async ValueTask ShowDialogAsync()
{
var module = await moduleTask.Value;
await module.InvokeVoidAsync(identifier: "showDialog", dialogElement, dotNetObjectReference);
...
}
public async ValueTask CloseDialogAsync()
{
var module = await moduleTask.Value;
await module.InvokeVoidAsync(identifier: "closeDialog", dialogElement);
...
}
[JSInvokable]
public void OnDialogClosed()
{
...
}
public async ValueTask DisposeAsync()
{
if (moduleTask.IsValueCreated)
{
var module = await moduleTask.Value;
await module.DisposeAsync();
}
}
}
Note: OnDialogClosed is called from the JavaScript.
Repo

How to get pathname in NextJS /_document.js file

I'm trying to get the current pathname in /page/_document.js file. I'm using a class, and my objective is to make a conditional with that value.
Here's my code (is basically the example in NextJS' page)
import Document, { Html, Head, Main, NextScript } from 'next/document'
class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx)
return { ...initialProps }
}
render() {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument
You can get it by ctx.req.url or ctx.asPath but getInitialProps only executed at first time you access to app
Note: 's getInitialProps function is not called during client-side transitions, nor when a page is automatically statically optimized.
Note: Make sure to check if ctx.req / ctx.res are defined in
getInitialProps. These variables will be undefined when a page is
being statically exported for next export or automatic static
optimization.
Document

Stencil custom page javascript not firing

I'm trying to create a custom web page in stencil website and trying to add custom javascript module.
This is the html file named '/templates/pages/custom/page/customz.html'
{{~inject 'template' template}}
<h2>Hello World!</h2>
<body>
Some custom content!
<body>
<script>window.__webpack_public_path__ = "{{cdn 'assets/dist/'}}";</script>
<script src="{{cdn 'assets/dist/theme-bundle.main.js'}}"></script>
<script>window.stencilBootstrap("{{page_type}}", {{jsContext}}).load();</script>
This is the javascript file named '/asset/js/theme/customz.js'
import PageManager from './page-manager';
export default class Customz extends PageManager {
onReady() {
console.log('onReady');
}
constructor(context) {
super(context);
console.log(context);
}
}
then i added this in app.js file
const customClasses = {
'pages/custom/page/customz': () => import('./theme/customz')
};
and also added it .stencil file to test it locally
I also created the web page in bigcommerce dashboard.
The problem i have is that the HTML is loaded but the Javascript file is not injected (i cannot see the console log strings in console or other js logic i insert).
Where can be the problem?
The place I usually start when troubleshooting a custom template is the related section on the BigCommerce Dev Center here: https://developer.bigcommerce.com/stencil-docs/storefront-customization/custom-templates#troubleshooting-template-authoring
If you've verified the version of the Stencil CLI and URL you're using, try using this same code with the base Cornerstone theme on the latest version.
you need add link for Windows too:
Look like:
const customClasses = {
'pages/custom/page/customz': () => import('./theme/customz'),
'pages\\custom\\page\\customz': () => import('./theme/customz')
};
And your custom page must contains:
`{{~inject 'template' template}}
<script>window.__webpack_public_path__ = "{{cdn 'assets/dist/'}}";</script>
<script src="{{cdn 'assets/dist/theme-bundle.main.js'}}"></script>
<script>window.stencilBootstrap("{{page_type}}", {{jsContext}}).load();</script>`
If don`t connection on base file from layout.

JavaFX invoking Java methods using JavaScript

My application is completely styled in a web document form (HTML, CSS & JavaScript), and I'm only using JavaFX WebView to load it as a normal resource.
I would like to invoke a method from one of my classes (a Java code) using JavaScript.
Like for example, a simple Hello World to the console:
public class Hello {
public void world() {
System.out.println("Hello World!");
}
}
How can I invoke the world() method in this case?
So my code from the page is something like this:
<!-- onlick action from a button calls hello() function -->
<button onclick="hello();" value="Invoke"></button>
<script>
function hello() {
/* CODE WHICH INVOKE A JAVA METHOD */
}
</script>
Any way to achieve this?
UPDATE
Notice: For those people who were looking for a complete and simple example about how to achieve this, you can test all the following codes written below.
I've finally achieved my goal, thanks to sir #Oshan_Mendis' answer. This example is based from this tutorial from Oracle docs: 6 Making Upcalls from JavaScript to JavaFX.
But here, I'll be using my own code, the main goal is to call a method from Java code using JavaScript from the HTML page.
File contents:
Controller.java /* Controller class for WebView */
Hello.java /* Class in which method(s) will be invoked */
Main.java /* Main class (launches the application) */
main.fxml /* Main layout (WebView) */
index.html /* Main layout web page content */
1. Creating the Main-Class (Main.java)
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage stage) throws Exception {
/* The root layout of the application, an FXML contains the WebView layout. */
Parent root = FXMLLoader.load(Main.class.getResource("/main.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
2. Preparing the Main layout (main.fxml)
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.web.WebView?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns="http://javafx.com/javafx/8"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="Controller" <!-- The controller class for this layout -->
prefHeight="400.0"
prefWidth="300.0">
<children>
<!-- Given the webView ID to initiate the web page -->
<WebView fx:id="webView" />
</children>
</VBox>
3. Setting up the web page (Controller.java)
import java.net.URL;
import java.util.ResourceBundle;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker.State;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import netscape.javascript.JSObject;
public class Controller implements Initializable {
private WebEngine webEngine;
#FXML private WebView webView;
#Override
public void initialize(URL location, ResourceBundle resources) {
/* Load the web page URL (location of the resource) */
URL url = Controller.class.getResource("/index.html");
webEngine = webView.getEngine();
webEngine.load(url.toExternalForm());
/* Set the State listener as well as the name of the JavaScript object and its
* corresponding Java object (the class in which methods will be invoked) that
* will serve as the bridge for the two objects.
*/
webEngine.getLoadWorker().stateProperty().addListener(new ChangeListener<State>() {
#Override
public void changed(ObservableValue<? extends State> observableValue, State oldState, State newState) {
if (newState == State.SUCCEEDED) {
JSObject window = (JSObject) webEngine.executeScript("window");
/* The two objects are named using the setMember() method. */
window.setMember("invoke", new Hello());
}
}
});
}
}
4. Preferred class and its method to invoke (Hello.java)
public class Hello {
public void world() {
System.out.println("Hello World!");
}
}
5. Main layout web page content (index.html)
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>
function helloWorld() {
/* JavaScript object name and the method to invoke */
invoke.world();
}
</script>
</head>
<body>
<!-- onlick event calls helloWorld() function -->
<button onclick="helloWorld()">INVOKE</button>
</body>
</html>
Note: You can perform other mouse-related events other than onclick event in this case like: onmouseenter, onmouseover, onmousemove, onmouseup, etc.. But I'm not really sure if these are the only supported events of invoking methods.
This is well explained in the Java API Documentation under Calling back to Java from JavaScript
public class JavaApplication {
public void exit() {
Platform.exit();
}
}
...
JavaApplication javaApp = new JavaApplication();
JSObject window = (JSObject) webEngine.executeScript("window");
window.setMember("app", javaApp);
You can then refer to the object and the method from your HTML page:
Click here to exit application

Categories