LoginView displays validation errors when using mock services.
This commit is contained in:
parent
433de15fdc
commit
703d87eaef
@ -17,6 +17,7 @@
|
||||
<Color x:Key="GreenColor">#00A69C</Color>
|
||||
<Color x:Key="DarkGreenColor">#00857D</Color>
|
||||
<Color x:Key="GrayColor">#e2e2e2</Color>
|
||||
<Color x:Key="ErrorColor">#ff5252</Color>
|
||||
|
||||
<!-- FONTS -->
|
||||
<OnPlatform
|
||||
@ -100,6 +101,7 @@
|
||||
<!-- CONVERTERS -->
|
||||
<converters:CountToBoolConverter x:Key="CountToBoolConverter" />
|
||||
<converters:DatetimeConverter x:Key="DatetimeConverter" />
|
||||
<converters:FirstValidationErrorConverter x:Key="FirstValidationErrorConverter" />
|
||||
<converters:ImageConverter x:Key="ImageConverter" />
|
||||
<converters:ItemTappedEventArgsConverter x:Key="ItemTappedEventArgsConverter" />
|
||||
<converters:InverseCountToBoolConverter x:Key="InverseCountToBoolConverter" />
|
||||
@ -109,6 +111,14 @@
|
||||
<converters:WebNavigatingEventArgsConverter x:Key="WebNavigatingEventArgsConverter" />
|
||||
|
||||
<!-- STYLES -->
|
||||
<Style x:Key="ValidationErrorLabelStyle"
|
||||
TargetType="{x:Type Label}">
|
||||
<Setter Property="TextColor"
|
||||
Value="{StaticResource ErrorColor}" />
|
||||
<Setter Property="FontSize"
|
||||
Value="{StaticResource LittleSize}" />
|
||||
</Style>
|
||||
|
||||
<Style x:Key="EntryStyle"
|
||||
TargetType="{x:Type Entry}">
|
||||
<Setter Property="FontFamily"
|
||||
|
@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace eShopOnContainers.Core.Converters
|
||||
{
|
||||
public class FirstValidationErrorConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
ICollection<string> errors = value as ICollection<string>;
|
||||
return errors != null && errors.Count > 0 ? errors.ElementAt(0) : null;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
using eShopOnContainers.Core.ViewModels.Base;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
|
||||
namespace eShopOnContainers.Core.Validations
|
||||
@ -8,13 +7,24 @@ namespace eShopOnContainers.Core.Validations
|
||||
public class ValidatableObject<T> : ExtendedBindableObject, IValidity
|
||||
{
|
||||
private readonly List<IValidationRule<T>> _validations;
|
||||
private readonly ObservableCollection<string> _errors;
|
||||
private List<string> _errors;
|
||||
private T _value;
|
||||
private bool _isValid;
|
||||
|
||||
public List<IValidationRule<T>> Validations => _validations;
|
||||
|
||||
public ObservableCollection<string> Errors => _errors;
|
||||
public List<string> Errors
|
||||
{
|
||||
get
|
||||
{
|
||||
return _errors;
|
||||
}
|
||||
set
|
||||
{
|
||||
_errors = value;
|
||||
RaisePropertyChanged(() => Errors);
|
||||
}
|
||||
}
|
||||
|
||||
public T Value
|
||||
{
|
||||
@ -22,7 +32,6 @@ namespace eShopOnContainers.Core.Validations
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_value = value;
|
||||
@ -36,11 +45,9 @@ namespace eShopOnContainers.Core.Validations
|
||||
{
|
||||
return _isValid;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_isValid = value;
|
||||
_errors.Clear();
|
||||
RaisePropertyChanged(() => IsValid);
|
||||
}
|
||||
}
|
||||
@ -48,7 +55,7 @@ namespace eShopOnContainers.Core.Validations
|
||||
public ValidatableObject()
|
||||
{
|
||||
_isValid = true;
|
||||
_errors = new ObservableCollection<string>();
|
||||
_errors = new List<string>();
|
||||
_validations = new List<IValidationRule<T>>();
|
||||
}
|
||||
|
||||
@ -59,11 +66,7 @@ namespace eShopOnContainers.Core.Validations
|
||||
IEnumerable<string> errors = _validations.Where(v => !v.Check(Value))
|
||||
.Select(v => v.ValidationMessage);
|
||||
|
||||
foreach (var error in errors)
|
||||
{
|
||||
Errors.Add(error);
|
||||
}
|
||||
|
||||
Errors = errors.ToList();
|
||||
IsValid = !Errors.Any();
|
||||
|
||||
return this.IsValid;
|
||||
|
@ -2,7 +2,6 @@
|
||||
using eShopOnContainers.Core.Models.User;
|
||||
using eShopOnContainers.Core.Services.Identity;
|
||||
using eShopOnContainers.Core.Services.OpenUrl;
|
||||
using eShopOnContainers.Core.Services.User;
|
||||
using eShopOnContainers.Core.Validations;
|
||||
using eShopOnContainers.Core.ViewModels.Base;
|
||||
using IdentityModel.Client;
|
||||
@ -128,6 +127,10 @@ namespace eShopOnContainers.Core.ViewModels
|
||||
|
||||
public ICommand SettingsCommand => new Command(async () => await SettingsAsync());
|
||||
|
||||
public ICommand ValidateUserNameCommand => new Command(() => ValidateUserName());
|
||||
|
||||
public ICommand ValidatePasswordCommand => new Command(() => ValidatePassword());
|
||||
|
||||
public override Task InitializeAsync(object navigationData)
|
||||
{
|
||||
if(navigationData is LogoutParameter)
|
||||
@ -250,16 +253,26 @@ namespace eShopOnContainers.Core.ViewModels
|
||||
|
||||
private bool Validate()
|
||||
{
|
||||
bool isValidUser = _userName.Validate();
|
||||
bool isValidPassword = _password.Validate();
|
||||
bool isValidUser = ValidateUserName();
|
||||
bool isValidPassword = ValidatePassword();
|
||||
|
||||
return isValidUser && isValidPassword;
|
||||
}
|
||||
|
||||
private bool ValidateUserName()
|
||||
{
|
||||
return _userName.Validate();
|
||||
}
|
||||
|
||||
private bool ValidatePassword()
|
||||
{
|
||||
return _password.Validate();
|
||||
}
|
||||
|
||||
private void AddValidations()
|
||||
{
|
||||
_userName.Validations.Add(new IsNotNullOrEmptyRule<string> { ValidationMessage = "Username should not be empty" });
|
||||
_password.Validations.Add(new IsNotNullOrEmptyRule<string> { ValidationMessage = "Password should not be empty" });
|
||||
_userName.Validations.Add(new IsNotNullOrEmptyRule<string> { ValidationMessage = "A username is required." });
|
||||
_password.Validations.Add(new IsNotNullOrEmptyRule<string> { ValidationMessage = "A password is required" });
|
||||
}
|
||||
|
||||
public void InvalidateMock()
|
||||
|
@ -6,6 +6,7 @@
|
||||
xmlns:animations="clr-namespace:eShopOnContainers.Core.Animations;assembly=eShopOnContainers.Core"
|
||||
xmlns:triggers="clr-namespace:eShopOnContainers.Core.Triggers;assembly=eShopOnContainers.Core"
|
||||
xmlns:behaviors="clr-namespace:eShopOnContainers.Core.Behaviors;assembly=eShopOnContainers.Core"
|
||||
xmlns:effects="clr-namespace:eShopOnContainers.Core.Effects;assembly=eShopOnContainers.Core"
|
||||
viewModelBase:ViewModelLocator.AutoWireViewModel="true">
|
||||
<ContentPage.Title>
|
||||
<OnPlatform
|
||||
@ -177,30 +178,60 @@
|
||||
Margin="24">
|
||||
<Label
|
||||
Text="User name or email"
|
||||
Style="{StaticResource HeaderLabelStyle}"/>
|
||||
<Entry
|
||||
Text="{Binding UserName.Value, Mode=TwoWay}">
|
||||
<Entry.Style>
|
||||
Style="{StaticResource HeaderLabelStyle}" />
|
||||
<Entry Text="{Binding UserName.Value, Mode=TwoWay}">
|
||||
<Entry.Style>
|
||||
<OnPlatform x:TypeArguments="Style"
|
||||
iOS="{StaticResource EntryStyle}"
|
||||
Android="{StaticResource EntryStyle}"
|
||||
WinPhone="{StaticResource UwpEntryStyle}"/>
|
||||
</Entry.Style>
|
||||
<Entry.Behaviors>
|
||||
<behaviors:EventToCommandBehavior
|
||||
EventName="TextChanged"
|
||||
Command="{Binding ValidateUserNameCommand}" />
|
||||
</Entry.Behaviors>
|
||||
<Entry.Triggers>
|
||||
<DataTrigger
|
||||
TargetType="Entry"
|
||||
Binding="{Binding UserName.IsValid}"
|
||||
Value="False">
|
||||
<Setter Property="effects:LineColorEffect.LineColor" Value="{StaticResource ErrorColor}" />
|
||||
</DataTrigger>
|
||||
</Entry.Triggers>
|
||||
</Entry>
|
||||
<Label
|
||||
Text="{Binding UserName.Errors, Converter={StaticResource FirstValidationErrorConverter}"
|
||||
Style="{StaticResource ValidationErrorLabelStyle}" />
|
||||
<Label
|
||||
Text="Password"
|
||||
Style="{StaticResource HeaderLabelStyle}"/>
|
||||
<Entry
|
||||
IsPassword="True"
|
||||
Text="{Binding Password.Value, Mode=TwoWay}"
|
||||
Style="{StaticResource EntryStyle}">
|
||||
Text="{Binding Password.Value, Mode=TwoWay}">
|
||||
<Entry.Style>
|
||||
<OnPlatform x:TypeArguments="Style"
|
||||
iOS="{StaticResource EntryStyle}"
|
||||
Android="{StaticResource EntryStyle}"
|
||||
WinPhone="{StaticResource UwpEntryStyle}"/>
|
||||
</Entry.Style>
|
||||
<Entry.Behaviors>
|
||||
<behaviors:EventToCommandBehavior
|
||||
EventName="TextChanged"
|
||||
Command="{Binding ValidatePasswordCommand}" />
|
||||
</Entry.Behaviors>
|
||||
<Entry.Triggers>
|
||||
<DataTrigger
|
||||
TargetType="Entry"
|
||||
Binding="{Binding Password.IsValid}"
|
||||
Value="False">
|
||||
<Setter Property="effects:LineColorEffect.LineColor" Value="{StaticResource ErrorColor}" />
|
||||
</DataTrigger>
|
||||
</Entry.Triggers>
|
||||
</Entry>
|
||||
<Label
|
||||
Text="{Binding Password.Errors, Converter={StaticResource FirstValidationErrorConverter}"
|
||||
Style="{StaticResource ValidationErrorLabelStyle}" />
|
||||
</StackLayout>
|
||||
<!-- LOGIN BUTTON -->
|
||||
<Grid
|
||||
|
@ -164,6 +164,7 @@
|
||||
<DependentUpon>ProductTemplate.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Converters\WebNavigatingEventArgsConverter.cs" />
|
||||
<Compile Include="Converters\FirstValidationErrorConverter.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
|
Loading…
x
Reference in New Issue
Block a user