What is the best method for two way binding and disabling editable text fields in Blazor? - asp.net-core

For POCOs with lots of fields I find it really convenient to have a simple form/component binding structure like this
< input type="text" bind="#Person.FirstName" />
< input type="text" bind="#Person.LastName" />
< input type="text" bind="#Person.Address" />
Where I then have save or create new buttons/events to deal with object at a time situations. In other words the entire object is bound to an editable component making life super easy to bring objects from the DB for editing and back into the db.
What I find annoying are situations where I have to both display an object's details but make sure parts of it are locked/un-editable based on certain conditions. Let's say I can't change LastName for some business logic reason. This:
< input type="text" bind="#Person.LastName" disabled="#somecondition" />
is unsafe as users can potentially inspect the page, modifying the disabled flag and still cause the two way binding to be enabled and changes overwritten in the existing save event. The workarounds I find are annoying e.g., you can use if code blocks to switch between textboxes and plain-text binding, or you just do all the business logic in the event which introduced more logic for error reporting.
Here is a compromise that I think "works":
if(some_protective_condition) { < input type="text" bind="#Person.Address" /> }
else { < span>#Person.Addressv< /span>}
If I understand correctly most of these workarounds essentially play with the DOM or some aspect of visibility ensuring the editable control does not get rendered. What is the coding pattern would you guys use for these situations of interface level locking?
EDIT/TLDR: Looking for the best/safe least code pattern to maintain two-way binding, display a value and make it editable/non-editable in certain situations.
Suggestions welcome as I am trying to build good solid long term habits.
Tried several techniques. Looking for the best option if I have missed something.

I think what your looking for is InputBase. Creating a component that inherits from InputBase is going to give you access to additional features like validation styles (#CssClass) on top of two-way binding. You can use the #attributes on the input inside your component to add readonly and disabled attributes according to your POCO.
For InputBase have a peek at my answer here. Disabling the input by adding #attributes="GetAttributes()" to the html input.
[Parameter] public bool IsReadOnly { get; set; } = false;
[Parameter] public bool IsDisabled { get; set; } = false;
Dictionary<string,object> GetAttributes()
{
// Required should be handled by the DataAnnotation.
var dict = new Dictionary<string, object>();
if (IsReadOnly)
{
dict.Add("readonly", true);
}
if (IsDisabled)
{
dict.Add("disabled", true);
}
return dict;
}

I think the 'Blazor Way' to integrate business rules into your views is to use AuthorizeView...
<AuthorizeView Policy="CanEditAddress">
<Authorized>
< input type="text" bind="#Person.Address" />
</Authorized>
<NotAuthorized>
< span>#Person.Addressv< /span>
</NotAuthorized>
</AuthorizeView>
The docs show how to populate authorization policies using cascading parameters...
https://learn.microsoft.com/en-us/aspnet/core/blazor/security/?view=aspnetcore-7.0#expose-the-authentication-state-as-a-cascading-parameter
If your rules are not really 'business rules' but are 'presentation rules' (like your forms have an edit mode vs a display mode) then the above pattern is still a good way to go. That is, I would create a class named 'PresentationView' that is the equivalent of AuthorizationView but for presentation rules.

Related

How to implement UWP the right way

I run often into many problems which leads to refactoring my code...
That is why I want to ask for some recommendations.
The problems I'm running into are:
1) Providing data to XAML
Providing simple data to control value instead of using a value converter. For instance I have a color string like "#FF234243" which is stored in a class. The value for the string is provided by a web application so I can only specify it at runtime.
2) UI for every resolution
In the beginnings of my learning I got told that you can create a UI for every possible resolution, which is stupid.
So I've written a ValueConverter which I bind on an element and as ConverterParameter I give a value like '300' which gets calculated for every possible resolution... But this leads to code like this...
<TextBlock
Height={Binding Converter={StaticResource SizeValue}, ConverterParameter='300'}
/>
3) DependencyProperties vs. NotifyProperties(Properties which implement INotifyPropertyChanged) vs. Properties
I have written a control which takes a list of value and converts them into Buttons which are clickable in the UI. So I did it like this I created a variable which I set as DataContext for this specific Control and validate my data with DataContextChanged but my coworker mentioned that for this reason DependencyProperties where introduced. So I created a DependecyProperty which takes the list of items BUT when the property gets a value I have to render the buttons... So I would have to do something like
public List<string> Buttons
{
get { return (List<string>)GetValue(ButtonsProperty); }
set
{
SetValue(ButtonsProperty, value);
RenderButtons();
}
}
// Using a DependencyProperty as the backing store for Buttons. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ButtonsProperty =
DependencyProperty.Register("Buttons", typeof(List<string>), typeof(MainPage), new PropertyMetadata(""));
private void RenderButtons()
{
ButtonBar.Children.Clear();
ButtonBar.ColumnDefinitions.Clear();
if(Buttons != null)
{
int added = 0;
foreach (var item in Buttons)
{
var cd = new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) };
var btn = new Button() { Content = item };
ButtonBar.ColumnDefinitions.Add(cd);
ButtonBar.Children.Add(btn);
Grid.SetColumn(btn, added);
}
}
}
And have to use it like this:
<Controls:MyControl
x:Name="ButtonBar" Button="{Binding MyButtons}">
</Controls:MyControl>
Since these are a lot of topics I could seperate those but I think that this is a pretty common topic for beginners and I have not found a got explanation or anything else
1. Providing data to XAML
There are two options: prepare data in the ViewModel or to use converter.
To my mind using converter is better since you can have crossplatform viewModel with color like you mentioned in your example and converter will create platform dependent color. We had similar problem with image. On android it should be converted to Bitmap class, while on UWP it's converted to BitmapImage class. In the viewModel we have byte[].
2. UI for every resolution
You don't need to use converter, since Height is specified in effective pixels which will suit all the required resolutions automatically for you. More info can be found at the following link:
https://learn.microsoft.com/en-us/windows/uwp/design/layout/layouts-with-xaml
There are two options how to deal with textblock sizes:
a) Use predefined textblock styles and don't invent the wheel (which is the recommended option):
https://learn.microsoft.com/en-us/windows/uwp/design/style/typography#type-ramp
Or
b) Specify font size in pixels. They are not pixels, but effective pixels. They will be automatically scaled on different devices:
https://learn.microsoft.com/en-us/windows/uwp/design/style/typography#size-and-scaling
Furthermore, use adaptive layout to have different Layout for different screen sizes.
3) DependencyProperties vs. NotifyProperties(Properties which implement INotifyPropertyChanged) vs. Properties
As per your code you can try to use ListView or ItemsControl and define custom item template.
DependencyProperties are created in DependencyObject and are accessible in xaml. All controls are inherited from DependencyObjects. Usually you create them when you want to set them in xaml. They are not stored directly in the objects, but in the global dictionary and resolved at runtime.
DependencyProperties were created long time ago and you can find lots of links which explain them in details:
http://www.wpftutorial.net/dependencyproperties.html
https://techpunch.wordpress.com/2008/09/25/wpf-wf-what-is-a-dependency-property/
When should I use dependency properties in WPF?
What is a dependency property? What is its use?
What is a dependency property?
INotifyPropertyChanged INPC are the central part of MVVM. You bind your view to viewModel which implements INPC and when you change value of the property control is notified and rereads the new value.
Download the following video in high resolution which explains MVVM in details (by Laurent Bugnion):
https://channel9.msdn.com/Events/MIX/MIX11/OPN03
MVVM: Tutorial from start to finish?
Normal properties are used in model classes or when there is no need to notify UI regarding changes.

