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.
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
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
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
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.