UX - také terorizujete své uživatele přesnými formáty vstupních polí?

Od začátku letošního roku pracujeme na drobných vylepšeních, které mají za cíl zlepšení uživatelské zkušenosti s našimi webovými aplikacemi. Kromě řady dalších věcí se naši UX odborníci zaměřili i na formuláře, které jsou standardní součástí většiny webů. O správném designu webových formulářů už toho bylo napsáno mnoho (viz. reference na konci článku) a v tomto článku je nechci opakovat. Jedním z požadavků, které dostali jako první byly automatické korekce zjevně špatných vstupů uživatele na místech, kde to je možné. Uvedu pár příkladů, které jste ještě donedávna mohli najít i na našich webech:

  • hodnota s desetinnou čárkou vyžadovala použití čárky a nikoliv tečky (nebo obráceně, podle nastaveného locale) - řada uživatelů ovšem jednou napíše to a podruhé ono (což souvisí častokrát i s nastavením klávesnice)
  • zadání vložené adresy vyžaduje správný formát - tj. aby text začínal např. protokolem http:// - uživatelé ovšem většinou protokol nepíší a často nepíší ani prefix www
  • datum a čas vyžadoval přesné zadání podle specifikovaného formátu (d.M.yyyy) a vložení mezery na nesprávné místo nebo lehce jiný formát datumu na vstupu ústil v selhání validace

Vyřešení dvou prvně jmenovaných problémů byla hračka, s datumem jsem se ovšem docela potrápil. V době, kdy jsem konverzi implementoval jsem nenašel žádný parser datumu, který by uměl "heuristicky" rozeznávat různé formáty, takže jsem tuto logiku implementoval sám (viz. další část článku). Od té doby se mi ale dostal do rukou odkaz na knihovnu POJava, která podobnou implementaci v DateTime třídě má.

Nenuťte uživatele přemýšlet

Ve smyslu této věty chceme zajistit, aby v případě, že vývojář požaduje datum ve formátu "d.M.yyyy H:mm" mohl uživatel zadat datum i v pozměněné podobě. Respektive ideálně se úplně zbavit komentáře u daného pole formuláře ve smyslu: "vložte datum ve formátu XY" a přizpůsobit se formátu, který vloží uživatel. Pro tyto účely jsem vytvořil třídu DatePatternConverter, ve které jsem implementoval (žádnou velkou chytristiku nečekejte) vlastní verzi heuristického rozpoznávání datumů. Variabilnost vstupů dobře dokumentuje test testGetDate.


/**
* Tries to recognize date / time pattern from arbitrary string. Uses several decomposition patterns to recognize
* proper value. It tries to make up missing parts in the natural way (current or initial date / time).
*
* @param dateAsString - user entered string
* @param dateRequested - true if you expect some date value
* @param timeRequested - true if you expect some time value
* @param locale - locale used to parse string (passed to the SimpleDateFormat object)
*/
public Date getDate(String dateAsString, boolean dateRequested, boolean timeRequested, Locale locale) {
   ...
}

Integrace v naší aplikaci pak spočívá v tom, že vývojář pouze specifikuje základní datumový formát, který očekává (tj. např. "d.M.yyyy H:mm") a konvertor se primárně pokusí datum parsovat proti tomuto formátu. Pokud konverze selže, normalizuje vstup uživatele do zjednodušené podoby a pokusí se aplikovat další "standardní" datumové formáty. Pokud se datum nepodaří z řetězce získat, vrací se NULL hodnota.

Parser se snaží domyslet si konvenčním způsobem chybějící hodnoty. Tj. pokud očekáváte plné datum - d.M.yyyy a uživatel zadá pouze řetězec "2011", vy jako vývojář dostanete celou hodnotu - pokud vložený rok odpovídá aktuálnímu, doplní se jako den a měsíc dnešní den jinak se použije 1. leden. Vzhledem k tomu, že nečekáte časovou hodnotu bude v Date objektu čas vynulován.

Vzhledem k tomu, že takto se snaží "počítač" přemýšlet za uživatele, může se jednoduše stát, že datum neuhodne správně. Měli bychom tedy uživateli ještě před finálním použitím hodnoty (např. uložení do databáze) oznámit, jak jsme si jeho vstup domysleli, aby mohl případně svou hodnotu dále doplnit nebo napsat správně, když se naše logika netrefila do toho, co uživatel zamýšlel.

Pozn. na okraj:
Možná si říkáte, zda není toto řešení v době různých sexy javascript datePickerů zbytečné, ale my často narážíme na případy, kdy je použití datePickeru kontraproduktivní - třeba v případě, že očekáváme datumy, které jsou daleko v minulosti nebo v budoucnosti (typicky třeba datum narození). V takových případech je výběr datumu přes datePicker spíš pro zlost. Navíc ve formulářích, které se používají často, je vložení datumu přes klávesnici (pokud nemusím myslet na formát) často rychlejší než zběsilé klikání v datePickeru.

Pokud vás zajímá, jak to celé vypadá v praxi, přeskočte na poslední odstavec tohoto článku.

Integrace Javy a jQuery UI DatePicker

Druhý problém, který jsem v souvislosti s předěláním práce s datumy řešil, byl převod Java formátu datumu na JavaScriptový formát používaný v jQueryUI komponentě datePicker. Když se podíváte na dokumentaci (datePicker, timePicker addon), brzy zjistíte, že formáty požadované JavaScriptovými komponentami se od Javovských liší a tudíž musí vývojáři zadávat buď datumové formáty 2x nebo musíte zajistit konverzi jednoho formátu do druhého.

Jelikož nám jde o zrychlení vývoje, nechci po dalších vývojářích, kteří budou používat moji datumovou komponentu, aby znali implementační detaily a studovali dokumentaci ze tří zdrojů. Vydal jsem se tedy cestou konverze - v třídě DatePatternConverter najdete tyto metody:

  • javaToJavascriptDatePatternConversion
  • javaToJavascriptTimePatternConversion
  • getDateAndTimeDelimiter

Ty se starají o konverzi Java SimpleDateFormat patternu na cílové patterny pro výše zmíněné dvě JavaScriptové komponenty. Třetí metoda vám vrátí případný řetězec, který má datum a čas spojovat dohromady. Použití a funkcionalita je nejlépe čitelná z JUnit testů.

Musím přiznat, že s touto věcí jsem se bavil celý jeden den. Na první pohled to ale nevypadá jako složitý úkol, že?

Ukázka funkcionality na prototypu

Na tomto videu se můžete podívat jak to celé funguje v praxi. Při použití těžíme z tzv. živé validace, která na pozadí provádí komunikaci se serverem a validuje hodnoty hned jak je uživatel zadá. Tím mu dokážeme okamžitě také propagovat návrhy oprav ze strany serveru:

To, co se mě na tom celém líbí nejvíc, je to, že vývojáři, který bude formuláře nakonec skládat, stačí jen hezky ostylovat zprávy zobrazující se vedle vstupních polí a na serverové straně deklarovat použité validace. Všechno ostatní už dostává zadarmo.

Zdroje

V závěru bych rád uvedl několik odkazů na články, které nás přinutily k sebereflexi a změně práce s formuláři. Minimálně bych rád doporučil první článek, na jehož základě jsem v podstatě postavil principy naší živé validace (a které obdobně respektuje i jQuery Validation plugin):