How do I communicate between two sibling Blazor components? - components

I have a Blazor page with two components. One component has a button which generates a random number when clicked. The other component has a text area which should display the generated random number.
<h1>Parent Page</h1>
<ProvideNumberComponent />
<DisplayNumberComponent />
#code {
}
<h3>Provides Number</h3>
<button class="btn btn-primary" #onclick="CalculateNumber">Provide Number</button>
#code {
private void CalculateNumber(MouseEventArgs e)
{
Random rnd = new Random();
Int32 nextNumber = rnd.Next();
}
}
<h3>Displays number</h3>
<textarea cols="9" rows="1" readonly style="font-family:monospace;" />
#code {
}
What is the cleanest way to get the number from the calculate sibling component to appear in the display sibling component?
A problem with my code is that the Random object is instantiated on every button click, instead of once on initialization. Is this best addressed by placing the Random object in a singleton service class, and injecting that into the calculate component?

The best solution, to my mind, is to create a service which implements the state pattern and the notifier pattern. The following code describes how communication between two sibling can be done through an intermediary
NotifierService.cs
public class NotifierService
{
public NotifierService()
{
}
int rnd;
public int RandomNumber
{
get => rnd;
set
{
if (rnd != value)
{
rnd= value;
if (Notify != null)
{
Notify?.Invoke();
}
}
}
}
public event Func<Task> Notify;
}
Add this: services.AddScoped<NotifierService>();
ProvideNumberComponent.razor
#inject NotifierService Notifier
#implements IDisposable
<h3>Provides Number</h3>
<button class="btn btn-primary" #onclick="CalculateNumber">Provide
Number</button>
#code
{
private void CalculateNumber(MouseEventArgs e)
{
Random rnd = new Random();
Int32 nextNumber = rnd.Next();
Notifier.RandomNumber = nextNumber;
}
public async Task OnNotify()
{
await InvokeAsync(() =>
{
StateHasChanged();
});
}
protected override void OnInitialized()
{
Notifier.Notify += OnNotify;
}
public void Dispose()
{
Notifier.Notify -= OnNotify;
}
}
DisplayNumberComponent.cs
#inject NotifierService Notifier
#implements IDisposable
<hr />
<h3>Displays number</h3>
<textarea cols="9" rows="1" readonly style="font-family:monospace;">
#Notifier.RandomNumber
</textarea>
#code {
public async Task OnNotify()
{
await InvokeAsync(() =>
{
StateHasChanged();
});
}
protected override void OnInitialized()
{
Notifier.Notify += OnNotify;
}
public void Dispose()
{
Notifier.Notify -= OnNotify;
}
}
Of course you can inject and use the service in multiple components, as well as adding more features that the service can provide.
Implementing communication by means of event handlers may be problematic, unless it is between a parent and its child...
Hope this works...

Indeed there are many ways to accomplish your goal, I just want to show you the way I like more:
Parent Component:
<EditForm Model="Message">
<PageOne #bind-Send="Message.Text"/>
<PageTwo #bind-Receive="Message.Text"/>
</EditForm>
#code{
public Content Message { get; set; }=new Index.Content();
public class Content
{
public string Text { get; set; } = "Hello world";
}
}
PageOne component - the one who send the value:
<button #onclick="#GetGuid">Change value</button>
#code{
[Parameter] public string Send { get; set; }
[Parameter] public EventCallback<string> SendChanged { get; set; }
async void GetGuid()
{
await SendChanged.InvokeAsync(Guid.NewGuid().ToString());
}
}
PageTwo the component which will receive the data
<h1>#Receive</h1>
#code{
[Parameter] public string Receive { get; set; }
[Parameter] public EventCallback<string> ReceiveChanged { get; set; }
}
Explanations:
Usually when we need to communicate, we need a third party service, and in this case I used the EditForm component, which can store a Model and the properties of this model can be shared by all child components.
I also made a custom component, with less functionality, and I named PhoneBox (to be used instead EditForm), just to be obvious the role :)
PhoneBox - third party communication service :)
<CascadingValue Value="EditContext">
#ChildContent(EditContext)
</CascadingValue>
#code {
[Parameter] public object Model { get; set; }
[Parameter]public EditContext EditContext { get; set; }
[Parameter] public RenderFragment<EditContext> ChildContent { get; set; }
protected override void OnInitialized()
{
EditContext = new EditContext(Model);
}
}
I like more this approach because look's more "blazor way" :)
Look how nice is "blazor way"
<PhoneBox Model="Message">
<PageOne #bind-Send="Message.Text"/>
<PageTwo #bind-Receive="Message.Text"/>
</PhoneBox>
You can see a working example Working Example