Binding list to a HTML textbox is not updating the view

In Aurelia, I have a textbox like this:
<input type="text" value.bind="contact.topics|commaList">
Where commaList is a value converter:
export class CommaListValueConverter {
toView(value) {
return value.join('|')
}
fromView(value) {
return value.split('|')
}
}
There are two things:
Updating the list (contact.topics) from code doesn't change anything in the view. Tried forcing two-way as well. The toView() is not called (tried to log calls). For comparison, in my view there is also a regular repeat topic of contact.topics which update fine.
Aurelia modifies my contact.topics list by adding an element __array_observer__: ModifyArrayObserver to my list. So now I somehow have to clean my list of such unwanted elements before saving (or dirty checking).
I update the array this way:
this.contact.topics.push('test')
The contents of the array display correctly in the for of loop (not shown).
gist
https://gist.run/?id=dd11c5837b77b29b586d2c4f978a7a48
Aurelia bindings don't react on Array and Object internal changes unfortunately.
You can't escape the __ private properties that aurelia adds for observation but since you are dirty checking an array you can take a clean clone of it with arr.slice()

how to hide dojo validation error tooltip?

I'm using dojo to validate input fields and if there is an error (for eg: required field) it appears in the dojo tooltip. But, I would like to show error in the custom div instead of tooltip.
So, I'm wondering if there is a way to hide/disable the validate error to appear in the tooltip? If so, I can capture the error message shown in the hidden tooltip and show the result in custom div, which will be consistent with error styling across the application.
Please advise. Thanks.
I would recommend to use the standard Dojo validation mechanism, contrary to what vivek_nk suggests. This mechanism works great in most cases, and covers most situations (required, regular expressions, numbers, dates etc.).
To solve your issue: you can overrule the "dispayMessage" function of a ValidationTextBox (for example).
displayMessage: function(/*String*/ message){
// summary:
// Overridable method to display validation errors/hints.
// By default uses a tooltip.
// tags:
// extension
if(message && this.focused){
Tooltip.show(message, this.domNode, this.tooltipPosition, !this.isLeftToRight());
}else{
Tooltip.hide(this.domNode);
}
}
Just create your own ValidationTextBox widget, extend dijit/form/ValidationTextBox, and implement your own "displayMessage" function.
Simple solution for this scenario is not to add the "required" condition at all to those fields. Instead add a separate event handler or function to check for this validation.
For eg: add a function for onBlur event. Check if the field is a mandatory. If so, show message in the custom div as expected.
<input data-dojo-type="dijit/form/TextBox"
id="sampleText" type="text" mandatory="true" onBlur="checkMandatory(this)"/>
function checkMandatory(field) {
if(field.mandatory=='true' && field.value=="") {
alert('value required'); // replace this code with my showing msg in div
} else {
field.domNode.blur();
}
}
This above code snippet does not use Dojo for validation, rather manual. Dojo actually helps to ease this by just adding the attribute "required". If that is not required, then just ignore Dojos help for this case and go native.
So, for all fields, just add the attributes - "mandatory" & "onBlur", and add the above given function for onBlur action for all these fields.

