Skip to content

Commit

Permalink
Merge pull request #384 from runceel/main
Browse files Browse the repository at this point in the history
v8.1.2-pre
  • Loading branch information
runceel authored Jun 5, 2022
2 parents 9d5d225 + d61a3a3 commit fc9c056
Show file tree
Hide file tree
Showing 31 changed files with 304 additions and 94 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ Binding code:

It's very simple.

ReactiveProperty doesn't provide base class by ViewModel, which means that ReactiveProperty can be used together with another MVVM library like Prism, MVVMLight, etc...
ReactiveProperty doesn't provide base class by ViewModel, which means that ReactiveProperty can be used together with another MVVM libraries such as Prism, Microsoft.Toolkit.Mvvm and etc.

## Documentation

Expand All @@ -140,6 +140,7 @@ ReactiveProperty doesn't provide base class by ViewModel, which means that React
|[ReactiveProperty](https://www.nuget.org/packages/ReactiveProperty/)|![](https://img.shields.io/nuget/v/ReactiveProperty.svg)![](https://img.shields.io/nuget/dt/ReactiveProperty.svg)|The package includes all core features, and the target platform is .NET Standard 2.0. It fits almost all situations.|
|[ReactiveProperty.Core](https://www.nuget.org/packages/ReactiveProperty.Core/)|![](https://img.shields.io/nuget/v/ReactiveProperty.Core.svg)![](https://img.shields.io/nuget/dt/ReactiveProperty.Core.svg)|The package includes minimum classes such as `ReactivePropertySlim<T>` and `ReadOnlyReactivePropertySlim<T>`. And this doesn't have any dependency even System.Reactive. If you don't need Rx features, then it fits.|
|[ReactiveProperty.WPF](https://www.nuget.org/packages/ReactiveProperty.WPF/)|![](https://img.shields.io/nuget/v/ReactiveProperty.WPF.svg)![](https://img.shields.io/nuget/dt/ReactiveProperty.WPF.svg)|The package includes EventToReactiveProperty and EventToReactiveCommand for WPF. This is for .NET Core 3.0 or later and .NET Framework 4.7.2 or later.|
|[ReactiveProperty.Blazor](https://www.nuget.org/packages/ReactiveProperty.Blazor/)|![](https://img.shields.io/nuget/v/ReactiveProperty.Blazor.svg)![](https://img.shields.io/nuget/dt/ReactiveProperty.Blazor.svg)|The package includes validation support for EditForm component of Blazor with ReactiveProperty validation feature. This is for .NET 6.0 or later. |
|[ReactiveProperty.UWP](https://www.nuget.org/packages/ReactiveProperty.UWP/)|![](https://img.shields.io/nuget/v/ReactiveProperty.UWP.svg)![](https://img.shields.io/nuget/dt/ReactiveProperty.UWP.svg)|The package includes EventToReactiveProperty and EventToReactiveCommand for UWP.|
|[ReactiveProperty.XamarinAndroid](https://www.nuget.org/packages/ReactiveProperty.XamarinAndroid/)|![](https://img.shields.io/nuget/v/ReactiveProperty.XamarinAndroid.svg)![](https://img.shields.io/nuget/dt/ReactiveProperty.XamarinAndroid.svg)|The package includes many extension methods to create IObservable from events for Xamarin.Android native.|
|[ReactiveProperty.XamariniOS](https://www.nuget.org/packages/ReactiveProperty.XamariniOS/)|![](https://img.shields.io/nuget/v/ReactiveProperty.XamariniOS.svg)![](https://img.shields.io/nuget/dt/ReactiveProperty.XamariniOS.svg)|The package includes many extension methods to bind ReactiveProperty and ReactiveCommand to Xamarin.iOS native controls.|
Expand Down
47 changes: 38 additions & 9 deletions Samples/Blazor/BlazorSample.Shared/Pages/Index.razor
Original file line number Diff line number Diff line change
@@ -1,27 +1,43 @@
@using Reactive.Bindings.Components
@using Reactive.Bindings
@using Reactive.Bindings.Extensions
@using Reactive.Bindings.Components
@using System.Reactive.Linq
@using System.Reactive.Disposables
@using System.Reactive.Concurrency
@page "/"
@inject IndexViewModel _viewModel
@implements IDisposable
@inject ValidationViewModel _validationViewModel
@inject HelloWorldViewModel _helloWorldViewModel

<h3>ReactivePropertiesValidator sample</h3>
<h1>ReactiveProperty samples</h1>

<EditForm Model="_viewModel" OnInvalidSubmit="InvalidSubmit" OnValidSubmit="ValidSubmit">
<h3>Hello world</h3>

<input class="form-control" type="text" @bind="_helloWorldViewModel.Input.Value" @bind:event="oninput" />
<div class="alert alert-primary">
@_helloWorldViewModel.Output.Value
</div>

<h3>Validation sample</h3>

<EditForm Model="_validationViewModel" OnInvalidSubmit="InvalidSubmit" OnValidSubmit="ValidSubmit">
<ReactivePropertiesValidator />

<ValidationSummary />

<div class="mb-3">
<label for="firstName">First name</label>
<InputText @bind-Value="_viewModel.FirstName.Value" class="form-control" />
<ValidationMessage For="() => _viewModel.FirstName.Value" />
<InputText @bind-Value="_validationViewModel.FirstName.Value" class="form-control" />
<ValidationMessage For="() => _validationViewModel.FirstName.Value" />
</div>
<div class="mb-3">
<label for="firstName">Last name</label>
<InputText @bind-Value="_viewModel.LastName.Value" class="form-control" />
<ValidationMessage For="() => _viewModel.LastName.Value" />
<InputText @bind-Value="_validationViewModel.LastName.Value" class="form-control" />
<ValidationMessage For="() => _validationViewModel.LastName.Value" />
</div>

<div class="mb-3">
<span>Full name: @_viewModel.FullName.Value</span>
<span>Full name: @_validationViewModel.FullName.Value</span>
</div>

<button type="submit" class="btn btn-primary">Submit</button>
Expand All @@ -35,6 +51,7 @@
}

@code {
private readonly CompositeDisposable _disposable = new();
private string? _message;

private void InvalidSubmit(EditContext context)
Expand All @@ -46,4 +63,16 @@
{
_message = "ValidSubmit was invoked.";
}

protected override void OnInitialized()
{
_validationViewModel.AddTo(_disposable);
_helloWorldViewModel.AddTo(_disposable);

_helloWorldViewModel.Output
.Subscribe(_ => InvokeAsync(StateHasChanged))
.AddTo(_disposable);
}

public void Dispose() => _disposable.Dispose();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System.Reactive.Disposables;
using System.Reactive.Linq;
using Reactive.Bindings;
using Reactive.Bindings.Extensions;

namespace BlazorSample.Shared.ViewModels;

public class HelloWorldViewModel : IDisposable
{
private readonly CompositeDisposable _disposable = new();

public ReactivePropertySlim<string> Input { get; }
public ReadOnlyReactivePropertySlim<string> Output { get; }

public HelloWorldViewModel()
{
Input = new ReactivePropertySlim<string>("")
.AddTo(_disposable);
Output = Input
.Select(x => x.ToUpperInvariant())
.ToReadOnlyReactivePropertySlim("")
.AddTo(_disposable);
}

public void Dispose() => _disposable.Dispose();
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace BlazorSample.Shared.ViewModels;

public class IndexViewModel : IDisposable
public class ValidationViewModel : IDisposable
{
private readonly CompositeDisposable _disposables = new();

Expand All @@ -18,7 +18,7 @@ public class IndexViewModel : IDisposable
public ReactiveProperty<string> LastName { get; }
public ReadOnlyReactivePropertySlim<string> FullName { get; }

public IndexViewModel()
public ValidationViewModel()
{
var reactivePropertyMode = ReactivePropertyMode.Default | ReactivePropertyMode.IgnoreInitialValidationError;
FirstName = new ReactiveProperty<string>("", mode: reactivePropertyMode)
Expand Down
2 changes: 1 addition & 1 deletion Samples/Blazor/BlazorServerApp/App.razor
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@using BlazorSample.Shared.ViewModels
<Router AppAssembly="@typeof(App).Assembly" AdditionalAssemblies="@(new[]{ typeof(IndexViewModel).Assembly })">
<Router AppAssembly="@typeof(App).Assembly" AdditionalAssemblies="@(new[]{ typeof(ValidationViewModel).Assembly })">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
Expand Down
3 changes: 2 additions & 1 deletion Samples/Blazor/BlazorServerApp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddSingleton<WeatherForecastService>();
builder.Services.AddTransient<IndexViewModel>();
builder.Services.AddTransient<ValidationViewModel>();
builder.Services.AddTransient<HelloWorldViewModel>();

var app = builder.Build();

Expand Down
2 changes: 1 addition & 1 deletion Samples/Blazor/BlazorWasmApp/App.razor
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@using BlazorSample.Shared.ViewModels
<Router AppAssembly="@typeof(App).Assembly" AdditionalAssemblies="@(new[]{ typeof(IndexViewModel).Assembly })">
<Router AppAssembly="@typeof(App).Assembly" AdditionalAssemblies="@(new[]{ typeof(ValidationViewModel).Assembly })">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
Expand Down
3 changes: 2 additions & 1 deletion Samples/Blazor/BlazorWasmApp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");

builder.Services.AddTransient<IndexViewModel>();
builder.Services.AddTransient<ValidationViewModel>();
builder.Services.AddTransient<HelloWorldViewModel>();
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });

await builder.Build().RunAsync();
2 changes: 1 addition & 1 deletion Source/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<RootNamespace>Reactive.Bindings</RootNamespace>
<Version>8.1.0</Version>
<Version>8.1.2</Version>
<Authors>neuecc xin9le okazuki</Authors>
<PackageProjectUrl>https://github.com/runceel/ReactiveProperty</PackageProjectUrl>
<PackageTags>rx mvvm async rx-main reactive</PackageTags>
Expand Down
32 changes: 17 additions & 15 deletions Source/ReactiveProperty.NETStandard/Binding/RxBindingExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
// This namespace is not used almost all cases. low priority to add nullable annnotation.
#nullable disable
using System;
using System.Diagnostics;
using System.Linq.Expressions;
using System.Reactive;
Expand Down Expand Up @@ -34,11 +36,11 @@ public static IDisposable BindTo<T, TTarget, TProperty>(
TTarget target,
Expression<Func<TTarget, TProperty>> propertySelector,
BindingMode mode = BindingMode.OneWay,
Func<T, TProperty>? convert = null,
Func<TProperty, T>? convertBack = null,
IObservable<Unit>? targetUpdateTrigger = null,
TProperty? propertyFallbackValue = default(TProperty),
T? sourceFallbackValue = default(T))
Func<T, TProperty> convert = null,
Func<TProperty, T> convertBack = null,
IObservable<Unit> targetUpdateTrigger = null,
TProperty propertyFallbackValue = default(TProperty),
T sourceFallbackValue = default(T))
{
if (convert == null)
{
Expand Down Expand Up @@ -101,8 +103,8 @@ public static IDisposable BindTo<T, TTarget, TProperty>(
this ReadOnlyReactiveProperty<T> self,
TTarget target,
Expression<Func<TTarget, TProperty>> propertySelector,
Func<T, TProperty>? convert = null,
TProperty? propertyFallbackValue = default(TProperty))
Func<T, TProperty> convert = null,
TProperty propertyFallbackValue = default(TProperty))
{
if (convert == null)
{
Expand Down Expand Up @@ -138,11 +140,11 @@ public static IDisposable BindTo<T, TTarget, TProperty>(
TTarget target,
Expression<Func<TTarget, TProperty>> propertySelector,
BindingMode mode = BindingMode.OneWay,
Func<T, TProperty>? convert = null,
Func<TProperty, T>? convertBack = null,
IObservable<Unit>? targetUpdateTrigger = null,
TProperty? propertyFallbackValue = default(TProperty),
T? sourceFallbackValue = default(T))
Func<T, TProperty> convert = null,
Func<TProperty, T> convertBack = null,
IObservable<Unit> targetUpdateTrigger = null,
TProperty propertyFallbackValue = default(TProperty),
T sourceFallbackValue = default(T))
{
if (convert == null)
{
Expand Down Expand Up @@ -205,8 +207,8 @@ public static IDisposable BindTo<T, TTarget, TProperty>(
this ReadOnlyReactivePropertySlim<T> self,
TTarget target,
Expression<Func<TTarget, TProperty>> propertySelector,
Func<T, TProperty>? convert = null,
TProperty? propertyFallbackValue = default(TProperty))
Func<T, TProperty> convert = null,
TProperty propertyFallbackValue = default(TProperty))
{
if (convert == null)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
// This namespace is not used almost all cases. low priority to add nullable annnotation.
#nullable disable
using System;

namespace Reactive.Bindings.Binding;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@

namespace Reactive.Bindings.Extensions;

// TODO: remove this line later.
#nullable disable

/// <summary>
/// INotify Collection Changed Extensions
/// </summary>
Expand Down Expand Up @@ -49,7 +52,7 @@ public static IObservable<T[]> ObserveAddChangedItems<T>(this INotifyCollectionC
public static IObservable<T> ObserveRemoveChanged<T>(this INotifyCollectionChanged source) =>
source.CollectionChangedAsObservable()
.Where(e => e.Action == NotifyCollectionChangedAction.Remove)
.Select(e => (T)e.OldItems[0]);
.Select(e => (T)e.OldItems[0]!);

/// <summary>
/// Observe CollectionChanged:Remove.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,17 +116,17 @@ public static ReactiveProperty<TProperty> ToReactivePropertyAsSynchronized<TSubj
{
var observer = PropertyObservable.CreateFromPropertySelector(subject, propertySelector);
var result = Observable.Using(() => observer, x => x)
.StartWith(observer.GetPropertyPathValue())
.ToReactiveProperty(raiseEventScheduler, mode: mode);
.ToReactiveProperty(raiseEventScheduler, initialValue: observer.GetPropertyPathValue(), mode: mode);
result
.Where(_ => !ignoreValidationErrorValue || !result.HasErrors)
.Subscribe(x => observer.SetPropertyPathValue(x));
return result;
}
else
{
var result = subject.ObserveSimpleProperty(propertySelector, isPushCurrentValueAtFirst: true)
.ToReactiveProperty(raiseEventScheduler, mode: mode);
var getter = AccessorCache<TSubject>.LookupGet(propertySelector, out var _);
var result = subject.ObserveSimpleProperty(propertySelector, isPushCurrentValueAtFirst: false)
.ToReactiveProperty(raiseEventScheduler, initialValue: getter(subject), mode: mode);
var setter = AccessorCache<TSubject>.LookupSet(propertySelector, out _);
result
.Where(_ => !ignoreValidationErrorValue || !result.HasErrors)
Expand Down Expand Up @@ -244,7 +244,7 @@ public static ReactiveProperty<TResult> ToReactivePropertyAsSynchronized<TSubjec
var observer = PropertyObservable.CreateFromPropertySelector(subject, propertySelector);
var result = convert(Observable.Using(() => observer, x => x)
.StartWith(observer.GetPropertyPathValue()))
.ToReactiveProperty(raiseEventScheduler, mode: mode);
.ToReactiveProperty<TResult>(raiseEventScheduler, mode: mode);
convertBack(result.Where(_ => !ignoreValidationErrorValue || !result.HasErrors))
.Subscribe(x => observer.SetPropertyPathValue(x));
return result;
Expand All @@ -253,7 +253,7 @@ public static ReactiveProperty<TResult> ToReactivePropertyAsSynchronized<TSubjec
{
var setter = AccessorCache<TSubject>.LookupSet(propertySelector, out _);
var result = convert(subject.ObserveProperty(propertySelector, isPushCurrentValueAtFirst: true))
.ToReactiveProperty(raiseEventScheduler, mode: mode);
.ToReactiveProperty<TResult>(raiseEventScheduler, mode: mode);
convertBack(result.Where(_ => !ignoreValidationErrorValue || !result.HasErrors))
.Subscribe(x => setter(subject, x));
return result;
Expand Down Expand Up @@ -347,8 +347,7 @@ public static ReactivePropertySlim<TResult> ToReactivePropertySlimAsSynchronized
{
var result = new ReactivePropertySlim<TResult>(mode: mode);
var observer = PropertyObservable.CreateFromPropertySelector(subject, propertySelector);
IDisposable disposable = null;
disposable = convert(subject.ObserveProperty(propertySelector, isPushCurrentValueAtFirst: true))
var disposable = convert(subject.ObserveProperty(propertySelector, isPushCurrentValueAtFirst: true))
.Subscribe(x => result.Value = x);
convertBack(result)
.Subscribe(x => observer.SetPropertyPathValue(x), _ => disposable.Dispose(), () => disposable.Dispose());
Expand All @@ -358,8 +357,7 @@ public static ReactivePropertySlim<TResult> ToReactivePropertySlimAsSynchronized
{
var setter = AccessorCache<TSubject>.LookupSet(propertySelector, out _);
var result = new ReactivePropertySlim<TResult>(mode: mode);
IDisposable disposable = null;
disposable = convert(subject.ObserveProperty(propertySelector, isPushCurrentValueAtFirst: true))
var disposable = convert(subject.ObserveProperty(propertySelector, isPushCurrentValueAtFirst: true))
.Subscribe(x => result.Value = x);
convertBack(result)
.Subscribe(x => setter(subject, x), _ => disposable.Dispose(), () => disposable.Dispose());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public static IObservable<TR> Pairwise<T, TR>(this IObservable<T> source, Func<T
{
var result = Observable.Create<TR>(observer =>
{
var prev = default(T);
T prev = default!;
var isFirst = true;
return source.Subscribe(x =>
Expand All @@ -36,7 +36,7 @@ public static IObservable<TR> Pairwise<T, TR>(this IObservable<T> source, Func<T
TR value;
try
{
value = selector(prev, x);
value = selector(prev!, x);
prev = x;
}
catch (Exception ex)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,14 @@ public static IObservable<TSource> OnErrorRetry<TSource, TException>(
var empty = Observable.Empty<TSource>();
var count = 0;
IObservable<TSource>? self = null;
IObservable<TSource> self = null!;
self = source.Catch((TException ex) =>
{
onError(ex);
return (++count < retryCount)
? (dueTime == TimeSpan.Zero)
? self!.SubscribeOn(Scheduler.CurrentThread)
? self.SubscribeOn(Scheduler.CurrentThread)
: empty.Delay(dueTime, delayScheduler).Concat(self).SubscribeOn(Scheduler.CurrentThread)
: Observable.Throw<TSource>(ex);
});
Expand Down
Loading

0 comments on commit fc9c056

Please sign in to comment.