I think interfaces are the best way to do this.
This is from my Nuget package, DataJugger.Blazor.Components
Interface IBlazorComponent:
#region using statements
using System.Collections.Generic;
#endregion
namespace DataJuggler.Blazor.Components.Interfaces
{
#region interface IBlazorComponent
/// <summary>
/// This interface allows communication between a blazor componetn and a parent component or page.
/// </summary>
public interface IBlazorComponent
{
#region Methods
#region ReceiveData(Message message)
/// <summary>
/// This method is used to send data from a child component to the parent component or page.
/// </summary>
/// <param name="data"></param>
void ReceiveData(Message message);
#endregion
#endregion
#region Properties
#region Name
/// <summary>
/// This property gets or sets the Name.
/// </summary>
public string Name { get; set; }
#endregion
#region Parent
/// <summary>
/// This property gets or sets the Parent componet or page for this object.
/// </summary>
public IBlazorComponentParent Parent { get; set; }
#endregion
#endregion
}
#endregion
}
Interface IBlazorComponentParent
#region using statements
using System.Collections.Generic;
#endregion
namespace DataJuggler.Blazor.Components.Interfaces
{
#region interface IBlazorComponentParent
/// <summary>
/// This interface is used to host IBlazorComponent objects
/// </summary>
public interface IBlazorComponentParent
{
#region Methods
#region FindChildByName(string name)
/// <summary>
/// This method is used to find a child component that has registered with the parent.
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
IBlazorComponent FindChildByName(string name);
#endregion
#region ReceiveData(Message message)
/// <summary>
/// This method is used to send data from a child component to the parent component or page.
/// </summary>
/// <param name="data"></param>
void ReceiveData(Message message);
#endregion
#region Refresh()
/// <summary>
/// This method will call StateHasChanged to refresh the UI
/// </summary>
void Refresh();
#endregion
#region Register(IBlazorComponent component)
/// <summary>
/// This method is called by the Sprite to a subscriber so it can register with the subscriber, and
/// receiver events after that.
/// </summary>
void Register(IBlazorComponent component);
#endregion
#endregion
#region Properties
#region Children
/// <summary>
/// This property gets or sets the value for Children.
/// </summary>
public List<IBlazorComponent> Children { get; set; }
#endregion
#endregion
}
#endregion
}
For usage, here is the most relevant parts:
In your component, which is an IBlazorCompoent (child), there is a Parent property.
In your component, you set the parent like this:
<Login Parent=this></Login>
Then in your component, you alter the parent property like this:
[Parameter]
public IBlazorComponentParent Parent
{
get { return parent; }
set
{
// set the value
parent = value;
// if the Parent exists
(Parent != null)
{
// Register with the parent
Parent.Register(this);
}
}
}
Next, in your parent component that implements IBlazorComponentParent, add a property for your component and change the Register method to this:
// Login component reference
public Login LoginComponent { get; set; }
public void Register(IBlazorComponent component)
{
if (component is Login)
{
// Store the LoginComponent
LoginComponent = component as Login;
}
else if (component is Join)
{
// Store the compoent
SignUpComponent = component as Join;
}
}
Now at this point, my Login component knows about the parent and the parent knows about the Login, so I can sent messages like this:
From the child, send a simple message:
if (Parent != null)
{
Message message = new Message();
message.Text = "Some message";
Parent.SendMessage(message);
}
Or send a complex message
// create a message
DataJuggler.Blazor.Components.Message message = new DataJuggler.Blazor.Components.Message();
// Create the parameters to pass to the component
NamedParameter parameter = new NamedParameter();
// Set the name
parameter.Name = "PixelInformation Update";
parameter.Value = pixel;
// Create a new collection of 'NamedParameter' objects.
message.Parameters = new List<NamedParameter>();
// Add this parameter
message.Parameters.Add(parameter);
// Send this to the component
ColorPickerComponent.ReceiveData(message);
Then in the parent to receive the message:
public void ReceiveData(Message message)
{
// If the message object exists and has parameters
if ((message != null) && (message.HasParameters))
{
// if this a PixelInformation update from the Index page
if (message.Parameters[0].Name == "PixelInformation Update")
{
// this is only relevant to my app, just showing an example of
// \what I do with the data after it is received.
// Set the SelectedPixel
SelectedPixel = (PixelInformation) message.Parameters[0].Value;
// Set the properties from the Pixel to display
SetupColorPicker();
}
}
}
The above code is used in my newest site, PixelDatabase.Net https://pixeldatabase.net
The Nuget package code is all open source if anyone wants it:
DataJuggler.Blazor.Components
https://github.com/DataJuggler/DataJuggler.Blazor.Components
I come from a Windows Forms background, so I love being able to communicate between components like this, which data binding doesn't always work.
this.Login.DoSomething(data);
You can also cast the parent as a specific type like this:
public IndexPage ParentIndexPage
{
get
{
// cast the Parent object as an Index page
return this.Parent as IndexPage;
}
}
So your child can call methods or set properties on the parent, if the parent exists of course, so always add a:
public bool HasParentIndexPage
{
get
{
// return true if the ParentIndexPage exists
return (ParentIndexPage != null);
}
}
So then for easy usage from the child:
// if the parent index page exists
if (HasParentIndexPage)
{
// Safely call your parent page
ParentIndexPage.SomeMethod();
}

