Skip to content

Commit

Permalink
Feature Add Validation to ReactiveProperty (#3777)
Browse files Browse the repository at this point in the history
<!-- Please be sure to read the
[Contribute](https://github.com/reactiveui/reactiveui#contribute)
section of the README -->

**What kind of change does this PR introduce?**
<!-- Bug fix, feature, docs update, ... -->

Feature for #3771 

**What is the current behavior?**
<!-- You can also link to an open issue here. -->

ReactiveProperty has basic functionality

**What is the new behavior?**
<!-- If this is a feature change -->

ReactiveProperty now supports Validation through INotifyDataErrorInfo
AddValidation and AddValidationError methods added to attach the
ReactiveProperty to the Validation mechanism
CheckValidation and Refresh exist to re-evaluate the Validation

```c#
MyReactiveProperty = new ReactiveProperty<string>()
    .AddValidation(() => MyReactiveProperty)
    .AddValidationError(s => string.IsNullOrWhiteSpace(s) ? "required" : null);
```

**What might this PR break?**

None expected.

**Please check if the PR fulfills these requirements**
- [x] Tests for the changes have been added (for bug fixes / features)
- [ ] Docs have been added / updated (for bug fixes / features)

**Other information**:
  • Loading branch information
ChrisPulman committed Mar 25, 2024
1 parent 095a9c9 commit 2e67fcd
Show file tree
Hide file tree
Showing 16 changed files with 1,186 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -349,9 +349,12 @@ namespace ReactiveUI
string? PropertyName { get; }
TSender Sender { get; }
}
public interface IReactiveProperty<T> : System.IDisposable, System.IObservable<T?>, System.Reactive.Disposables.ICancelable
public interface IReactiveProperty<T> : System.ComponentModel.INotifyDataErrorInfo, System.ComponentModel.INotifyPropertyChanged, System.IDisposable, System.IObservable<T?>, System.Reactive.Disposables.ICancelable
{
System.IObservable<System.Collections.IEnumerable?> ObserveErrorChanged { get; }
System.IObservable<bool> ObserveHasErrors { get; }
T Value { get; set; }
void Refresh();
}
public interface IRoutableViewModel : ReactiveUI.IReactiveObject, Splat.IEnableLogger, System.ComponentModel.INotifyPropertyChanged, System.ComponentModel.INotifyPropertyChanging
{
Expand Down Expand Up @@ -753,18 +756,37 @@ namespace ReactiveUI
public ReactivePropertyChangingEventArgs(TSender sender, string? propertyName) { }
public TSender Sender { get; }
}
public static class ReactivePropertyMixins
{
public static ReactiveUI.ReactiveProperty<T> AddValidation<T>(this ReactiveUI.ReactiveProperty<T> self, System.Linq.Expressions.Expression<System.Func<ReactiveUI.ReactiveProperty<T>?>> selfSelector) { }
public static System.IObservable<string?> ObserveValidationErrors<T>(this ReactiveUI.ReactiveProperty<T> self) { }
}
[System.Runtime.Serialization.DataContract]
public class ReactiveProperty<T> : ReactiveUI.ReactiveObject, ReactiveUI.IReactiveProperty<T>, System.IDisposable, System.IObservable<T?>, System.Reactive.Disposables.ICancelable
public class ReactiveProperty<T> : ReactiveUI.ReactiveObject, ReactiveUI.IReactiveProperty<T>, System.ComponentModel.INotifyDataErrorInfo, System.ComponentModel.INotifyPropertyChanged, System.IDisposable, System.IObservable<T?>, System.Reactive.Disposables.ICancelable
{
public ReactiveProperty() { }
public ReactiveProperty(T? initialValue) { }
public ReactiveProperty(T? initialValue, System.Reactive.Concurrency.IScheduler? scheduler) { }
public ReactiveProperty(T? initialValue, bool skipCurrentValueOnSubscribe, bool allowDuplicateValues) { }
public ReactiveProperty(T? initialValue, System.Reactive.Concurrency.IScheduler? scheduler, bool skipCurrentValueOnSubscribe, bool allowDuplicateValues) { }
public bool HasErrors { get; }
public bool IsDisposed { get; }
public System.IObservable<System.Collections.IEnumerable?> ObserveErrorChanged { get; }
public System.IObservable<bool> ObserveHasErrors { get; }
[System.Runtime.Serialization.DataMember]
[System.Text.Json.Serialization.JsonInclude]
public T Value { get; set; }
public event System.EventHandler<System.ComponentModel.DataErrorsChangedEventArgs>? ErrorsChanged;
public ReactiveUI.ReactiveProperty<T> AddValidationError(System.Func<System.IObservable<T?>, System.IObservable<System.Collections.IEnumerable?>> validator, bool ignoreInitialError = false) { }
public ReactiveUI.ReactiveProperty<T> AddValidationError(System.Func<System.IObservable<T?>, System.IObservable<string?>> validator, bool ignoreInitialError = false) { }
public ReactiveUI.ReactiveProperty<T> AddValidationError(System.Func<T?, System.Collections.IEnumerable?> validator, bool ignoreInitialError = false) { }
public ReactiveUI.ReactiveProperty<T> AddValidationError(System.Func<T?, System.Threading.Tasks.Task<System.Collections.IEnumerable?>> validator, bool ignoreInitialError = false) { }
public ReactiveUI.ReactiveProperty<T> AddValidationError(System.Func<T?, System.Threading.Tasks.Task<string?>> validator, bool ignoreInitialError = false) { }
public ReactiveUI.ReactiveProperty<T> AddValidationError(System.Func<T?, string?> validator, bool ignoreInitialError = false) { }
public void CheckValidation() { }
public void Dispose() { }
protected virtual void Dispose(bool disposing) { }
public System.Collections.IEnumerable? GetErrors(string? propertyName) { }
public void Refresh() { }
public System.IDisposable Subscribe(System.IObserver<T?> observer) { }
}
[System.Runtime.Serialization.DataContract]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -349,9 +349,12 @@ namespace ReactiveUI
string? PropertyName { get; }
TSender Sender { get; }
}
public interface IReactiveProperty<T> : System.IDisposable, System.IObservable<T?>, System.Reactive.Disposables.ICancelable
public interface IReactiveProperty<T> : System.ComponentModel.INotifyDataErrorInfo, System.ComponentModel.INotifyPropertyChanged, System.IDisposable, System.IObservable<T?>, System.Reactive.Disposables.ICancelable
{
System.IObservable<System.Collections.IEnumerable?> ObserveErrorChanged { get; }
System.IObservable<bool> ObserveHasErrors { get; }
T Value { get; set; }
void Refresh();
}
public interface IRoutableViewModel : ReactiveUI.IReactiveObject, Splat.IEnableLogger, System.ComponentModel.INotifyPropertyChanged, System.ComponentModel.INotifyPropertyChanging
{
Expand Down Expand Up @@ -753,18 +756,37 @@ namespace ReactiveUI
public ReactivePropertyChangingEventArgs(TSender sender, string? propertyName) { }
public TSender Sender { get; }
}
public static class ReactivePropertyMixins
{
public static ReactiveUI.ReactiveProperty<T> AddValidation<T>(this ReactiveUI.ReactiveProperty<T> self, System.Linq.Expressions.Expression<System.Func<ReactiveUI.ReactiveProperty<T>?>> selfSelector) { }
public static System.IObservable<string?> ObserveValidationErrors<T>(this ReactiveUI.ReactiveProperty<T> self) { }
}
[System.Runtime.Serialization.DataContract]
public class ReactiveProperty<T> : ReactiveUI.ReactiveObject, ReactiveUI.IReactiveProperty<T>, System.IDisposable, System.IObservable<T?>, System.Reactive.Disposables.ICancelable
public class ReactiveProperty<T> : ReactiveUI.ReactiveObject, ReactiveUI.IReactiveProperty<T>, System.ComponentModel.INotifyDataErrorInfo, System.ComponentModel.INotifyPropertyChanged, System.IDisposable, System.IObservable<T?>, System.Reactive.Disposables.ICancelable
{
public ReactiveProperty() { }
public ReactiveProperty(T? initialValue) { }
public ReactiveProperty(T? initialValue, System.Reactive.Concurrency.IScheduler? scheduler) { }
public ReactiveProperty(T? initialValue, bool skipCurrentValueOnSubscribe, bool allowDuplicateValues) { }
public ReactiveProperty(T? initialValue, System.Reactive.Concurrency.IScheduler? scheduler, bool skipCurrentValueOnSubscribe, bool allowDuplicateValues) { }
public bool HasErrors { get; }
public bool IsDisposed { get; }
public System.IObservable<System.Collections.IEnumerable?> ObserveErrorChanged { get; }
public System.IObservable<bool> ObserveHasErrors { get; }
[System.Runtime.Serialization.DataMember]
[System.Text.Json.Serialization.JsonInclude]
public T Value { get; set; }
public event System.EventHandler<System.ComponentModel.DataErrorsChangedEventArgs>? ErrorsChanged;
public ReactiveUI.ReactiveProperty<T> AddValidationError(System.Func<System.IObservable<T?>, System.IObservable<System.Collections.IEnumerable?>> validator, bool ignoreInitialError = false) { }
public ReactiveUI.ReactiveProperty<T> AddValidationError(System.Func<System.IObservable<T?>, System.IObservable<string?>> validator, bool ignoreInitialError = false) { }
public ReactiveUI.ReactiveProperty<T> AddValidationError(System.Func<T?, System.Collections.IEnumerable?> validator, bool ignoreInitialError = false) { }
public ReactiveUI.ReactiveProperty<T> AddValidationError(System.Func<T?, System.Threading.Tasks.Task<System.Collections.IEnumerable?>> validator, bool ignoreInitialError = false) { }
public ReactiveUI.ReactiveProperty<T> AddValidationError(System.Func<T?, System.Threading.Tasks.Task<string?>> validator, bool ignoreInitialError = false) { }
public ReactiveUI.ReactiveProperty<T> AddValidationError(System.Func<T?, string?> validator, bool ignoreInitialError = false) { }
public void CheckValidation() { }
public void Dispose() { }
protected virtual void Dispose(bool disposing) { }
public System.Collections.IEnumerable? GetErrors(string? propertyName) { }
public void Refresh() { }
public System.IDisposable Subscribe(System.IObserver<T?> observer) { }
}
[System.Runtime.Serialization.DataContract]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -349,9 +349,12 @@ namespace ReactiveUI
string? PropertyName { get; }
TSender Sender { get; }
}
public interface IReactiveProperty<T> : System.IDisposable, System.IObservable<T?>, System.Reactive.Disposables.ICancelable
public interface IReactiveProperty<T> : System.ComponentModel.INotifyDataErrorInfo, System.ComponentModel.INotifyPropertyChanged, System.IDisposable, System.IObservable<T?>, System.Reactive.Disposables.ICancelable
{
System.IObservable<System.Collections.IEnumerable?> ObserveErrorChanged { get; }
System.IObservable<bool> ObserveHasErrors { get; }
T Value { get; set; }
void Refresh();
}
public interface IRoutableViewModel : ReactiveUI.IReactiveObject, Splat.IEnableLogger, System.ComponentModel.INotifyPropertyChanged, System.ComponentModel.INotifyPropertyChanging
{
Expand Down Expand Up @@ -753,18 +756,37 @@ namespace ReactiveUI
public ReactivePropertyChangingEventArgs(TSender sender, string? propertyName) { }
public TSender Sender { get; }
}
public static class ReactivePropertyMixins
{
public static ReactiveUI.ReactiveProperty<T> AddValidation<T>(this ReactiveUI.ReactiveProperty<T> self, System.Linq.Expressions.Expression<System.Func<ReactiveUI.ReactiveProperty<T>?>> selfSelector) { }
public static System.IObservable<string?> ObserveValidationErrors<T>(this ReactiveUI.ReactiveProperty<T> self) { }
}
[System.Runtime.Serialization.DataContract]
public class ReactiveProperty<T> : ReactiveUI.ReactiveObject, ReactiveUI.IReactiveProperty<T>, System.IDisposable, System.IObservable<T?>, System.Reactive.Disposables.ICancelable
public class ReactiveProperty<T> : ReactiveUI.ReactiveObject, ReactiveUI.IReactiveProperty<T>, System.ComponentModel.INotifyDataErrorInfo, System.ComponentModel.INotifyPropertyChanged, System.IDisposable, System.IObservable<T?>, System.Reactive.Disposables.ICancelable
{
public ReactiveProperty() { }
public ReactiveProperty(T? initialValue) { }
public ReactiveProperty(T? initialValue, System.Reactive.Concurrency.IScheduler? scheduler) { }
public ReactiveProperty(T? initialValue, bool skipCurrentValueOnSubscribe, bool allowDuplicateValues) { }
public ReactiveProperty(T? initialValue, System.Reactive.Concurrency.IScheduler? scheduler, bool skipCurrentValueOnSubscribe, bool allowDuplicateValues) { }
public bool HasErrors { get; }
public bool IsDisposed { get; }
public System.IObservable<System.Collections.IEnumerable?> ObserveErrorChanged { get; }
public System.IObservable<bool> ObserveHasErrors { get; }
[System.Runtime.Serialization.DataMember]
[System.Text.Json.Serialization.JsonInclude]
public T Value { get; set; }
public event System.EventHandler<System.ComponentModel.DataErrorsChangedEventArgs>? ErrorsChanged;
public ReactiveUI.ReactiveProperty<T> AddValidationError(System.Func<System.IObservable<T?>, System.IObservable<System.Collections.IEnumerable?>> validator, bool ignoreInitialError = false) { }
public ReactiveUI.ReactiveProperty<T> AddValidationError(System.Func<System.IObservable<T?>, System.IObservable<string?>> validator, bool ignoreInitialError = false) { }
public ReactiveUI.ReactiveProperty<T> AddValidationError(System.Func<T?, System.Collections.IEnumerable?> validator, bool ignoreInitialError = false) { }
public ReactiveUI.ReactiveProperty<T> AddValidationError(System.Func<T?, System.Threading.Tasks.Task<System.Collections.IEnumerable?>> validator, bool ignoreInitialError = false) { }
public ReactiveUI.ReactiveProperty<T> AddValidationError(System.Func<T?, System.Threading.Tasks.Task<string?>> validator, bool ignoreInitialError = false) { }
public ReactiveUI.ReactiveProperty<T> AddValidationError(System.Func<T?, string?> validator, bool ignoreInitialError = false) { }
public void CheckValidation() { }
public void Dispose() { }
protected virtual void Dispose(bool disposing) { }
public System.Collections.IEnumerable? GetErrors(string? propertyName) { }
public void Refresh() { }
public System.IDisposable Subscribe(System.IObserver<T?> observer) { }
}
[System.Runtime.Serialization.DataContract]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -347,9 +347,12 @@ namespace ReactiveUI
string? PropertyName { get; }
TSender Sender { get; }
}
public interface IReactiveProperty<T> : System.IDisposable, System.IObservable<T?>, System.Reactive.Disposables.ICancelable
public interface IReactiveProperty<T> : System.ComponentModel.INotifyDataErrorInfo, System.ComponentModel.INotifyPropertyChanged, System.IDisposable, System.IObservable<T?>, System.Reactive.Disposables.ICancelable
{
System.IObservable<System.Collections.IEnumerable?> ObserveErrorChanged { get; }
System.IObservable<bool> ObserveHasErrors { get; }
T Value { get; set; }
void Refresh();
}
public interface IRoutableViewModel : ReactiveUI.IReactiveObject, Splat.IEnableLogger, System.ComponentModel.INotifyPropertyChanged, System.ComponentModel.INotifyPropertyChanging
{
Expand Down Expand Up @@ -751,18 +754,37 @@ namespace ReactiveUI
public ReactivePropertyChangingEventArgs(TSender sender, string? propertyName) { }
public TSender Sender { get; }
}
public static class ReactivePropertyMixins
{
public static ReactiveUI.ReactiveProperty<T> AddValidation<T>(this ReactiveUI.ReactiveProperty<T> self, System.Linq.Expressions.Expression<System.Func<ReactiveUI.ReactiveProperty<T>?>> selfSelector) { }
public static System.IObservable<string?> ObserveValidationErrors<T>(this ReactiveUI.ReactiveProperty<T> self) { }
}
[System.Runtime.Serialization.DataContract]
public class ReactiveProperty<T> : ReactiveUI.ReactiveObject, ReactiveUI.IReactiveProperty<T>, System.IDisposable, System.IObservable<T?>, System.Reactive.Disposables.ICancelable
public class ReactiveProperty<T> : ReactiveUI.ReactiveObject, ReactiveUI.IReactiveProperty<T>, System.ComponentModel.INotifyDataErrorInfo, System.ComponentModel.INotifyPropertyChanged, System.IDisposable, System.IObservable<T?>, System.Reactive.Disposables.ICancelable
{
public ReactiveProperty() { }
public ReactiveProperty(T? initialValue) { }
public ReactiveProperty(T? initialValue, System.Reactive.Concurrency.IScheduler? scheduler) { }
public ReactiveProperty(T? initialValue, bool skipCurrentValueOnSubscribe, bool allowDuplicateValues) { }
public ReactiveProperty(T? initialValue, System.Reactive.Concurrency.IScheduler? scheduler, bool skipCurrentValueOnSubscribe, bool allowDuplicateValues) { }
public bool HasErrors { get; }
public bool IsDisposed { get; }
public System.IObservable<System.Collections.IEnumerable?> ObserveErrorChanged { get; }
public System.IObservable<bool> ObserveHasErrors { get; }
[System.Runtime.Serialization.DataMember]
[System.Text.Json.Serialization.JsonInclude]
public T Value { get; set; }
public event System.EventHandler<System.ComponentModel.DataErrorsChangedEventArgs>? ErrorsChanged;
public ReactiveUI.ReactiveProperty<T> AddValidationError(System.Func<System.IObservable<T?>, System.IObservable<System.Collections.IEnumerable?>> validator, bool ignoreInitialError = false) { }
public ReactiveUI.ReactiveProperty<T> AddValidationError(System.Func<System.IObservable<T?>, System.IObservable<string?>> validator, bool ignoreInitialError = false) { }
public ReactiveUI.ReactiveProperty<T> AddValidationError(System.Func<T?, System.Collections.IEnumerable?> validator, bool ignoreInitialError = false) { }
public ReactiveUI.ReactiveProperty<T> AddValidationError(System.Func<T?, System.Threading.Tasks.Task<System.Collections.IEnumerable?>> validator, bool ignoreInitialError = false) { }
public ReactiveUI.ReactiveProperty<T> AddValidationError(System.Func<T?, System.Threading.Tasks.Task<string?>> validator, bool ignoreInitialError = false) { }
public ReactiveUI.ReactiveProperty<T> AddValidationError(System.Func<T?, string?> validator, bool ignoreInitialError = false) { }
public void CheckValidation() { }
public void Dispose() { }
protected virtual void Dispose(bool disposing) { }
public System.Collections.IEnumerable? GetErrors(string? propertyName) { }
public void Refresh() { }
public System.IDisposable Subscribe(System.IObserver<T?> observer) { }
}
[System.Runtime.Serialization.DataContract]
Expand Down

0 comments on commit 2e67fcd

Please sign in to comment.