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