Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Notify all clients of added student and update ui

I have a blazor web assembly project and a signal r service project I would like to invoke the changes to the ui when I add a student. Currently I have to refesh the page to see the addition.

StudentService.cs

public class StudentService
{
    public HubConnection connection;

    public StudentServicen()
    {    
        connection = new HubConnectionBuilder()                                            
                     .WithUrl(".../StudentsHub")
                     .Build();

        connection.StartAsync();
    }

    public async Task<List<Students>> GetAllStudents() => 
    await connection.InvokeAsync<List<Students>>("GetAllStudents"));    

    public async Task<Boolean> AddStudent(StudentData student) => 
    await connection.InvokeAsync<Boolean>("AddStudent", student);

}

Students.razor

@inject StudentService StudentService

<ul >

    @foreach (var student in students)
    {
       <li>@student.Name</li>
    } 

</ul>

@code {  

private List<Students> students = new List<Students>();  

protected override async Task OnInitializedAsync()
{
    students = await StudentService.GetAllStudents();        
}

Students hub in another project.

public class StudentsHub : Hub
{
    public Task<List<Students>> GetAllStudents() => 
    Task.FromResult(getAllStudents.GetAll());

    public Boolean AddStudent(StudentData student) => 
    studentService.AddStudent(student);
}
like image 872
SW2702 Avatar asked Mar 09 '20 13:03

SW2702


1 Answers

You've provided partial code snippets, so I created a small sample which is working, and using a custom service to access the server hub, and return values to a razor component into which the custom service is injected.

Note that when you use services and call backs you must use the InvokeAsync method, which dispatches to Blazor's SynchronizationContext, an object that enforces a single logical thread of execution.

Here's the complete code, copy and test it, and see if it can help you with your app...

UserService.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Routing;
using Microsoft.AspNetCore.SignalR.Client;

namespace BlazorSignalRApp.Client
{
    public class UserService
   {
        public event Action Notify;
        public string User { get; set; }
        public string Message { get; set; }
        HubConnection hubConnection;

        public UserService(NavigationManager navigationManager) 
        {
             hubConnection = new HubConnectionBuilder()
            .WithUrl(navigationManager.ToAbsoluteUri("/chatHub"))
            .Build();

             hubConnection.On<string, string>("ReceiveMessage", (user, 
                                                               message) =>
             {
                User = user;
                Message = message;

                if (Notify != null)
                {
                   Notify?.Invoke();
                }
             });

              hubConnection.StartAsync();
              hubConnection.SendAsync("SendMessage", null, null);
      }

      public void Send(string userInput, string messageInput) => 
          hubConnection.SendAsync("SendMessage", userInput, messageInput);

      public bool IsConnected => hubConnection.State == 
                                             HubConnectionState.Connected;
   }
}

Index.razor

@page "/"

@inject UserService UserService
@implements IDisposable

<div>
    <label for="userInput">User:</label>
    <input id="userInput" @bind="@userInput" />
</div>
<div class="form-group">
    <label for="messageInput">Message:</label>
    <input id="messageInput" @bind="@messageInput" />
</div>
<button @onclick="@(() => UserService.Send(userInput, messageInput))" 
             disabled="@(!UserService.IsConnected)">Send Message</button>

<hr />

<ul id="messagesList">
    @foreach (var message in messages)
    {
        <li>@message</li>
    }
</ul>

@code {

    List<string> messages = new List<string>();
    string userInput;
    string messageInput;

    protected override void OnInitialized()
    {
        UserService.Notify += OnNotify;

    }

    public void OnNotify()
    {
        if (!string.IsNullOrEmpty(UserService.User))
        {
            var encodedMsg = UserService.User + " says " + 
                                                        UserService.Message;
            messages.Add(encodedMsg);
        }

        InvokeAsync(() =>
        {
            StateHasChanged();
        });
    }


    public void Dispose()
    {
        UserService.Notify -= OnNotify;
    }

}

ChatHub.cs (Put this file in a Hubs folder in the Server project)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;

namespace BlazorSignalRApp.Server.Hubs
{
    public class ChatHub : Hub
    {
        public async Task SendMessage(string user, string message)
        {
            await Clients.All.SendAsync("ReceiveMessage", user, message);
        }
    }
}

Program.cs (Client project)

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Text;
using Microsoft.AspNetCore.Blazor.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Components;

namespace BlazorSignalRApp.Client
{
   public class Program
   {
        public static async Task Main(string[] args)
        {
            var builder = WebAssemblyHostBuilder.CreateDefault(args);

            builder.Services.AddSingleton<UserService>();

            builder.RootComponents.Add<App>("app");

            await builder.Build().RunAsync();
        }
   }
 }

Hope this helps...

like image 109
enet Avatar answered Nov 07 '22 10:11

enet