On way to do it would absolutely be to use the session pattern and inject the same instance in both components and then notify them onchange. A faster way would probably be to use the two way binding and eventcallbacks.
In ProvideNumberComponent.razor
<button class="btn btn-primary" #onclick="CalculateNumber">Provide Number</button>
#code {
[Parameter]
public EventCallback<int> OnRandomNumberSet{get; set;}
private void CalculateNumber(MouseEventArgs e)
{
Random rnd = new Random();
Int32 nextNumber = rnd.Next();
OnRandomNumberSet.InvokeAsync(nextNumber);
}
}
In ParentComponent.razor
<h1>Parent Page</h1>
<ProvideNumberComponent OnRandomNumberSet="((r) => SetRandomNumber(r))"/>
<DisplayNumberComponent TextAreaValue="_randomNumber" />
#code {
private int _randomNumber;
private void SetRandomNumber(int randomNumber)
{
_randomNumber = randomNumber;
}
}
In DisplayNumberComponent.razor
<h3>Displays number</h3>
<textarea cols="9" rows="1" bind:value="TextAreaValue" readonly style="font-family:monospace;" />
#code
{
[Parameter]
public int TextAreaValue{get; set;}
}

MDSN has an example using DI injected Notifier service
invoke component methods externally to update state, which should work for any component-relation (not only siblings).
At a steeper learning curve, but more maintenance-friendly + scaleable in the long run is the Flux/Redux library Fluxor
For anyone trying to get an overview of more "design-pattern"'ish solutions, MVVM is also a posibility, example here: MVVM example implementation 4 Blazor

Related

WebPart Properties Persistence

