Hi,
Started to port some React components to play with Blazor.
First thing is stumbled upon is Parameter attribute.
Here is a current snippet from my code:
@functions {
[Parameter]
double? left { get; set; }
[Parameter]
double? top { get; set; }
[Parameter]
double? right { get; set; }
[Parameter]
double? bottom { get; set; }
[Parameter]
double? width { get; set; }
[Parameter]
double? height { get; set; }
[Parameter]
double? star { get; set; }
[Parameter]
double? basis { get; set; }
[Parameter]
bool auto { get; set; }
[Parameter]
bool column { get; set; }
[Parameter]
bool absolute { get; set; }
[Parameter]
bool absoluteLeft { get; set; }
[Parameter]
bool absoluteRight { get; set; }
[Parameter]
bool relative { get; set; }
[Parameter]
bool @static { get; set; }
[Parameter]
bool @fixed { get; set; }
[Parameter]
bool fixedLeft { get; set; }
[Parameter]
bool fixedRight { get; set; }
[Parameter]
bool hidden { get; set; }
[Parameter]
bool wrap { get; set; }
[Parameter]
bool autoScroll { get; set; }
[Parameter]
bool crop { get; set; }
[Parameter]
RenderFragment ChildContent { get; set; }
}
I imagine that ParameterAttribute indicates properties (and only properties) which could be specified from outside. Exactly for the same reason C# has public property and field modifiers.
So why couldn't we for the sake of simplicity indicate parameters with public properties and fields? Each public property and field should be a parameter by default.
So the code above could look much simpler and take less time to type:
@functions {
public double? left, top, right, bottom, width, height, star, basis;
public bool auto, column, absolute, absoluteLeft, absoluteRight, relative, @static, @fixed, fixedLeft, fixedRight, hidden, wrap, autoScroll, crop;
public RenderFragment ChildContent;
}
If we want internal fields for state, just use private or protected members like we normally do.
I'm a fan for doing such thing in RazorPages, to make public properties bound automatically without BindAtribute
Mate,
public double? left defines a C# variable of public scope and nuallable double. It is not a definition of a property. And it has nothing to do with Blazor. This is the way to go in .NET from the very beginning. The Parameter attribute is necessary in order to distinguish between ordinary property and otherwise. However, I believe you can define a class, annotated with the Parameter attribute, and containing your properties...
Here we discuss Parameter concept of Razor components, not variables. This concept could be easily implemented using built-in C# features like public properties and fields. The sole purpose of a Razor Component is to receive parameters from Razor page, process them and spit out rendered HTML markup.
Why should one introduce additional ceremony for parameters in this particular case is beyond any reasonable explanation. Most of properties in Razor Components are parameters, those which are not, could be marked protected or private. To indicate additional parameter's aspect, different attributes can be used.
Normal C# properties and fields are more than enough. It just works in Json.NET, Entity Framework etc.
We could argue about public fields as parameters, because they need special attention. But from what I've seen in actual Blazor implementation, public fields could be supported as well with little as none effort. The main .NET concert against public fields is that they could break assembly binary interface in Razor Component libraries. However with .NET Core deployment model those libraries will be deployed along with web application anyway and never be shared between different web apps.
Thus there is no good reason to:
1) have private properties as parameters (you can set them in markup anyway)
2) have public properties and fields that you cannot set in markup.
3) introduce additional performance penalty by forcing parameters always be properties not fields
4) introduce additional ceremony to back every parameter with explicit field to speed up access.
I believe that Razor Components could be simpler to understand, faster to code and more fun to develop.
Maybe there is a strong technical reason to have ParameterAttribute on private properties.
But it has still not been presented by stakeholders.
In this case I will make separate class with all this properties and declare it as parameter.
It will give us more clean code.
[Parameter]
MyClass myclass { get; set; }
MyClass will have all necessary properties inside.
I think parameter and public property are different and we do not need to mix.
Parameter we use for markup (razor to recognize with properties can be set in markup) and public property we can use from code behind anytime.
Perhaps the case is different little bit from RazorPages, because in RazorPages public properties are intended to be used from within the view, so that's why I see all the samples and demo decorate the public properties as BindAttribute
Component parameters have different semantics than ordinary .NET properties, hence the additional ceremony. Parameters are the principle way that data should flow into a component that affects rendering. This allows the Blazor runtime to reason about when the component should be rendered. I.e. if the parameters change then the component should be rendered, otherwise it doesn't need to be. So we want the definition of parameters to be an explicit gesture. That's why the [Parameter] attribute is required.
Parameters also should not be mutated outside of the normal component life cycle. Parameters should be set through the IComponent.SetParameters method. That's why we recommend that parameters not be public. This guidance is implemented as an analyzer, but you can disable it if you want.
We prefer properties to using fields because it gives component authors the opportunity to insert some code when the parameter is set. Ex you want to do some conversion logic.
To make it easier to create parameters we provide a convenient snippet. Just type "para" and then hit TAB twice. We also considered adding a Razor directive to make defining parameters easier, but then you have the problem of having to replicate normal C# features (like attributes) for the new syntax.
I hope this helps explain why we landed where we did.
Parameters also should not be mutated outside of the normal component life cycle. Parameters should be set through the
IComponent.SetParametersmethod. That's why we recommend that parameters not be public. This guidance is implemented as an analyzer, but you can disable it if you want.We prefer properties to using fields because it gives component authors the opportunity to insert some code when the parameter is set. Ex you want to do some conversion logic.
@danroth27 Now that Parameters need to be public would you still advise against mutating them outside the component lifecycle, e.g. in a button handler in the child?
One has to understand that public properties and fields are set by parent component. So you may mutate them if you need, but next render cycle will set them again.
I still not get this ceremonial approach for Razor components, looks like MS ppl just get paid for lines of code, not for the end result.
So the more characters you type, the better approach it is.
I miss simplicity of JavaScript React components, where you just define incoming data as props or fields. But Razor went rogue from good ideas and headed the LOC way. And this is just one more obstacle towards the adoption.
Now that Parameters need to be public would you still advise against mutating them outside the component lifecycle, e.g. in a button handler in the child?
@rabberbock In general, yes, that's still our recommendation. We think of component parameters like parameters to a constructor or method.