Add validation to forms

Fix values being treated as strings
This commit is contained in:
CheddarCrisp 2020-03-04 20:53:18 -05:00
parent e42f3656a9
commit cea4f35d52
3 changed files with 64 additions and 38 deletions

View file

@ -11,14 +11,16 @@
</div> </div>
{#if showAddDialog} {#if showAddDialog}
<div class="dialog"> <div class="dialog">
<div class="dialog-content"> <form class="dialog-content" novalidate bind:this={ dialogForm }>
<label>Name<input type="text" bind:value={newCounter.title} use:focus/></label> <label>Counter Name<input type="text" required bind:value={newCounter.title} use:focus/></label>
<label>Initial Value<input type="text" inputmode="decimal" bind:value={newCounter.initialValue} /></label> <label>Initial Value<input type="text" required pattern="(0|([1-9]\d*))(\.\d+)?" inputmode="decimal" bind:value={newCounter.initialValue} /></label>
<label>Target<input type="text" inputmode="decimal" bind:value={newCounter.max} /></label> <label>Target<input type="text" pattern="(0|([1-9]\d*))(\.\d+)?" inputmode="decimal" bind:value={newCounter.max} /></label>
<label>Save history<input type="checkbox" bind:checked={newCounter.saveHistory} /></label> <label>Save history<input type="checkbox" bind:checked={newCounter.saveHistory} /></label>
<button on:click={ () => { showAddDialog = false; } }>Cancel</button> <div class="buttons">
<button class="ok" on:click={ add }>OK</button> <button class="ok" on:click|preventDefault={ add }>OK</button>
</div> <button on:click|preventDefault={ () => { showAddDialog = false; } }>Cancel</button>
</div>
</form>
</div> </div>
{/if} {/if}
{#if showAbout} {#if showAbout}
@ -32,6 +34,7 @@ import { counters } from './db.js';
let showAddDialog = false; let showAddDialog = false;
let showAbout = false; let showAbout = false;
let newCounter = null; let newCounter = null;
let dialogForm = null;
function focus(node){ function focus(node){
node.focus(); node.focus();
@ -41,6 +44,7 @@ function showAdd(){
newCounter = { newCounter = {
title: '', title: '',
initialValue: 0, initialValue: 0,
max: '',
saveHistory: false, saveHistory: false,
history: [] history: []
} }
@ -49,9 +53,15 @@ function showAdd(){
} }
function add(){ function add(){
if(!dialogForm.checkValidity()) {
dialogForm.removeAttribute('novalidate');
return;
}
const counter = { const counter = {
...newCounter, ...newCounter,
value: newCounter.initialValue max: newCounter.max !== '' ? +newCounter.max : null,
value: +newCounter.initialValue
} }
counters.add(counter); counters.add(counter);
@ -60,10 +70,6 @@ function add(){
} }
</script> </script>
<style lang="scss"> <style lang="scss">
.ok {
margin-left: auto;
}
.app { .app {
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View file

@ -1,7 +1,7 @@
<div class="counter"> <div class="counter">
<header>{ counter.title }</header> <header>{ counter.title }</header>
<span class="value">{ counter.value }</span> <span class="value">{ counter.value }</span>
{#if counter.max} {#if counter.max !== null}
<MyProgress value={ counter.value } max={ counter.max }></MyProgress> <MyProgress value={ counter.value } max={ counter.max }></MyProgress>
{/if} {/if}
<div class="controls"> <div class="controls">
@ -16,24 +16,28 @@
</div> </div>
{#if showValueDialog} {#if showValueDialog}
<div class="dialog"> <div class="dialog">
<div class="dialog-content"> <form class="dialog-content" novalidate bind:this={ dialogForm }>
<h1>{ showIncrement ? "Add" : "Set" }</h1> <h1>{ showIncrement ? "Add" : "Set" }</h1>
<input class="dialog-value" type="text" inputmode="decimal" bind:value={ dialogValue } use:focus /> <input class="dialog-value" type="text" required pattern="(0|([1-9]\d*))(\.\d+)?" inputmode="decimal" bind:value={ dialogValue } use:focus />
<button on:click={ dialogCancel }>Cancel</button> <div class="buttons">
<button class="ok" on:click={ dialogDone }>OK</button> <button class="ok" on:click|preventDefault={ dialogDone }>OK</button>
</div> <button on:click|preventDefault={ dialogCancel }>Cancel</button>
</div>
</form>
</div> </div>
{/if} {/if}
{#if showEditDialog} {#if showEditDialog}
<div class="dialog"> <div class="dialog">
<div class="dialog-content"> <form class="dialog-content" novalidate bind:this={ dialogForm }>
<label>Name<input type="text" bind:value={editingCounter.title} use:focus/></label> <label>Name<input type="text" required bind:value={editingCounter.title} use:focus/></label>
<label>Initial Value<input type="text" inputmode="decimal" bind:value={editingCounter.initialValue} /></label> <label>Initial Value<input type="text" required pattern="(0|([1-9]\d*))(\.\d+)?" inputmode="decimal" bind:value={editingCounter.initialValue} /></label>
<label>Target<input type="text" inputmode="decimal" bind:value={editingCounter.max} /></label> <label>Target<input type="text" pattern="(0|([1-9]\d*))(\.\d+)?" inputmode="decimal" bind:value={editingCounter.max} /></label>
<label>Save history<input type="checkbox" bind:checked={editingCounter.saveHistory} /></label> <label>Save history<input type="checkbox" bind:checked={editingCounter.saveHistory} /></label>
<button on:click={ () => { showEditDialog = false; } }>Cancel</button> <div class="buttons">
<button class="ok" on:click={ finishEdit }>OK</button> <button class="ok" on:click|preventDefault={ finishEdit }>OK</button>
</div> <button on:click|preventDefault={ () => { showEditDialog = false; } }>Cancel</button>
</div>
</form>
</div> </div>
{/if} {/if}
{#if showHistory} {#if showHistory}
@ -56,6 +60,7 @@ let showSet;
let showEditDialog; let showEditDialog;
let editingCounter; let editingCounter;
let showHistory; let showHistory;
let dialogForm;
$: showValueDialog = showIncrement || showSet; $: showValueDialog = showIncrement || showSet;
@ -68,14 +73,23 @@ function update(newCounter){
} }
function startEdit(e){ function startEdit(e){
e.stopPropagation(); editingCounter = { ...counter };
editingCounter = {...counter};
showEditDialog = true; showEditDialog = true;
} }
function finishEdit(){ function finishEdit(){
update(editingCounter); if(!dialogForm.checkValidity()) {
dialogForm.removeAttribute('novalidate');
return;
}
const updatedCounter = {
...editingCounter,
max: editingCounter.max !== null && editingCounter.max !== '' ? +editingCounter.max : null,
value: +editingCounter.value
}
update(updatedCounter);
showEditDialog = false; showEditDialog = false;
} }
@ -85,12 +99,17 @@ function dialogCancel(){
} }
function dialogDone(){ function dialogDone(){
if(!dialogForm.checkValidity()) {
dialogForm.removeAttribute('novalidate');
return;
}
const newCounter = showIncrement ? { const newCounter = showIncrement ? {
...counter, ...counter,
value: counter.value + dialogValue value: +counter.value + +dialogValue
} : { } : {
...counter, ...counter,
value: dialogValue value: +dialogValue
} }
update(newCounter); update(newCounter);
@ -101,12 +120,10 @@ function dialogDone(){
} }
function reset(e){ function reset(e){
e.stopPropagation();
if(confirm(`Reset ${counter.title}?`)){ if(confirm(`Reset ${counter.title}?`)){
const newCounter = { const newCounter = {
...counter, ...counter,
value: counter.initialValue value: +counter.initialValue
} }
update(newCounter); update(newCounter);
@ -166,8 +183,4 @@ header {
width: 100%; width: 100%;
margin: 5px 0; margin: 5px 0;
} }
.ok {
margin-left: auto;
}
</style> </style>

View file

@ -125,6 +125,13 @@ button:focus, button:hover {
width: auto; width: auto;
} }
.dialog-content .buttons {
display: flex;
flex-direction: row-reverse;
justify-content: space-between;
width: 100%;
}
.dialog-content button:focus, .dialog-content button:hover { .dialog-content button:focus, .dialog-content button:hover {
border: 0; border: 0;
outline: 2px solid var(--button-color); outline: 2px solid var(--button-color);