I've tried to Develop a Web Part that displays the text entered in the Property of a custom EditorPart,
but i have problem with the Properties persistence When i click on OK or Save and open the WebPart Properties the values of the properties revert to default,
same thing when i save the Page after editing the WebPart Properties and clicking on Apply OR OK buttons. Below is the code i implemented :
using System;
using System.ComponentModel;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.WebPartPages;
using System.Web.UI.WebControls.WebParts;
using System.Collections.Generic;
using Microsoft.SharePoint.Administration;
namespace SH_Perso.WebPartPerso
{
[ToolboxItemAttribute(false)]
public class WebPartPerso : System.Web.UI.WebControls.WebParts.WebPart
{
[WebBrowsable(false), Personalizable(PersonalizationScope.User), DefaultValue("PERSO")]
public string MyPortalChoice { get; set; }
[WebBrowsable(false), Personalizable(PersonalizationScope.User), DefaultValue("Documents")]
public string MyAppChoice { get; set; }
[WebBrowsable(false), Personalizable(PersonalizationScope.User), DefaultValue("Tous les éléments")]
public string MyViewChoice { get; set; }
protected override void CreateChildControls()
{
base.CreateChildControls();
Controls.Add(new LiteralControl("PORTAIL :" + MyPortalChoice + "<br/> APPLICATION : " + MyAppChoice + "<br/> VUE : " + MyViewChoice));
}
/// <summary>
/// Creates custom editor parts here and assigns a unique id to each part
/// </summary>
/// <returns>All custom editor parts used by this web part</returns>
public override EditorPartCollection CreateEditorParts()
{
PEditorPart editorPart = new PEditorPart();
editorPart.Title = "Choix de la liste à afficher";
editorPart.ID = ID + "_editorPart";
EditorPartCollection editors = new EditorPartCollection(base.CreateEditorParts(), new EditorPart[] { editorPart });
return editors;
}
public override object WebBrowsableObject
{
get
{
return(this);
}
}
}
}
// THE EDITOR PART CLASS
class PEditorPart : EditorPart
{
public TextBox PortalChoices;
public TextBox AppChoices;
public TextBox ListViews;
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
PortalChoices = new TextBox();
AppChoices = new TextBox();
ListViews = new TextBox();
}
protected override void CreateChildControls()
{
Controls.Add(new LiteralControl("<div> <span style='font-weight:bold;'>Portail</span> "));
Controls.Add(PortalChoices);
Controls.Add(new LiteralControl("</div>"));
Controls.Add(new LiteralControl("<div> <span style='font-weight:bold;'>Listes disponibles</span> "));
Controls.Add(AppChoices);
Controls.Add(new LiteralControl("</div>"));
Controls.Add(new LiteralControl("<div> <span style='font-weight:bold;'>Listes des vues disponibles</span> "));
Controls.Add(ListViews);
Controls.Add(new LiteralControl("</div>"));
base.CreateChildControls();
ChildControlsCreated = true;
}
/// <summary>
/// Applies change in editor part ddl to the parent web part
/// </summary>
/// <returns></returns>
public override bool ApplyChanges()
{
try
{
EnsureChildControls();
WebPartPerso ParentWebPart = (WebPartPerso)WebPartToEdit;
if (ParentWebPart != null)
{
ParentWebPart.MyAppChoice = AppChoices.Text;
ParentWebPart.MyViewChoice = ListViews.Text;
ParentWebPart.MyPortalChoice = PortalChoices.Text;
}
ParentWebPart.SaveChanges();
// The operation was succesful
return true;
}
catch
{
// Because an error has occurred, the SyncChanges() method won’t be invoked.
return false;
}
}
/// <summary>
/// Reads current value from parent web part and show that in the ddl
/// </summary>
public override void SyncChanges()
{
EnsureChildControls();
WebPartPerso ParentWebPart = (WebPartPerso)WebPartToEdit;
if (ParentWebPart != null)
{
AppChoices.Text = ParentWebPart.MyAppChoice;
PortalChoices.Text = ParentWebPart.MyPortalChoice;
ListViews.Text = ParentWebPart.MyViewChoice.ToString();
}
}
}
Make WebBrowsable true
[WebBrowsable(true), Personalizable(PersonalizationScope.User), DefaultValue("PERSO")]
public string MyPortalChoice { get; set; }

AddBindings in Ninject and null repository in Controller class

