micsp24
micsp24

Reputation: 887

Blazor onclick event is not triggered

I try to implement a simple onclick event handler like this sample, but it is not working in my solution. The event is only triggered at the run of the web page for unknown reasons.

The HTML page with Blazor component is well shown, but when I click on the button, nothing is happening.

I'm on Visual Studio 2019 and .NET Core 3.0, with an ASP.NET MVC project.

Counter.razor file

@using Microsoft.AspNetCore.Components

<p>Current count: @currentCount</p>

<button class="btn btn-primary" onclick="@IncrementCount();">Click me</button>

@code {
    int currentCount = 0;

    private async Task IncrementCount()
    {
        await Task.Run(() => currentCount++);
    }
}

File Index.cshtml

@using WebApplication2.Views.Components

@{
    ViewData["Title"] = "Home Page";
}

<div class="text-center">
    <h1 class="display-4">Welcome</h1>
    <p>Learn about <a href="https://learn.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>

@(await Html.RenderComponentAsync<Counter>(RenderMode.Server, new { }))

File startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();

    services.AddHttpClient();
    services.AddRazorPages();
    services.AddServerSideBlazor();
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        // The default HSTS value is 30 days. You may want to change 
        // this for production scenarios; see https://aka.ms/aspnetcore-hsts.
        app.UseHsts();
    }
    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
        endpoints.MapBlazorHub();
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");
    });
}

The button in the browser:

<button class="btn btn-primary" onclick="System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[System.Threading.Tasks.VoidTaskResult,WebApplication2.Views.Components.Counter+<IncrementCount>d__2];">Click me</button>

The error in the browser:

Bug in browser

Upvotes: 77

Views: 114991

Answers (22)

IgleDev
IgleDev

Reputation: 1

I use .NET 8 and this work for me

@rendermode @(new InteractiveServerRenderMode(prerender: false))

Put this on the top of your component and this work for me:)

Upvotes: 0

DKR
DKR

Reputation: 5734

I resolved the issues by adding @rendermode InteractiveServer in razor page.

Upvotes: 7

Robson Valente
Robson Valente

Reputation: 61

I'm using WebAssembly .net8. For this to work on server-side pages, was necessary to include .AddInteractiveServerComponents() and .AddInteractiveServerRenderMode() on the Program.cs

builder.Services.AddRazorComponents()
.AddInteractiveServerComponents()
.AddInteractiveWebAssemblyComponents();

and

app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode()
.AddInteractiveWebAssemblyRenderMode()
.AddAdditionalAssemblies(typeof(SiteCasamento.Client._Imports).Assembly);

and than include rendermode in the pages

@rendermode InteractiveServer

Thanks

Upvotes: 6

Greg Gum
Greg Gum

Reputation: 37895

This answer is only for .net 8, where static rendering is the default. .net 8 introduced the notion of Render Modes, and it is now required either at the page level, or globally. The default is static rendering which means that the component is rendered exactly as it is written with no interactivity being wired up by the Blazor engine. This is nice if you have pure static html, and don't need the overhead of the interactivity. On the other hand, it can catch you by suprise if have used earlier version and your click event is just not getting processed and there is no obvious reason for it.

To wire up interactivity, you have two options.

Per component solution
Check for this directive at the top of the file:

@rendermode InteractiveServer

If it's missing, then you will not get events being fired. On the client side (wasm) it will be this:

@rendermode InteractiveAuto

Global solution
Update Routes element in App.razor file:

<body>
    <Routes @rendermode=RenderMode.InteractiveServer />
    <script src="_framework/blazor.web.js"></script>
</body>

The Global solution allows you to use interactive elements in the MainLayout.razor which is not supported in the per component solution. This can be useful if you are doing something like wiring up a "dark" mode and need a class to be applied at the top level of your html so it gets applied to everything.

Edit: Even though you have added AddInteractiveServerComponents() this does NOT mean that you have set the default to be InteractiveServer. It only means that you have the option to add InteractiveServer as a global or component option. So you still must add the global or component option as above.

Upvotes: 157

Jeff George
Jeff George

Reputation: 1

This fixed my issue. I just hide the unwanted buttons instead of adding and removing them.

<MudItem sm="4" Style="padding-top:30px">
    <MudButton style="@ScheduleAddButton" Color="Color.Warning" Variant="Variant.Outlined" OnClick="() => SaveSchedule()">Add Schedule</MudButton>
    <MudButton style="@ScheduleRemoveButton" Color="Color.Warning" Variant="Variant.Outlined" OnClick="() => DeleteSchedule()">Delete Schedule</MudButton>
