Browse Source

LoginView displays validation errors when using mock services.

pull/125/head
David Britch 7 years ago
parent
commit
703d87eaef
6 changed files with 103 additions and 23 deletions
  1. +10
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/App.xaml
  2. +22
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Converters/FirstValidationErrorConverter.cs
  3. +15
    -12
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Validations/ValidatableObject.cs
  4. +18
    -5
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/LoginViewModel.cs
  5. +37
    -6
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/LoginView.xaml
  6. +1
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/eShopOnContainers.Core.csproj

+ 10
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/App.xaml View File

@ -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"


+ 22
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Converters/FirstValidationErrorConverter.cs View File

@ -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();
}
}
}

+ 15
- 12
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Validations/ValidatableObject.cs View File

@ -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;


+ 18
- 5
src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/LoginViewModel.cs View File

@ -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()


+ 37
- 6
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/LoginView.xaml View File

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


+ 1
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/eShopOnContainers.Core.csproj View File

@ -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…
Cancel
Save