In my NinjectDependencyresolver I have this:
public NinjectDependencyResolver(IKernel kernelParam)
{
this.kernel = kernelParam;
AddBindings();
}
private void AddBindings()
{
kernel.Bind<IProductsRepository>().To<EFProductRepository>();
}
and then in my controller class I have this:
public class ProductController : Controller
{
private IProductsRepository repository;
public int PageSize = 4;
public ProductController()
{
}
public ProductController(IProductsRepository productRepository)
{
this.repository = productRepository;
}
The problem is that the repository is null
Also If I add a break point to the AddBinsings() method, it doesn't get hit before going to the controller, controller gets hit but AddBindings() does not.
So does it mean it is a problem with my Ninject?
ALSO: I added the parameter less constructor of ProductController after getting this error:
No parameterless constructor defined for this object
I don't think I need that constructor, but if I remove it I get that error.
I would start by removing the constructor to ProductController that has no parameters. This will force ninject to use the constructor with IProductsRepository.
As for the binding part, we have the binding taking place inside the NinjectWebCommon.cs file. Here is our sample:
public static class NinjectWebCommon
{
private static readonly Bootstrapper Bootstrapper = new Bootstrapper();
/// <summary>
/// Starts the application
/// </summary>
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof (OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof (NinjectHttpModule));
Bootstrapper.Initialize(CreateKernel);
}
/// <summary>
/// Stops the application.
/// </summary>
public static void Stop()
{
Bootstrapper.ShutDown();
}
/// <summary>
/// Creates the kernel that will manage your application.
/// </summary>
/// <returns>The created kernel.</returns>
private static IKernel CreateKernel()
{
var kernel = new StandardKernel(new VBNinjectModule());
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
BindWebSpecificServices(kernel);
GlobalConfiguration.Configuration.DependencyResolver = new VBNinjectDependencyResolver(kernel);
return kernel;
}
public static void BindWebSpecificServices(IKernel kernel)
{
kernel.Bind<IUserHelper>()
.To<UserHelper>()
.InRequestScope();
kernel.Bind<IRoleWebService>()
.To<RoleWebService>()
.InRequestScope();
}
Should have also called it in Gloabal.ashx.cs file

HelloService and TodoService

ServiceStack self host windows service question, at the link there are two Services: TodoService.cs and HelloService.cs.
I am a little confused, are they different examples or related each other?
//Register REST Paths
[Route("/todos")]
[Route("/todos/{Id}")]
public class Todo //REST Resource DTO
{
public long Id { get; set; }
public string Content { get; set; }
public int Order { get; set; }
public bool Done { get; set; }
}
//Todo REST Service implementation
public class TodoService : RestServiceBase<Todo>
{
public TodoRepository Repository { get; set; } //Injected by IOC
public override object OnGet(Todo request)
{
if (request.Id == default(long))
return Repository.GetAll();
return Repository.GetById(request.Id);
}
//Called for new and update
public override object OnPost(Todo todo)
{
return Repository.Store(todo);
}
public override object OnDelete(Todo request)
{
Repository.DeleteById(request.Id);
return null;
}
}
And
/// <summary>
/// Define your ServiceStack web service request (i.e. the Request DTO).
/// </summary>
[Description("ServiceStack's Hello World web service.")]
[Route("/hello")]
[Route("/hello/{Name*}")]
public class Hello
{
public string Name { get; set; }
}
/// <summary>
/// Define your ServiceStack web service response (i.e. Response DTO).
/// </summary>
public class HelloResponse : IHasResponseStatus
{
public string Result { get; set; }
public ResponseStatus ResponseStatus { get; set; }
}
/// <summary>
/// Create your ServiceStack web service implementation.
/// </summary>
public class HelloService : ServiceBase<Hello>
{
protected override object Run(Hello request)
{
return new HelloResponse { Result = "Hello, " + request.Name };
}
}
They are separate examples of different services you can build with ServiceStack. The ServiceStack examples are made available in a single solution called ServiceStack.Examples, but it contains separate projects.
You are looking in a directory called StarterTemplates.Common, this is simply shared by several of the examples for code reusability. The folder structure does not indicate that TodoService.cs and HelloService.cs are directly related.
The individual projects of the ServiceStack Examples, can be seen here.
Backbone.js TODO app with REST and Redis backend
Creating a Hello World Web service from scratch

How do I load values into a class

Here is what I want to be happened. I am working on a Win Forms application. I want a class to run first then store values in a different class which has a struct in it which holds a value. Now when the Form frmMainConsole loads I want it to read the values from the struct to set certain properties on the form.
Here is what I have tried:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new frmMainConsole());
}
here is the class with the struct:
public class MyAppSetting
{
//View Setting
public bool ShowMoves { get; set; }
}
Now when the frmMainConsoleLoads which is here:
private void frmMainConsole_Load(object sender, EventArgs e)
{
CreateGroupBox();
//Set Initial Settings
MyAppSetting MyAppSetting = new ChessStrategyGame.MyAppSetting();
movesToolStripMenuItem.Checked = MyAppSetting.ShowMoves;
}
Now the class I want to use to start the application looks like this:
class StartUp
{
public static void Main()
{
MyAppSetting MyAppSetting = new MyAppSetting();
MyAppSetting.ShowMoves = true;
//On the Main Form of application
Form frmMainConsole = new frmMainConsole();
frmMainConsole.Show();
}
}
Basically I want the application to remember certain settings from the last time it was run

