I am working through some Blazor examples, and while trying to work with some JSInterop solutions, I ran into an issue with jQuery UI elements. I am not a proficient Javascript programmer, but I am proficient enough with .NET so I may be missing something simple. The first jQuery UI component I have tried to work with is the "resizable" component, found here: https://jqueryui.com/resizable/
Here is what my current code looks like:
index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width">
<base href="/" />
<link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
<link href="css/site.css" rel="stylesheet" />
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.3.1.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
</head>
<body>
<app>Loading...</app>
<script src="_framework/blazor.server.js"></script>
<script>
$( function() {
$( "#resizable" ).resizable();
});
</script>
</body>
</html>
I am certain that the issue isn't with loading the libraries, and I have placed the script after loading blazor.server.js.
Now, my Index.cshtml has the following in the html portion:
<body>
<div class="container-fluid">
<div id="resizable" class="ui-widget-content">
<div class="row row-no-gutters" style="width: 100%; height: 50%">
<h3 class="ui-widget-header">Resizable</h3>
</div>
</div>
</div>
</body>
Ideally, this would yield a resizable div, but the resulting html element is not resizable. From my understanding, Blazor JSInterop no longer requires JS functions to be registered. What am I doing wrong?
The problem is that of timing: your jQuery function executes before your Blazor app has rendered.
The way I solved this is by replacing the "onready" ($(...)) function with a named function (e.g. onBlazorReady) that I then invoke from my Blazor's MainLayout component at the right time.
The right time being OnAfterRender.
For example:
MainLayout.razor:
#code {
[Inject]
protected IJSRuntime JsRuntime { get; set; }
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
JsRuntime.InvokeVoidAsync("onBlazorReady");
}
}
index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width">
<base href="/" />
<link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
<link href="css/site.css" rel="stylesheet" />
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.3.1.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
</head>
<body>
<app>Loading...</app>
<script src="_framework/blazor.server.js"></script>
<script>
onBlazorReady() {
$("#resizable").resizable();
});
</script>
</body>
</html>
As you can see, I'm injecting IJSInterop so that I can call my onBlazorReady JS function after the LayoutComponent has rendered.
Works fine for me like this, to add datepicker from jquery ui:
Razor component file.
#code {
protected override async void OnAfterRender(bool firstRender)
{
await jsRuntime.InvokeVoidAsync("addDatePicker");
base.OnAfterRender(firstRender);
}
}
Global razor file:
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Magiro.Blazor</title>
<base href="~/" />
<link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
<link href="css/site.css" rel="stylesheet" />
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
</head>
<body>
<app>
#(await Html.RenderComponentAsync<App>(RenderMode.ServerPrerendered))
</app>
<script src="_framework/blazor.server.js"></script>
<script>
window.addDatePicker = () => {
$(".datepicker").datepicker();
}
</script>
</body>
</html>
There is no need any more to add a method in the global index page (_Hosts.cshtml in ASP.NET Core 5.0), which would fill those file with view-specific logic and dependencies. At least with v5 we have IJSObjectReference allowing us to call $('table').DataTable() directly from our .razor file like this:
<table>
<thead>
<tr>
<td>Id</td>
<td>Name</td>
</tr>
</thead>
<tbody>
<!-- your data -->
</tbody>
</table>
#code {
[Inject]
protected IJSRuntime JSRuntime { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender) {
if (firstRender) {
var jQuery = await JSRuntime.InvokeAsync<IJSObjectReference>("$", "table");
await jQuery.InvokeVoidAsync("DataTable");
}
}
}
It's also possible to pass options to the plugin with an anonymous type. For example, to disable the search, we'd call in JS
$('#example').dataTable({
"searching": false
});
In Blazor we create an anonymous type with the same structure and pass it as parameter to the DataTable call:
var options = new {
searching = false
};
await jQuery.InvokeVoidAsync("DataTable", options);
The most probable cause is timing. The jQuery function gets executed before the DOM elements exists.
You should register the JS code as an interop function and call that after in OnAfterRender event.
Normally you don't need any JQuery function like ready, you can simply put those all required code into your custom js function which should run on ON_LOAD of your component as said by #Sipke Schoorstra in above answer
Related
I've tried to create a new window and print it from current one in maui application. I invoke the printPage() method from Index.razor. The problem is that I use js code to open it and it throws an exception that created window is null. Is it possible to create new window in such a way or there is another method?
I know that if I call just a window.print() in script, the print dialog will pop up. However, it'll print a current page and I want some specific page. (In the example it's just a blank page)
Index.razor
#page "/"
<h1>Hello, world!</h1>
Welcome to your new app.
<SurveyPrompt Title="How is Blazor working for you?" />
#inject IJSRuntime JS
<button class="btn btn-primary" #onclick="Print">Print</button>
#code {
private async void Print()
{
await JS.InvokeVoidAsync("printPage");
}
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
<title>MauiApp11</title>
<base href="/" />
<link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
<link href="css/app.css" rel="stylesheet" />
<link href="MauiApp11.styles.css" rel="stylesheet" />
</head>
<body>
<div class="status-bar-safe-area"></div>
<div id="app">Loading...</div>
<div id="blazor-error-ui">
An unhandled error has occurred.
Reload
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.webview.js" autostart="false"></script>
<script>
function printPage() {
var i = window.open("", "", "height=452,width=1024,tabbar=no");
i.document.write(`<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h1>Template</h1>
<p>This is a blank template for a web page.</p>
</body>
</html>`);
i.document.close();
i.focus();
i.print();
}
</script>
</body>
</html>
So I have this issue, I want to use vanilla JavaScript because I don't like how bloated JQuery is. Now, the problem I have is when I load my file into the div withe the following code:
async function loadPage (pagename) {
await fetch(pagename, {
headers: {
"Content-Type": "text/html; charset=utf-8"
}
})
.then((response) => response.text())
.then((html) => {
document.getElementById("Loader").innerHTML = html;
})
.catch((error) => {
console.warn(error);
});
}
my JavaScript in the page doesn't fire.
I have tried running JavaScript in the file and also with external files.
When I put the JavaScript file for this HTML in the main page it fires, but it is littered with errors. When I use the JQuery load function it works fine though, is there any way to get this right in vanilla JavaScript or am I gonna have to use JQuery??
HTML for details.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="css/autocomplete.css">
<link rel="stylesheet" href="css/details.css">
<link rel="stylesheet" href="css/tablestyles.css">
<title>Search</title>
</head>
<body>
<!-- Enter search criteria here -->
<div id="top">
<button onclick="search();">Search</button>
<div class="autocomplete" style="width:300px;">
<input type="text" name="searchPhrase" id="searchPhrase" placeholder="Search Phrase" autocomplete="off">
</div>
<!-- Options for autocomplete -->
<div id="displaydiv">
</div>
</div>
<div id="results">
</div>
</body>
<script src="js/autocomplete_input.js"></script>
<script src="js/tablecreator.js"></script>
<script src="js/details.js"></script>
</html>
loadPage is called by main.js
function openSearch(){
loadPage('details.html');
}
I tried replicating it in codesandbox!
I am using Radzen Blazor Components, which are very useful and easy to implement, with a .NET 6 Blazor WebAssembly App.
I followed the guide on the Radzen web to install components.
The issue is that I'm trying to call a JavaScript function using IJSRuntime but it throws an Exception saying that the function is undefined. I guess that in some way, the added javascript files are not included in the compilation, because I tested the JS interop with a plain Blazor WASM app and it works.
Can anybody help me? thank you very much.
Exception:
blazor.webassembly.js:1 crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
Unhandled exception rendering component: Could not find 'createAlert' ('createAlert' was undefined).
Error: Could not find 'createAlert' ('createAlert' was undefined).
at https://localhost:7000/_framework/blazor.webassembly.js:1:328
at Array.forEach ()
at a.findFunction (https://localhost:7000/_framework/blazor.webassembly.js:1:296)
at _ (https://localhost:7000/_framework/blazor.webassembly.js:1:2437)
at https://localhost:7000/_framework/blazor.webassembly.js:1:3325
at new Promise ()
at Object.beginInvokeJSFromDotNet (https://localhost:7000/_framework/blazor.webassembly.js:1:3306)
at Object.St [as invokeJSFromDotNet] (https://localhost:7000/_framework/blazor.webassembly.js:1:59853)
at _mono_wasm_invoke_js_blazor (https://localhost:7000/_framework/dotnet.6.0.8.yj5vlwdtrc.js:1:195300)
at wasm://wasm/00971db2:wasm-function[219]:0x1a48f
Here is the code:
Index.razor
#inject IJSRuntime JS
<RadzenButton Text="Show Alert" Click=#ShowAlert/>
#code {
private async Task ShowAlert() => await JS.InvokeVoidAsync("createAlert");
}
interop.js
function createAlert() {
alert("Hey this is an alert");
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>Blazor App</title>
<base href="/" />
<link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
<link href="css/app.css" rel="stylesheet" />
<link href="BlazorApp.Client.styles.css" rel="stylesheet" />
<link rel="stylesheet" href="_content/Radzen.Blazor/css/default.css">
<script src="_content/Radzen.Blazor/Radzen.Blazor.js"></script>
</head>
<body>
<div id="app">Loading...</div>
<div id="blazor-error-ui">
An unhandled error has occurred.
Reload
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.webassembly.js"></script>
<script scr="interop.js"></script>
</body>
</html>
Try using IJSObjectReference to reference JS files:
Index.razor:
#page "/"
#inject IJSRuntime JS
<RadzenButton Text="Show Alert" Click=#ShowAlert />
#code {
private IJSObjectReference? moudle;
protected override async Task OnInitializedAsync()
{
moudle = await JS.InvokeAsync<IJSObjectReference>("import", "./interop.js");
}
private async Task ShowAlert()
{
await moudle.InvokeVoidAsync("createAlert");
}
}
interop.js:
export function createAlert() {
alert("Hey this is an alert");
}
I am learning the basis of HTML, Bootstrap & JavaScript. I want to use bootstrap nav class for navigation. The first problem: bootstrap doesn't work in shared view _Layout.cshtml and buttons look like:
But the content of index.cshtml looks OK.
The second problem: when I click on Messages, it redirects me only to controller ../User, not to its action ../User/Index. I have no idea why it does not work.
Below my _Layout.cshtml code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width" />
<link href="~/lib/bootstrap/dist/css/bootstrap.css" rel="stylesheet" />
<script src="~/lib/twitter-bootstrap/js/bootstrap.js"></script>
<title>#ViewData["Title"] - title</title>
<link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
<header>
<ul class="nav nav-tabs">
<li role="navigation" class="active">Home</li>
<li role="navigation">Profile</li>
<li role="navigation">Messages</li>
</ul>
</header>
<div class="container">
<main role="main" class="pb-3">
#RenderBody()
</main>
</div>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
#RenderSection("Scripts", required: false)
</body>
</html>
For first problem, Try to change from
<link href="~/lib/bootstrap/dist/css/bootstrap.css" rel="stylesheet" />
To:
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
For second problem, try to change from #Url.Action("Index", "User") to #Url.Action("User")
You have to make sure that your controller naming is correct. Otherwise, we may need your UserController sample code for further assist
Everything works fine when I manually go to ../User/Index view.
Here is the part of UserController.cs class:
public class UserController : Controller
{
private readonly DietDbContext _dbContext;
private readonly IMapper _mapper;
public UserController(DietDbContext dbContext, IMapper mapper)
{
_dbContext = dbContext;
_mapper = mapper;
}
public ActionResult Index()
{
return View(_dbContext.Users.ToList());
}
I can't seem to understand what is wrong with the code.
I've added jquery and jquery UI still have the :
[objec object] has no method effect error.
Here is the html code:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>title</title>
<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" />
<link rel="stylesheet" href="stylesheet.css" type="text/css"/>
<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.10.2.min.js">
</script>
<script type="text/javascript" src = 'script.js'></script>
</head>
<body>
<div id = 'container'>
<div id ='one' class = 'inside'>1</div>
<div id ='two' class = 'inside'>2</div>
<div id ='three' class = 'inside'>3</div>
<div id ='four'class = 'inside'>4</div>
<div id ='five' class = 'inside'>5</div>
</div>
</body>
</html>
And the javascript code :
$(document).ready( function () {
alert('JQ on');
$('.inside').mouseover(function () {
$(this).fadeTo(1000,1);
});
$('.inside').mouseleave(function () {
$(this).fadeTo(500,0.2);
})
$('.inside').click(function () {
$(this).effect('explode');
})
})
Any ideas?
You've included the jQuery UI stylesheet, but not the JavaScript for it.
You need to add:
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js"></script>
(Or another URI that supplies it) after you include the jQuery JS.
I suppose you need to include jQuery UI