Closed
Description
Blazor API Review: Components programming model
This is the API for the primary programming surface for authoring components. This includes types and attributes that make up ComponentBase
as well as types that you will use like MarkupString
and ElementRef
. This excludes feature areas like IUriHelper
, RenderTreeBuilder
, or auth which get their own API review.
ComponentBase
namespace Microsoft.AspNetCore.Components
{
public abstract partial class ComponentBase : Microsoft.AspNetCore.Components.IComponent, Microsoft.AspNetCore.Components.IHandleAfterRender, Microsoft.AspNetCore.Components.IHandleEvent
{
public ComponentBase() { }
protected virtual void BuildRenderTree(Microsoft.AspNetCore.Components.RenderTree.RenderTreeBuilder builder) { }
protected System.Threading.Tasks.Task Invoke(System.Action workItem) { throw null; }
protected System.Threading.Tasks.Task InvokeAsync(System.Func<System.Threading.Tasks.Task> workItem) { throw null; }
void Microsoft.AspNetCore.Components.IComponent.Configure(Microsoft.AspNetCore.Components.RenderHandle renderHandle) { }
System.Threading.Tasks.Task Microsoft.AspNetCore.Components.IHandleAfterRender.OnAfterRenderAsync() { throw null; }
System.Threading.Tasks.Task Microsoft.AspNetCore.Components.IHandleEvent.HandleEventAsync(Microsoft.AspNetCore.Components.EventCallbackWorkItem callback, object arg) { throw null; }
protected virtual void OnAfterRender() { }
protected virtual System.Threading.Tasks.Task OnAfterRenderAsync() { throw null; }
protected virtual void OnInit() { }
protected virtual System.Threading.Tasks.Task OnInitAsync() { throw null; }
protected virtual void OnParametersSet() { }
protected virtual System.Threading.Tasks.Task OnParametersSetAsync() { throw null; }
public virtual System.Threading.Tasks.Task SetParametersAsync(Microsoft.AspNetCore.Components.ParameterCollection parameters) { throw null; }
protected virtual bool ShouldRender() { throw null; }
protected void StateHasChanged() { }
}
public partial interface IComponent
{
void Configure(Microsoft.AspNetCore.Components.RenderHandle renderHandle);
System.Threading.Tasks.Task SetParametersAsync(Microsoft.AspNetCore.Components.ParameterCollection parameters);
}
public partial interface IComponentContext
{
bool IsConnected { get; }
}
public partial interface IHandleAfterRender
{
System.Threading.Tasks.Task OnAfterRenderAsync();
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public readonly partial struct RenderHandle
{
private readonly object _dummy;
private readonly int _dummyPrimitive;
public bool IsInitialized { get { throw null; } }
public System.Threading.Tasks.Task Invoke(System.Action workItem) { throw null; }
public System.Threading.Tasks.Task InvokeAsync(System.Func<System.Threading.Tasks.Task> workItem) { throw null; }
public void Render(Microsoft.AspNetCore.Components.RenderFragment renderFragment) { }
}
}
Some topics to discuss here:
- I don't feel great about the fact that
Invoke
andInvokeAsync
are different method instead of overloads (here, onIDispatcher
andRenderHandle
). Should we have overloads ofInvoke
andInvokeAsync
that return something here as well?- Do we really want
OnInit
vsOnInitialized
? Abbreviations in APIs always feel bad to me, even if they are known things likeOnInit
. Are we sure that it's worth providing sync and async versions of all of our lifecycle methods?SetParametersAsync
should probably be an explicit implementation, the user cannot duplicate the behaviour of theComponentBase
implementation, so it's not useful to override.We don't expose any lifecyle methods related toIHandleEvents
(other thanShouldRender
). That's probably ok? it could be done in the future.- Should we expose a property
HasRendered
- basically provide some API for the common use case of initializing JS interop? -
RenderHandle
seems to wear two hats. Should we pass the dispatcher into the component instead? - Is there a really good reason for
RenderHandle
to be a struct? - I don't love
Configure
as a name. Other ideas?
Events and EventCallback
namespace Microsoft.AspNetCore.Components
{
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public partial struct EventCallbackWorkItem
{
private object _dummy;
public static readonly Microsoft.AspNetCore.Components.EventCallbackWorkItem Empty;
public EventCallbackWorkItem(System.MulticastDelegate @delegate) { throw null; }
public System.Threading.Tasks.Task InvokeAsync(object arg) { throw null; }
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public readonly partial struct EventCallback
{
private readonly object _dummy;
public static readonly Microsoft.AspNetCore.Components.EventCallback Empty;
public static readonly Microsoft.AspNetCore.Components.EventCallbackFactory Factory;
public EventCallback(Microsoft.AspNetCore.Components.IHandleEvent receiver, System.MulticastDelegate @delegate) { throw null; }
public bool HasDelegate { get { throw null; } }
public System.Threading.Tasks.Task InvokeAsync(object arg) { throw null; }
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public readonly partial struct EventCallback<T>
{
private readonly object _dummy;
public EventCallback(Microsoft.AspNetCore.Components.IHandleEvent receiver, System.MulticastDelegate @delegate) { throw null; }
public bool HasDelegate { get { throw null; } }
public System.Threading.Tasks.Task InvokeAsync(T arg) { throw null; }
}
public partial interface IHandleEvent
{
System.Threading.Tasks.Task HandleEventAsync(Microsoft.AspNetCore.Components.EventCallbackWorkItem item, object arg);
}
public sealed partial class EventCallbackFactory
{
public EventCallbackFactory() { }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public Microsoft.AspNetCore.Components.EventCallback Create(object receiver, Microsoft.AspNetCore.Components.EventCallback callback) { throw null; }
public Microsoft.AspNetCore.Components.EventCallback Create(object receiver, System.Action callback) { throw null; }
public Microsoft.AspNetCore.Components.EventCallback Create(object receiver, System.Action<object> callback) { throw null; }
public Microsoft.AspNetCore.Components.EventCallback Create(object receiver, System.Func<object, System.Threading.Tasks.Task> callback) { throw null; }
public Microsoft.AspNetCore.Components.EventCallback Create(object receiver, System.Func<System.Threading.Tasks.Task> callback) { throw null; }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public Microsoft.AspNetCore.Components.EventCallback<T> CreateInferred<T>(object receiver, System.Action<T> callback, T value) { throw null; }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public Microsoft.AspNetCore.Components.EventCallback<T> CreateInferred<T>(object receiver, System.Func<T, System.Threading.Tasks.Task> callback, T value) { throw null; }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public Microsoft.AspNetCore.Components.EventCallback<T> Create<T>(object receiver, Microsoft.AspNetCore.Components.EventCallback callback) { throw null; }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public Microsoft.AspNetCore.Components.EventCallback<T> Create<T>(object receiver, Microsoft.AspNetCore.Components.EventCallback<T> callback) { throw null; }
public Microsoft.AspNetCore.Components.EventCallback<T> Create<T>(object receiver, System.Action callback) { throw null; }
public Microsoft.AspNetCore.Components.EventCallback<T> Create<T>(object receiver, System.Action<T> callback) { throw null; }
public Microsoft.AspNetCore.Components.EventCallback<T> Create<T>(object receiver, System.Func<System.Threading.Tasks.Task> callback) { throw null; }
public Microsoft.AspNetCore.Components.EventCallback<T> Create<T>(object receiver, System.Func<T, System.Threading.Tasks.Task> callback) { throw null; }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public string Create<T>(object receiver, string callback) { throw null; }
}
public static partial class EventCallbackFactoryBinderExtensions
{
public static Microsoft.AspNetCore.Components.EventCallback<Microsoft.AspNetCore.Components.UIChangeEventArgs> CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action<bool> setter, bool existingValue) { throw null; }
public static Microsoft.AspNetCore.Components.EventCallback<Microsoft.AspNetCore.Components.UIChangeEventArgs> CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action<System.DateTime> setter, System.DateTime existingValue) { throw null; }
public static Microsoft.AspNetCore.Components.EventCallback<Microsoft.AspNetCore.Components.UIChangeEventArgs> CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action<System.DateTime> setter, System.DateTime existingValue, string format) { throw null; }
public static Microsoft.AspNetCore.Components.EventCallback<Microsoft.AspNetCore.Components.UIChangeEventArgs> CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action<decimal> setter, decimal existingValue) { throw null; }
public static Microsoft.AspNetCore.Components.EventCallback<Microsoft.AspNetCore.Components.UIChangeEventArgs> CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action<double> setter, double existingValue) { throw null; }
public static Microsoft.AspNetCore.Components.EventCallback<Microsoft.AspNetCore.Components.UIChangeEventArgs> CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action<int> setter, int existingValue) { throw null; }
public static Microsoft.AspNetCore.Components.EventCallback<Microsoft.AspNetCore.Components.UIChangeEventArgs> CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action<long> setter, long existingValue) { throw null; }
public static Microsoft.AspNetCore.Components.EventCallback<Microsoft.AspNetCore.Components.UIChangeEventArgs> CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action<bool?> setter, bool? existingValue) { throw null; }
public static Microsoft.AspNetCore.Components.EventCallback<Microsoft.AspNetCore.Components.UIChangeEventArgs> CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action<System.DateTime?> setter, System.DateTime? existingValue) { throw null; }
public static Microsoft.AspNetCore.Components.EventCallback<Microsoft.AspNetCore.Components.UIChangeEventArgs> CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action<decimal?> setter, decimal? existingValue) { throw null; }
public static Microsoft.AspNetCore.Components.EventCallback<Microsoft.AspNetCore.Components.UIChangeEventArgs> CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action<double?> setter, double? existingValue) { throw null; }
public static Microsoft.AspNetCore.Components.EventCallback<Microsoft.AspNetCore.Components.UIChangeEventArgs> CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action<int?> setter, int? existingValue) { throw null; }
public static Microsoft.AspNetCore.Components.EventCallback<Microsoft.AspNetCore.Components.UIChangeEventArgs> CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action<long?> setter, long? existingValue) { throw null; }
public static Microsoft.AspNetCore.Components.EventCallback<Microsoft.AspNetCore.Components.UIChangeEventArgs> CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action<float?> setter, float? existingValue) { throw null; }
public static Microsoft.AspNetCore.Components.EventCallback<Microsoft.AspNetCore.Components.UIChangeEventArgs> CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action<float> setter, float existingValue) { throw null; }
public static Microsoft.AspNetCore.Components.EventCallback<Microsoft.AspNetCore.Components.UIChangeEventArgs> CreateBinder(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action<string> setter, string existingValue) { throw null; }
public static Microsoft.AspNetCore.Components.EventCallback<Microsoft.AspNetCore.Components.UIChangeEventArgs> CreateBinder<T>(this Microsoft.AspNetCore.Components.EventCallbackFactory factory, object receiver, System.Action<T> setter, T existingValue) { throw null; }
}
}
Some topics to discuss here:
-
EventCallbackWorkItem
should be readonly. EventCallbackWorkItem
is needed to keep the API from being insane due to generics, but I feel like it doesn't provide any useful information.ShouldEventCallback
andEventCallback<>
have anInvokeAsync()
method? It feels bad to pass null to the existing method.-
EventCallback<>
should probably have anEmpty
member for consistency. -
public string Create<T>(object receiver, string callback) { throw null; }
should be removed. -
We decided to leaveEventCallbackFactoryBinderExtensions
- we have a problem here. All of our existingbind
functionality for elements is tied toUIChangeEventArgs
which is an HTML/DOM concept. I have no brilliant ideas what to do about this.UIChangeEventArgs
where it is for now.
Dispatcher
namespace Microsoft.AspNetCore.Components.Rendering
{
public partial interface IDispatcher
{
System.Threading.Tasks.Task Invoke(System.Action action);
System.Threading.Tasks.Task InvokeAsync(System.Func<System.Threading.Tasks.Task> asyncAction);
System.Threading.Tasks.Task<TResult> InvokeAsync<TResult>(System.Func<System.Threading.Tasks.Task<TResult>> asyncFunction);
System.Threading.Tasks.Task<TResult> Invoke<TResult>(System.Func<TResult> function);
}
}
Some topics to discuss here:
- This should probably be an abstract class. We have a single implementation.
- We could also implement this on
ComponentBase
orRenderHandle
(or both).
- We could also implement this on
- I don't love that these are different method names, but I'm not sure how to resolve the conflict between
Func<TResult>
andFunc<Task>
.
Parameters
namespace Microsoft.AspNetCore.Components
{
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public readonly partial struct Parameter
{
private readonly object _dummy;
private readonly int _dummyPrimitive;
public bool Cascading { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public string Name { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public object Value { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public readonly partial struct ParameterCollection
{
private readonly object _dummy;
private readonly int _dummyPrimitive;
public static Microsoft.AspNetCore.Components.ParameterCollection Empty { get { throw null; } }
public static Microsoft.AspNetCore.Components.ParameterCollection FromDictionary(System.Collections.Generic.IDictionary<string, object> parameters) { throw null; }
public Microsoft.AspNetCore.Components.ParameterEnumerator GetEnumerator() { throw null; }
public T GetValueOrDefault<T>(string parameterName) { throw null; }
public T GetValueOrDefault<T>(string parameterName, T defaultValue) { throw null; }
public System.Collections.Generic.IReadOnlyDictionary<string, object> ToDictionary() { throw null; }
public bool TryGetValue<T>(string parameterName, out T result) { throw null; }
}
public static partial class ParameterCollectionExtensions
{
public static void SetParameterProperties(this in Microsoft.AspNetCore.Components.ParameterCollection parameterCollection, object target) { }
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public partial struct ParameterEnumerator
{
private object _dummy;
private int _dummyPrimitive;
public Microsoft.AspNetCore.Components.Parameter Current { get { throw null; } }
public bool MoveNext() { throw null; }
}
}
Some topics to discuss here:
- I'm still complaining about the name of this.
Parameter
is reallyParameterState
orParameterValue
. Low value, not doing.Cascading
->IsCascadingValue
?-
ParameterCollection
doesn't implement any interfaces, this could easily implementIReadOnlyDictionary<>
and then we don't needToDictionary()
. edit we're renaming this instead. - It's extremely wierd that
SetParameterProperties
is public, and extension method. This is OK, there is predendent for this in the BCLParameterEnumerator
doesn't implementIEnumerator
.
Attributes
namespace Microsoft.AspNetCore.Components
{
[System.AttributeUsageAttribute(System.AttributeTargets.Property, AllowMultiple=false, Inherited=false)]
public sealed partial class CascadingParameterAttribute : System.Attribute
{
public CascadingParameterAttribute() { }
public string Name { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
}
[System.AttributeUsageAttribute(System.AttributeTargets.Property, AllowMultiple=false)]
public partial class InjectAttribute : System.Attribute
{
public InjectAttribute() { }
}
[System.AttributeUsageAttribute(System.AttributeTargets.Class, AllowMultiple=true, Inherited=false)]
public partial class RouteAttribute : System.Attribute
{
public RouteAttribute(string template) { }
public string Template { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
}
[System.AttributeUsageAttribute(System.AttributeTargets.Property, AllowMultiple=false, Inherited=false)]
public sealed partial class ParameterAttribute : System.Attribute
{
public ParameterAttribute() { }
public bool CaptureUnmatchedValues { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
}
}
namespace Microsoft.AspNetCore.Components.Layouts
{
[System.AttributeUsageAttribute(System.AttributeTargets.Class, AllowMultiple=false, Inherited=true)]
public partial class LayoutAttribute : System.Attribute
{
public LayoutAttribute(System.Type layoutType) { }
public System.Type LayoutType { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
}
}
Some topics to discuss here:
- Make all of these sealed.
-
LayoutAttribute
should probably be in.Components
-
Inherited = true
for all byRouteAttribute
(which has a good reason to prevent inheritance)
Fundamental building blocks
namespace Microsoft.AspNetCore.Components
{
public delegate void RenderFragment(Microsoft.AspNetCore.Components.RenderTree.RenderTreeBuilder builder);
public delegate Microsoft.AspNetCore.Components.RenderFragment RenderFragment<T>(T value);
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public readonly partial struct ElementRef
{
private readonly object _dummy;
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public string __internalId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public readonly partial struct MarkupString
{
private readonly object _dummy;
public MarkupString(string value) { throw null; }
public string Value { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public static explicit operator Microsoft.AspNetCore.Components.MarkupString (string value) { throw null; }
public override string ToString() { throw null; }
}
}
Some topics to discuss here:
- Should
ElementRef
beElementReference
?