Repository Pattern For Accessing Text File

I am fairly new to the Repository Pattern and I would like to do this correctly. I am also trying to make use of Inversion of Control (also new).
I would like to make sure I am using the repository pattern correctly.
I picked this up as an example of a base interface for my repositories.
public interface IRepository<T> where T : class
{
IEnumerable<T> Find(Expression<Func<T, bool>> where);
IEnumerable<T> GetAll();
void Create(T p);
void Update(T p);
}
IPaymentRepository is intended for extensions to IRepository (although I don't see why I would need this if I have the Find method above)
public interface IPaymentRepository : IRepository<Payment>
{
}
PaymentRepository simply reads a text file and builds a POCO.
public class PaymentRepository : IPaymentRepository
{
#region Members
private FileInfo paymentFile;
private StreamReader reader;
private List<Payment> payments;
#endregion Members
#region Constructors
#endregion Constructors
/// <summary>
/// Initializes a new instance of the <see cref="PaymentRepository"/> class.
/// </summary>
/// <param name="paymentFile">The payment file.</param>
public PaymentRepository(FileInfo paymentFile)
{
if (!paymentFile.Exists)
throw new FileNotFoundException("Could not find the payment file to process.");
this.paymentFile = paymentFile;
}
#region Properties
#endregion Properties
#region Methods
public IEnumerable<Payment> Find(Expression<Func<Payment, bool>> where)
{
throw new NotImplementedException();
}
/// <summary>
/// Gets all payments from payment file.
/// </summary>
/// <returns>Collection of payment objects.</returns>
public IEnumerable<Payment> GetAll()
{
this.reader = new StreamReader(this.paymentFile.FullName);
this.payments = new List<Payment>();
while (!reader.EndOfStream)
{
string line = reader.ReadLine();
Payment payment = new Payment()
{
AccountNo = line.Substring(0, 11),
Amount = double.Parse(line.Substring(11, 10))
};
this.payments.Add(payment);
}
return this.payments;
}
public void Create(Payment p)
{
throw new NotImplementedException();
}
public void Update(Payment p)
{
throw new NotImplementedException();
}
#endregion Methods
I would like to know how to implement the Find method. I am assuming I would call GetAll and build an internal cache to the repository. For example, I would like to find all accounts that have payments greater than $50.
With your current IRepository signature you would implement it like this:
public IEnumerable<Payment> Find(Expression<Func<Payment, bool>> where)
{
this.reader = new StreamReader(this.paymentFile.FullName);
this.payments = new List<Payment>();
while (!reader.EndOfStream)
{
string line = reader.ReadLine();
Payment payment = new Payment()
{
AccountNo = line.Substring(0, 11),
Amount = double.Parse(line.Substring(11, 10))
};
if (where(payment)
{
this.payments.Add(payment);
}
}
return this.payments;
}
However, If your system memory allows it, you could keep a cached list (from GetAll()) and use Find() on the list. This should be an order of magnitude faster depending on the size of your list.

Resources