</MudItem>

Added code

if (Schedule.Trim().Length == 0)
{
    ScheduleAddButton = "display:block";
    ScheduleRemoveButton = "display:none";
}
else
{
    ScheduleAddButton = "display:none";
    ScheduleRemoveButton = "display:block";
}

StateHasChanged();

Upvotes: 0

Tanksley
Tanksley

Reputation: 67

Much like DreamEvil 's answer, I was having this problem after I added StaticFileOptions to my app.UseStaticFiles() method in Program.cs.

I stumbled across this known Blazor issue which explains the problem: Passing options to UseStaticFiles breaks Blazor Server's static file service #19578

There are a couple of solutions, but my favorite is by configuring StaticFileOptions in the Services collection before calling app.UseStaticFiles() (without any parameters).

builder.Services.Configure<StaticFileOptions>(options =>
{
    options.OnPrepareResponse = ctx=>
    {
        const int durationInSeconds = 60 * 60 * 24 * 7;
        ctx.Context.Response.Headers[HeaderNames.CacheControl] =
            "public,max-age=" + durationInSeconds;
    };
});

Upvotes: 1

somedotnetguy
somedotnetguy

Reputation: 938

Maybe learn the difference between a return value of an executed method and a method-type aka as delegate e.g. Action

IncrementCount(); // Is a method that returns void (or string, or a Task, if you want)
var returnValue = MyMethod2();


// These declare and initialize delegate variables
var action = IncrementCount;
var action2 = MyMethod;
Func<int> myFunc = MyMethod2;
MyDelegate myDel = MyMethod3;

You want to provide a delegate to the @onclick directive, ... not use the old syntax, nor a return value (although it’s possible)

<button @onclick=@Hahaha()>Click me</button>
<button @onclick=@Foo>Click me</button>
<button onclick="console.log('Hahaha Foo')">Click me</button>
<button onclick=@Baar()>Click me</button>
@code {
Action Hahaha(){
return Foo;
}
void Foo() {
i = 5;
}
}
string Baar()
{
    return "console.log('Boofar')";
}

The code onclick="@IncrementCount();" is simply dead wrong in at least three ways.

  • why would there be " if you dont wanna provide a string
  • why use the ; if there is no block at all -> learn about the @ directive
  • not using @onclick
  • using a implicit string conversion of a Task object returned from a method, which is absolutely not what is desired here

The framework does exactly what you tell it todo: casting the Task into a string with the implicit ToString(), append a ';' and pass that as the JavaScript code for the html attribute onlick which gets exactly rendered as that. Note that this even only works for non-void methods, because void methods would not even return anything and thusly cannot used as an expression for a string, what is what is inferred here!

Upvotes: 0

Mirko MyRent
Mirko MyRent

Reputation: 211

Use:

<button class="dropdown-item" type="button" @onclick="() => lock_invoice(r)"> <i class="fa fa-lock"></i> &nbsp; Lock</button>

And code:

@code {
    void lock_invoice(System.Data.DataRow row)
    {
        string id = row["id"].ToString();
        ...
    }
}

Upvotes: 0

ElConrado
ElConrado

Reputation: 1638

I tried to figure out most of the solutions for Blazor inside the ASP.NET MVC application. Some are incomplete, so I’ve tried to prepare complex instructions which work for me. If you are working on Blazor inside an ASP.NET MVC application, you have to do the following stuff.

Go to the _Layout.cshtml or index.html and in the bottom script section, add

<script src="~/_framework/blazor.server.js"></script>

In the same file, _Layout.cshtml or index.html, in the head tag section right after the title or meta tags, add:

<base href="/" />

Or alternatively

<base href="~/"/> 

Create the _Imports.razor file inside your Blazor components directory and add to it following using statements:

@using System.Net.Http
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.JSInterop

Alternatively, you can add these statements directly at the beginning of your component file.

Use onclick as follow:

If you want to pass parameters:

@onclick="(()=>YourEvent(yourParams))"

Or simply if you just want to call:

@onclick="YourEvent"

Upvotes: 3

Brendan
Brendan

Reputation: 1025

The solution for me ended up being quite a bit different than the other answers here. I was using Cloudflare CDN to help serve my Blazor Server application.

In the end, I needed to turn off 'HTML Minification' in Cloudflare to get my onclick events working again.

Turn this setting 'off' in Cloudflare in the 'Speed' settings section:

Enter image description here

