normal chemist
normal chemist

Reputation: 413

How to use jQuery UI from Blazor component

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?

Upvotes: 15

Views: 32634

Answers (5)

Lion
Lion

Reputation: 17879

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);

Upvotes: 7

asif juneja
asif juneja

Reputation: 61

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

Upvotes: 0

Sgedda
Sgedda

Reputation: 1531

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>

Upvotes: 10

Sipke Schoorstra
Sipke Schoorstra

Reputation: 3409

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.

Upvotes: 18

Flores
Flores

Reputation: 8932

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.

Upvotes: 2

Related Questions