Binding a ScrollViewer from ViewModel to View

I Build a scrollViewer and its elements in my ViewModel, and it's built into a property FrameworkElement PageElement I rebuild the pageElement every time some event happens, I want to bind the PageElement to a real scrollViewer in the View so that whenever I change pageElement, it draws itself in it's view.
Let me give you a little armchair advice. I don't know the details of your project but the details in your question make me draw a few conclusions.
First, to have your view model create UI elements is not wrong. But it is really unusual. It sounds like you might be missing the concept of data template or data template selector.
Using a data template allows you to have a rich presentation of data that is built as the individual record is generated and rendered in a repeater or in a single content control.
Using a data template selector allows you to have various different presentations of data that using code-behind logic will switch between based on data or other criteria.
Ref on templates: http://blog.jerrynixon.com/2012/08/windows-8-beauty-tip-using.html
Second, to have your UI be re-generated as the result of an event being raised sounds like a short path to performance problems.
Every time you manually create elements and add them to the visual tree, you put your app at risk of binding lag while the layout is re-rendered. Run your app on an ARM and I bet you may already see it. Then again, a simplistic UI may not suffer from this general rule of thumb.
Because I do not know the event, I cannot presume it is frequently occurring. However, if it is frequently occurring, then even a simplistic UI will suffer from this.
Now to answer your question
Sherif, there is no write-enabled property on a scrollviewer that will set the horizontal or vertical offset. The only way to set the offset of a scrollviewer is to call changeview().
var s = new ScrollViewer();
s.ChangeView(0, 100, 0);
You cannot bind to a method, so binding to something like this is a non-starter without some code-behind to read the desired offset and calling the method directly.
Something like this:
public sealed partial class MainPage : Page
{
MyViewModel _Vm = new MyViewModel();
ScrollViewer _S = new ScrollViewer();
public MainPage()
{
this.InitializeComponent();
this._Vm.PropertyChanged += (s, e) =>
{
if (e.PropertyName.Equals("Offset"))
_S.ChangeView(0, _Vm.Offset, 0);
};
}
}
public class MyViewModel : INotifyPropertyChanged
{
private int _Offset;
public int Offset
{
get { return _Offset; }
set
{
_Offset = value;
PropertyChanged(this, new PropertyChangedEventArgs("Offset"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
But let me caution you. The offset will need to be based on something. And those variables may change based on the window size, the font size, scaling from transforms, and lots of other factors. The code above will work most of the time, but it will possible fail frequently on other devices.
So, what to do? My recommendation is that you code this in your code-behind, monitoring for whatever scenario you feel would require a scroll, and simply programmatically scroll it from bode-behind. Beware, though, programmatically scrolling a scrollviewer could make your UI confusing to the user.
You know your app. You will have to choose.
Best of luck!

Dojo Dijit: Why does attr("required", true) fail to set the style "dijitRequired"? or Is there another class which indicates a Dijit is required?

As far as I can judge, the CSS-Rule "dijitRequired" is used to mark a required input field. Yet, this style is not set when I apply the "required"-Attribute to a dijit, for example, a date dijit:
The Dijit is built as follows:
<input dojoType="dijit.form.DateTextBox" class="l" id="datumsTestID" name="datumsTest" tabindex="5" value="2009-01-01" />
The Attribute is set with the following Javscript code
dijit.byId('datumsTestID').attr('required', true)
Am I doing something wrong or is the style "dijitRequired" not intended to be used as I assume?
For my purposes, I patched ValidationTextBox.js to set/unset the class, but is there a cleaner (meaning: more correct) way to set the class or can I style required fields using other attributes?
ValidationTextBox.js, Dojo 1.3, Line 116
_setRequiredAttr:function(_12){
this.required=_12;
if (_12) dojo.addClass(this.domNode, "dijitRequired");
else dojo.removeClass(this.domNode, "dijitRequired");
dijit.setWaiState(this.focusNode,"required",_12);
this._refreshState();
}
Hmm, I don't see that code in ValidationTextBox.js or anywhere else. My _setRequiredAttr() in 1.3 is:
_setRequiredAttr: function(/*Boolean*/ value){
this.required = value;
dijit.setWaiState(this.focusNode,"required", value);
this._refreshState();
}
Actually I don't see any references to dijitRequired at all, maybe that's something you added to your local copy?
Setting dijitRequired is not enough. dijit.form.DateTextBox has its own internal state. Even if required attribute is set, this widget display error only when it has been blurred. You can disable this mechanism using such subclass:
dojo.provide("my.DateTextBox");
dojo.require("dijit.form.DateTextBox");
dojo.declare("my.DateTextBox", dijit.form.DateTextBox, {
_setRequiredAttr: function(required){
this._hasBeenBlurred = true;
this.inherited(arguments);
}
});