For additional options for getting around the issue, please reference this blog post:

ASP.NET Core Blazor with SignalR breaks when used with Cloudflare's HTML minification by Tobias Zimmergren

I also ran into this same HTML Minification issue using Scully with Angular and Cloudflare.

Additional helpful blog post: Blazor Server events not triggering when hosted behind Cloudflare

Upvotes: 3

Krika
Krika

Reputation: 463

I had all my Blazor components in a separate "class library" project.

Including @using Microsoft.AspNetCore.Components.Web in the component solved the issue.

Upvotes: 1

StuartV
StuartV

Reputation: 107

I had the same problem. The problem was running the app in Internet Explorer.

OnClick worked fine when I ran the app in Edge.

Upvotes: -2

DreamEvil
DreamEvil

Reputation: 189

In my case, I added StaticFileOptions in file Startup.cs like this (I used this code in my ASP.NET Core project):

app.UseStaticFiles(new StaticFileOptions()
{
   OnPrepareResponse = ctx =>
   {
       const int duration = 60 * 60 * 24;
       ctx.Context.Response.Headers[HeaderNames.CacheControl] = $"public,max-age={duration}";
   }
});

Then I returned to

 app.UseStaticFiles();

and it started to work.

Upvotes: 2

YodasMyDad
YodasMyDad

Reputation: 9475

I had the exact same issue, but I found the solution in onclick method is not working in Blazor server-side Razor component.

You need to add a _Imports.razor file into your Components folder with the following using statements.

@using System.Net.Http
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.JSInterop

Upvotes: 15

rvnlord
rvnlord

Reputation: 3697

This is the most popular question related to the problem: Blazor onclick events don't get triggered. Adding to the answers that resolved the OP's problem and were unable to resolve mine, I would like to add that:

<script src="~/_framework/blazor.server.js"></script> need to be placed after the component that declares the events, not in the <head /> tag. Possibly at the very bottom of the body tag of _Host.cshtml or your MVC View that needs Blazor components. If you don't do it, Blazor syntax will still work, the project will compile and most things will behave as expected, but the events won't be triggered.

I just spent hours trying to figure that out.

Also if @onclick is not recognised as Blazor code in .razor files at all (this can happen especially in Component Libraries), make sure to add _Imports.razor at the very top of the project with the following content:

@using System.Net.Http
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.JSInterop

If for some reason some of the namespaces can't be found, they can be added as NuGet packages.

Upvotes: 51

David Masterson
David Masterson

Reputation: 1005

I just noticed you are embedding this in an MVC application. You don't have the script tag to it for blazor.server.js in the index.cshtml. In it you add

<script src="_framework/blazor.server.js"/>

at the bottom of the index.cshtml page. This is what wires up SignalR to the server, which is needed for server side.

See Scott Hanselman doing exactly what you are trying to do.

Upvotes: 0

David Masterson
David Masterson

Reputation: 1005

I think your call to the onclick event is wrong. They changed the way you reference functions from

<button class="btn btn-primary" onclick="@IncrementCount">Click me</button>

to

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

The @ is now in front of the @onclick and not in front of the function name.

Upvotes: 33

Pascal R.
Pascal R.

Reputation: 2323

Changing RenderMode.Static to RenderMode.Server will probably solve your problem since you use server-side Blazor. But as JohnB suggested, you should read the documentation. Learn about the difference between server-side Blazor and client-side Blazor at ASP.NET Core Blazor hosting models.

Upvotes: 2

Joon w K
Joon w K

Reputation: 827

Try add @ at onclick like this

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

Thanks Joon

Upvotes: 1

Mike
Mike

Reputation: 983

I've just had a very strange case of this problem: this code wasn't triggered.

<div ... @onclick=@(_ => OnClick.InvokeAsync(EventArgs.Empty))></div>

The problem's root is still unclear, but the solution (or rather a workaround) was to replace lambda with a method name.

<div ... @onclick=OnClickHandler></div>

@code {
  private void OnClickHandler()
  {
    OnClick.InvokeAsync(EventArgs.Empty);
  }
}

Upvotes: 3

Steven
Steven

Reputation: 13

I was stuck all day with this problem and then discovered that was working just fine in Chrome. Therefore, I added http://localhost to my list of Intranet websites in the Windows 10 Control Panel / Internet Settings / Security page. This only needs to be done for developers running the app from Visual Studio.

Upvotes: 0

Peter Morris
Peter Morris

Reputation: 23224

It should be <button @onclick=IncrementCount

Upvotes: 3

Related Questions